Snap for 13135932 from 551534959c to 25Q2-release

Change-Id: Ie0d910ac493b806091a9d6004d7ba8dbc4225e9f
This commit is contained in:
Android Build Coastguard Worker
2025-02-27 16:19:40 -08:00
46 changed files with 1617 additions and 527 deletions

View File

@@ -5508,6 +5508,24 @@
android:value="com.android.settings.applications.contacts.ContactsStorageSettings"/>
</activity>
<activity
android:name="com.android.settings.connecteddevice.audiosharing.AudioSharingJoinHandlerActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:excludeFromRecents="true"
android:launchMode="singleTop"
android:permission="android.permission.BLUETOOTH_PRIVILEGED"
android:taskAffinity="com.android.settings.connecteddevice.audiosharing.AudioSharingJoinHandlerActivity"
android:theme="@style/Transparent"
android:exported="false">
<intent-filter android:priority="1">
<action android:name="android.settings.AUDIO_SHARING_JOIN_HANDLER" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
<meta-data
android:name="com.android.settings.FRAGMENT_CLASS"
android:value="com.android.settings.connecteddevice.audiosharing.AudioSharingJoinHandlerDashboardFragment" />
</activity>
<service
android:name="com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService"
android:foregroundServiceType="mediaPlayback"

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2025 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="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal" android:autoMirrored="true">
<path android:fillColor="@color/settingslib_materialColorPrimary" android:pathData="M560,720L320,480L560,240L616,296L432,480L616,664L560,720Z"/>
</vector>

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2025 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="960" android:viewportHeight="960" android:tint="?attr/colorControlNormal" android:autoMirrored="true">
<path android:fillColor="@color/settingslib_materialColorPrimary" android:pathData="M504,480L320,296L376,240L616,480L376,720L320,664L504,480Z" />
</vector>

View File

@@ -38,20 +38,47 @@
android:text="@string/screen_zoom_preview_title"
style="@style/AccessibilityTextReadingPreviewTitle" />
<com.android.settings.accessibility.TextReadingPreviewPager
android:id="@+id/preview_pager"
<LinearLayout
android:layout_width="match_parent"
android:layout_height="217dp"
android:contentDescription="@string/preview_pager_content_description"
android:nestedScrollingEnabled="true" />
<com.android.settings.widget.DotsPageIndicator
android:id="@+id/page_indicator"
style="@style/PreviewPagerPageIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_horizontal"
android:padding="6dp"
android:visibility="gone" />
android:orientation="vertical">
<com.android.settings.accessibility.TextReadingPreviewPager
android:id="@+id/preview_pager"
android:layout_width="wrap_content"
android:layout_height="217dp"
android:contentDescription="@string/preview_pager_content_description"
android:nestedScrollingEnabled="true" />
<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:orientation="horizontal"
android:layout_gravity="center_horizontal"
android:gravity="center_horizontal">
<ImageButton
android:id="@+id/preview_left_button"
android:src="@drawable/keyboard_arrow_left"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/preview_pager_previous_button"
style="?android:attr/borderlessButtonStyle" />
<com.android.settings.widget.DotsPageIndicator
android:id="@+id/page_indicator"
style="@style/PreviewPagerPageIndicator"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_gravity="center_vertical"
android:padding="6dp"
android:visibility="gone" />
<ImageButton
android:id="@+id/preview_right_button"
android:src="@drawable/keyboard_arrow_right"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_gravity="center_vertical"
android:contentDescription="@string/preview_pager_next_button"
style="?android:attr/borderlessButtonStyle" />
</LinearLayout>
</LinearLayout>
</LinearLayout>
</FrameLayout>

View File

@@ -80,6 +80,10 @@
<string name="preview_pager_content_description">Preview</string>
<!-- Content description for qrcode image. [CHAR LIMIT=none]-->
<string name="qr_code_content_description">QR code</string>
<!-- Previous button for preview pager. [CHAR LIMIT=NONE] -->
<string name="preview_pager_previous_button">Previous preview</string>
<!-- Next button for preview pager. [CHAR LIMIT=NONE] -->
<string name="preview_pager_next_button">Next preview</string>
<!-- Description for the button that makes interface elements smaller. [CHAR_LIMIT=NONE] -->
<string name="font_size_make_smaller_desc">Make smaller</string>
@@ -174,6 +178,12 @@
<string name="bluetooth_ambient_volume_control_left">Left</string>
<!-- Connected devices settings. The text to show the control is for right side device. [CHAR LIMIT=30] -->
<string name="bluetooth_ambient_volume_control_right">Right</string>
<!-- Connected devices settings. Content description for unified ambient control slider. [CHAR LIMIT=NONE] -->
<string name="bluetooth_ambient_volume_control_description">Surroundings</string>
<!-- Connected devices settings. Content description for left ambient control slider. [CHAR LIMIT=NONE] -->
<string name="bluetooth_ambient_volume_control_left_description">Left surroundings</string>
<!-- Connected devices settings. Content description for right ambient control slider.. [CHAR LIMIT=NONE] -->
<string name="bluetooth_ambient_volume_control_right_description">Right surroundings</string>
<!-- Connected devices settings. Content description for a button, that mute ambient volume [CHAR_LIMIT=NONE] -->
<string name="bluetooth_ambient_volume_mute">Mute surroundings</string>
<!-- Connected devices settings. Content description for a button, that unmute ambient volume [CHAR LIMIT=NONE] -->
@@ -521,6 +531,8 @@
<string name="language_selection_title">Add a language</string>
<!-- Title for the region picker [CHAR LIMIT=25] -->
<string name="region_selection_title">Choose a region</string>
<!-- Title for the numbering system selection screen [CHAR LIMIT=25] -->
<string name="numbering_system_selection_title">Number System</string>
<!-- Title for the region selection screen [CHAR LIMIT=25] -->
<string name="country_selection_title">Region preference</string>
<!-- Hint text in a search edit box (used to filter long language / country lists) [CHAR LIMIT=25] -->
@@ -536,7 +548,7 @@
<!-- Message for asking to change system locale region or not. [CHAR LIMIT=50]-->
<string name="body_change_system_locale_region">Your device will keep <xliff:g id="system_language" example="English">%1$s</xliff:g> as a system language</string>
<!-- Description for the numbering system language. [CHAR LIMIT=NONE]-->
<string name="top_intro_numbering_system_title">Most apps will use your regional preferences</string>
<string name="top_intro_numbering_system_title">Choose how your device, websites, and apps display numbers</string>
<!-- Regional Preferences begin -->
<!-- The title of the menu entry of regional preferences. [CHAR LIMIT=50] -->
@@ -3163,6 +3175,7 @@
<string name="dark_ui_summary_on_auto_mode_modes">Will turn off when <xliff:g name="modeName" example="Bedtime">%1$s</xliff:g> ends</string>
<!-- Dark theme screen, description of Dark theme feature. [CHAR LIMIT=NONE] -->
<string name="dark_ui_text">Dark theme uses a black background to help keep battery alive longer on some screens. Dark theme schedules wait to turn on until your screen is off.</string>
<string name="dark_ui_text_force_invert">Use a dark background to make your screen more comfortable to view and reduce battery usage on some screens. Your theme will change if you have a schedule, when the screen is off.</string>
<!-- Dark UI screen footer summary text shown when the when Dark theme turns on/off automatically according to a user bedtime schedule. [CHAR LIMIT=NONE] -->
<string name="dark_ui_bedtime_footer_summary">Dark theme is currently following your Bedtime mode schedule</string>
<!-- Dark UI screen footer action text shown when the when Dark theme turns on/off automatically according to a user bedtime schedule. [CHAR LIMIT=NONE] -->
@@ -5560,7 +5573,7 @@
<!-- Title for the accessibility preference for forcing all apps to use dark theme. [CHAR LIMIT=35] -->
<string name="accessibility_force_invert_title">Make more apps dark</string>
<!-- Summary for the accessibility preference for forcing all apps to use dark theme. [CHAR LIMIT=100] -->
<string name="accessibility_force_invert_summary">Automatically convert light theme apps to dark theme</string>
<string name="accessibility_force_invert_summary">Expands dark theme to more apps. May not work with all apps.</string>
<!-- Title for the accessibility preference for disabling animations. [CHAR LIMIT=35] -->
<string name="accessibility_disable_animations">Remove animations</string>
<!-- Summary for the accessibility preference for disabling animations. [CHAR LIMIT=60] -->
@@ -6981,7 +6994,8 @@ Data usage charges may apply.</string>
<!-- Message displayed to let the user know that some of the options are disabled by admin. [CHAR LIMIT=NONE] -->
<string name="admin_disabled_other_options">Other options are disabled by your admin</string>
<string name="admin_more_details">Learn more</string>
<!-- Text on the link that lets user learn more about restricted screen timeout options. [CHAR LIMIT=NONE] -->
<string name="admin_more_details">Learn more about disabled screen timeout options</string>
<string name="notification_log_title">Notification log</string>
<string name="notification_history_title">Notification history</string>
@@ -9315,7 +9329,7 @@ Data usage charges may apply.</string>
<string name="nls_feature_settings_summary">It can turn Do Not Disturb on or off and change related settings.</string>
<string name="nls_feature_modes_settings_summary">It can manage and activate Modes, and change related settings.</string>
<string name="notification_listener_disable_warning_summary">
If you turn off notification access for <xliff:g id="notification_listener_name">%1$s</xliff:g>, Do Not Disturb access may also be turned off.
If you turn off notification access for <xliff:g id="notification_listener_name">%1$s</xliff:g>, Do Not Disturb control may also be turned off.
</string>
<string name="notification_listener_disable_modes_warning_summary">
If you turn off notification access for <xliff:g id="notification_listener_name">%1$s</xliff:g>, Modes access may also be turned off.
@@ -9498,14 +9512,14 @@ Data usage charges may apply.</string>
The placeholder would be the app name (e.g. Calendar). [CHAR LIMIT=NONE]-->
<string name="interact_across_profiles_install_app_summary">Tap to get the app</string>
<!-- Sound & notification > Advanced section: Title for managing Do Not Disturb access option. [CHAR LIMIT=40] -->
<string name="manage_zen_access_title">Do Not Disturb access</string>
<!-- Sound & notification > Advanced section: Title for managing Do Not Disturb control option. [CHAR LIMIT=40] -->
<string name="manage_zen_access_title">Do Not Disturb control</string>
<!-- Button title that grants 'Do Not Disturb' permission to an app [CHAR_LIMIT=60]-->
<string name="zen_access_detail_switch">Allow Do Not Disturb</string>
<!-- Sound & notification > Do Not Disturb access > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
<string name="zen_access_empty_text">No installed apps have requested Do Not Disturb access</string>
<!-- Sound & notification > Do Not Disturb control > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
<string name="zen_access_empty_text">No installed apps have requested Do Not Disturb control</string>
<!-- Special App Access: Title for managing Modes access option. [CHAR LIMIT=40] -->
<string name="manage_zen_modes_access_title">Modes access</string>
@@ -9513,7 +9527,7 @@ Data usage charges may apply.</string>
<!-- Button title that grants 'Modes' permission to an app [CHAR_LIMIT=60]-->
<string name="zen_modes_access_detail_switch">Allow Modes access</string>
<!-- Special App Access > Do Not Disturb access > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
<!-- Special App Access > Do Not Disturb control > Text to display when the list is empty. [CHAR LIMIT=NONE] -->
<string name="zen_modes_access_empty_text">No installed apps have requested Modes access</string>
<!-- [CHAR LIMIT=NONE] Text appearing when app notifications are off -->

View File

@@ -29,7 +29,7 @@
android:key="vibration_intensity_category_call"
android:title="@string/accessibility_call_vibration_category_title">
<com.android.settings.widget.SeekBarPreference
<com.android.settingslib.widget.SliderPreference
android:key="vibration_intensity_preference_ring"
android:title="@string/accessibility_ring_vibration_title"
app:keywords="@string/keywords_ring_vibration"
@@ -47,13 +47,13 @@
android:key="vibration_intensity_category_notification_alarm"
android:title="@string/accessibility_notification_alarm_vibration_category_title">
<com.android.settings.widget.SeekBarPreference
<com.android.settingslib.widget.SliderPreference
android:key="vibration_intensity_preference_notification"
android:title="@string/accessibility_notification_vibration_title"
app:keywords="@string/keywords_notification_vibration"
app:controller="com.android.settings.accessibility.NotificationVibrationIntensityPreferenceController" />
<com.android.settings.widget.SeekBarPreference
<com.android.settingslib.widget.SliderPreference
android:key="vibration_intensity_preference_alarm"
android:title="@string/accessibility_alarm_vibration_title"
app:keywords="@string/keywords_alarm_vibration"
@@ -65,13 +65,13 @@
android:key="vibration_intensity_category_haptics"
android:title="@string/accessibility_interactive_haptics_category_title">
<com.android.settings.widget.SeekBarPreference
<com.android.settingslib.widget.SliderPreference
android:key="vibration_intensity_preference_touch"
android:title="@string/accessibility_touch_vibration_title"
app:keywords="@string/keywords_touch_vibration"
app:controller="com.android.settings.accessibility.HapticFeedbackIntensityPreferenceController" />
<com.android.settings.widget.SeekBarPreference
<com.android.settingslib.widget.SliderPreference
android:key="vibration_intensity_preference_media"
android:title="@string/accessibility_media_vibration_title"
app:keywords="@string/keywords_media_vibration"

View File

@@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<!-- Copyright (C) 2025 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="audio_sharing_join_handler"
settings:searchable="false"
settings:controller="com.android.settings.connecteddevice.audiosharing.AudioSharingJoinHandlerController" />

View File

@@ -22,8 +22,8 @@
<com.android.settingslib.widget.TopIntroPreference
android:key="dark_ui_top_intro"
android:title="@string/dark_ui_text"
settings:searchable="false"/>
settings:searchable="false"
settings:controller="com.android.settings.display.darkmode.DarkModeTopIntroPreferenceController"/>
<com.android.settingslib.widget.MainSwitchPreference
android:key="dark_ui_activated"

View File

@@ -696,7 +696,7 @@ public final class Utils extends com.android.settingslib.Utils {
final SpannableString str = new SpannableString(displayText);
str.setSpan(new TtsSpan.TextBuilder(accessibileText).build(), 0,
displayText.length(),
Spannable.SPAN_INCLUSIVE_INCLUSIVE);
Spannable.SPAN_EXCLUSIVE_EXCLUSIVE);
return str;
}

View File

@@ -75,6 +75,8 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im
private static final String BLUETOOTH_SHOW_DEVICES_WITHOUT_NAMES_PROPERTY =
"persist.bluetooth.showdeviceswithoutnames";
private static final String KEY_AVAILABLE_HEARING_DEVICES = "available_hearing_devices";
// Flags data type from CSS 1.3 Flags
private static final int BT_DISCOVERABLE_MASK = 0x03;
LocalBluetoothManager mLocalManager;
@Nullable
@@ -322,7 +324,7 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im
};
void handleLeScanResult(ScanResult result) {
if (mCachedDeviceManager == null) {
if (mCachedDeviceManager == null || !isDeviceDiscoverable(result)) {
return;
}
final BluetoothDevice device = result.getDevice();
@@ -505,4 +507,13 @@ public class HearingDevicePairingFragment extends RestrictedDashboardFragment im
Toast.makeText(getContext(), R.string.connected_device_bluetooth_turned_on_toast,
Toast.LENGTH_SHORT).show();
}
boolean isDeviceDiscoverable(ScanResult result) {
final ScanRecord scanRecord = result.getScanRecord();
if (scanRecord == null) {
return false;
}
final int flags = scanRecord.getAdvertiseFlags();
return (flags & BT_DISCOVERABLE_MASK) != 0;
}
}

View File

@@ -22,6 +22,7 @@ import android.os.Parcelable;
import android.util.AttributeSet;
import android.view.View;
import android.widget.FrameLayout;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import androidx.preference.Preference;
@@ -99,6 +100,28 @@ public class TextReadingPreviewPreference extends Preference {
(DotsPageIndicator) holder.findViewById(R.id.page_indicator);
updateAdapterIfNeeded(viewPager, pageIndicator, mPreviewAdapter);
updatePagerAndIndicator(viewPager, pageIndicator);
viewPager.setClipToOutline(true);
int layoutDirection =
getContext().getResources().getConfiguration().getLayoutDirection();
int previousId = (layoutDirection == View.LAYOUT_DIRECTION_RTL)
? R.id.preview_right_button : R.id.preview_left_button;
int nextId = (layoutDirection == View.LAYOUT_DIRECTION_RTL)
? R.id.preview_left_button : R.id.preview_right_button;
final ImageButton previousButton = previewLayout.findViewById(previousId);
final ImageButton nextButton = previewLayout.findViewById(nextId);
// These call ViewPager#setCurrentItem directly
// because that doesn't force a refresh through notifyChanged().
// We found this avoids a crash in SUW (See b/386906497).
previousButton.setOnClickListener((view) ->
viewPager.setCurrentItem(getCurrentItem() - 1));
previousButton.setContentDescription(getContext().getString(
R.string.preview_pager_previous_button));
nextButton.setOnClickListener((view) ->
viewPager.setCurrentItem(getCurrentItem() + 1));
previousButton.setContentDescription(getContext().getString(
R.string.preview_pager_next_button));
}
@Override

View File

@@ -24,10 +24,10 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.SliderPreferenceController;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.android.settingslib.widget.SliderPreference;
/**
* Abstract preference controller for a vibration intensity setting, that displays multiple
@@ -69,15 +69,16 @@ public abstract class VibrationIntensityPreferenceController extends SliderPrefe
@Override
public void displayPreference(PreferenceScreen screen) {
super.displayPreference(screen);
final SeekBarPreference preference = screen.findPreference(getPreferenceKey());
final SliderPreference preference = screen.findPreference(getPreferenceKey());
mSettingsContentObserver.onDisplayPreference(this, preference);
preference.setEnabled(mPreferenceConfig.isPreferenceEnabled());
preference.setSummaryProvider(unused -> mPreferenceConfig.getSummary());
preference.setMin(getMin());
preference.setMax(getMax());
preference.setSliderIncrement(1); // Discrete slider
// Haptics previews played by the Settings app don't bypass user settings to be played.
// The sliders continuously updates the intensity value so the previews can apply them.
preference.setContinuousUpdates(true);
preference.setUpdatesContinuously(true);
}
@Override

View File

@@ -339,9 +339,16 @@ public class AmbientVolumePreference extends PreferenceGroup implements AmbientV
if (side == SIDE_LEFT) {
slider.setTitle(
getContext().getString(R.string.bluetooth_ambient_volume_control_left));
slider.setSliderContentDescription(getContext().getString(
R.string.bluetooth_ambient_volume_control_left_description));
} else if (side == SIDE_RIGHT) {
slider.setTitle(
getContext().getString(R.string.bluetooth_ambient_volume_control_right));
slider.setSliderContentDescription(getContext().getString(
R.string.bluetooth_ambient_volume_control_right_description));
} else {
slider.setSliderContentDescription(getContext().getString(
R.string.bluetooth_ambient_volume_control_description));
}
mSideToSliderMap.put(side, slider);
}

View File

@@ -0,0 +1,46 @@
/*
* Copyright (C) 2025 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.connecteddevice.audiosharing;
import android.os.Bundle;
import com.android.settings.SettingsActivity;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.flags.Flags;
public class AudioSharingJoinHandlerActivity extends SettingsActivity {
private static final String TAG = "AudioSharingJoinHandlerActivity";
@Override
protected void onCreate(Bundle savedState) {
super.onCreate(savedState);
if (!Flags.promoteAudioSharingForSecondAutoConnectedLeaDevice()
|| !BluetoothUtils.isAudioSharingUIAvailable(this)) {
finish();
}
}
@Override
protected boolean isToolbarEnabled() {
return false;
}
@Override
protected boolean isValidFragment(String fragmentName) {
return AudioSharingJoinHandlerDashboardFragment.class.getName().equals(fragmentName);
}
}

View File

@@ -0,0 +1,274 @@
/*
* Copyright (C) 2025 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.connecteddevice.audiosharing;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.content.Intent;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.VisibleForTesting;
import androidx.annotation.WorkerThread;
import androidx.lifecycle.DefaultLifecycleObserver;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
import com.android.settings.bluetooth.Utils;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.flags.Flags;
import com.android.settingslib.utils.ThreadUtils;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
public class AudioSharingJoinHandlerController extends BasePreferenceController
implements DefaultLifecycleObserver, BluetoothCallback {
private static final String TAG = "AudioSharingJoinHandlerCtrl";
private static final String KEY = "audio_sharing_join_handler";
@Nullable private final LocalBluetoothManager mBtManager;
@Nullable private final BluetoothEventManager mEventManager;
@Nullable private final CachedBluetoothDeviceManager mDeviceManager;
@Nullable private final LocalBluetoothLeBroadcastAssistant mAssistant;
private final Executor mExecutor;
@Nullable private DashboardFragment mFragment;
@Nullable private AudioSharingDialogHandler mDialogHandler;
@VisibleForTesting
BluetoothLeBroadcastAssistant.Callback mAssistantCallback =
new BluetoothLeBroadcastAssistant.Callback() {
@Override
public void onSearchStarted(int reason) {
}
@Override
public void onSearchStartFailed(int reason) {
}
@Override
public void onSearchStopped(int reason) {
}
@Override
public void onSearchStopFailed(int reason) {
}
@Override
public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {
}
@Override
public void onSourceAdded(
@NonNull BluetoothDevice sink, int sourceId, int reason) {
Log.d(TAG, "onSourceAdded: dismiss stale dialog.");
if (mDeviceManager != null && mDialogHandler != null) {
CachedBluetoothDevice cachedDevice = mDeviceManager.findDevice(sink);
if (cachedDevice != null) {
mDialogHandler.closeOpeningDialogsForLeaDevice(cachedDevice);
}
}
}
@Override
public void onSourceAddFailed(
@NonNull BluetoothDevice sink,
@NonNull BluetoothLeBroadcastMetadata source,
int reason) {
}
@Override
public void onSourceModified(
@NonNull BluetoothDevice sink, int sourceId, int reason) {
}
@Override
public void onSourceModifyFailed(
@NonNull BluetoothDevice sink, int sourceId, int reason) {
}
@Override
public void onSourceRemoved(
@NonNull BluetoothDevice sink, int sourceId, int reason) {
}
@Override
public void onSourceRemoveFailed(
@NonNull BluetoothDevice sink, int sourceId, int reason) {
}
@Override
public void onReceiveStateChanged(
@NonNull BluetoothDevice sink,
int sourceId,
@NonNull BluetoothLeBroadcastReceiveState state) {
}
};
public AudioSharingJoinHandlerController(@NonNull Context context,
@NonNull String preferenceKey) {
super(context, preferenceKey);
mBtManager = Utils.getLocalBtManager(mContext);
mEventManager = mBtManager == null ? null : mBtManager.getEventManager();
mDeviceManager = mBtManager == null ? null : mBtManager.getCachedDeviceManager();
mAssistant = mBtManager == null ? null
: mBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
mExecutor = Executors.newSingleThreadExecutor();
}
/**
* Initialize the controller.
*
* @param fragment The fragment to provide the context and metrics category for {@link
* AudioSharingBluetoothDeviceUpdater} and provide the host for dialogs.
*/
public void init(@NonNull DashboardFragment fragment) {
mFragment = fragment;
mDialogHandler = new AudioSharingDialogHandler(mContext, fragment);
}
@Override
public void onStart(@NonNull LifecycleOwner owner) {
var unused = ThreadUtils.postOnBackgroundThread(() -> {
if (!isAvailable()) {
Log.d(TAG, "Skip onStart(), feature is not supported.");
return;
}
if (mEventManager == null || mDialogHandler == null || mAssistant == null) {
Log.d(TAG, "Skip onStart(), profile is not ready.");
return;
}
Log.d(TAG, "onStart() Register callbacks.");
mEventManager.registerCallback(this);
mAssistant.registerServiceCallBack(mExecutor, mAssistantCallback);
mDialogHandler.registerCallbacks(mExecutor);
});
}
@Override
public void onStop(@NonNull LifecycleOwner owner) {
var unused = ThreadUtils.postOnBackgroundThread(() -> {
if (!isAvailable()) {
Log.d(TAG, "Skip onStop(), feature is not supported.");
return;
}
if (mEventManager == null || mDialogHandler == null || mAssistant == null) {
Log.d(TAG, "Skip onStop(), profile is not ready.");
return;
}
Log.d(TAG, "onStop() Unregister callbacks.");
mEventManager.unregisterCallback(this);
mAssistant.unregisterServiceCallBack(mAssistantCallback);
mDialogHandler.unregisterCallbacks();
});
}
@Override
public int getAvailabilityStatus() {
return (Flags.promoteAudioSharingForSecondAutoConnectedLeaDevice()
&& BluetoothUtils.isAudioSharingUIAvailable(mContext))
? AVAILABLE_UNSEARCHABLE
: UNSUPPORTED_ON_DEVICE;
}
@Override
public String getPreferenceKey() {
return KEY;
}
@Override
public int getSliceHighlightMenuRes() {
return 0;
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
if (mFragment == null
|| mFragment.getActivity() == null
|| mFragment.getActivity().getIntent() == null) {
Log.d(TAG, "Skip handleDeviceConnectedFromIntent, fragment intent is null");
return;
}
Intent intent = mFragment.getActivity().getIntent();
var unused =
ThreadUtils.postOnBackgroundThread(() -> handleDeviceConnectedFromIntent(intent));
}
@Override
public void onProfileConnectionStateChanged(
@NonNull CachedBluetoothDevice cachedDevice,
@ConnectionState int state,
int bluetoothProfile) {
if (mDialogHandler == null || mFragment == null) {
Log.d(TAG, "Ignore onProfileConnectionStateChanged, not init correctly");
return;
}
// Close related dialogs if the BT remote device is disconnected.
if (state == BluetoothAdapter.STATE_DISCONNECTED) {
boolean isLeAudioSupported = BluetoothUtils.isLeAudioSupported(cachedDevice);
if (isLeAudioSupported
&& bluetoothProfile == BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT) {
mDialogHandler.closeOpeningDialogsForLeaDevice(cachedDevice);
} else if (!isLeAudioSupported && !cachedDevice.isConnected()) {
mDialogHandler.closeOpeningDialogsForNonLeaDevice(cachedDevice);
}
}
}
/** Handle just connected device via intent. */
@WorkerThread
public void handleDeviceConnectedFromIntent(@NonNull Intent intent) {
BluetoothDevice device = intent.getParcelableExtra(EXTRA_BLUETOOTH_DEVICE,
BluetoothDevice.class);
CachedBluetoothDevice cachedDevice =
(device == null || mDeviceManager == null)
? null
: mDeviceManager.findDevice(device);
if (cachedDevice == null) {
Log.d(TAG, "Skip handleDeviceConnectedFromIntent, device is null");
return;
}
if (mDialogHandler == null) {
Log.d(TAG, "Skip handleDeviceConnectedFromIntent, handler is null");
return;
}
Log.d(TAG, "handleDeviceConnectedFromIntent, device = " + device.getAnonymizedAddress());
mDialogHandler.handleDeviceConnected(cachedDevice, /* userTriggered= */ false);
}
@VisibleForTesting
void setDialogHandler(@Nullable AudioSharingDialogHandler dialogHandler) {
mDialogHandler = dialogHandler;
}
}

View File

@@ -0,0 +1,55 @@
/*
* Copyright (C) 2025 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.connecteddevice.audiosharing;
import android.content.Context;
import androidx.annotation.Nullable;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
public class AudioSharingJoinHandlerDashboardFragment extends DashboardFragment {
private static final String TAG = "AudioSharingJoinHandlerFrag";
@Nullable private AudioSharingJoinHandlerController mController;
@Override
public int getMetricsCategory() {
// TODO: use real enum
return 0;
}
@Override
protected int getPreferenceScreenResId() {
return R.xml.bluetooth_le_audio_sharing_join_handler;
}
@Override
protected String getLogTag() {
return TAG;
}
@Override
public void onAttach(Context context) {
super.onAttach(context);
mController = use(AudioSharingJoinHandlerController.class);
if (mController != null) {
mController.init(this);
}
}
}

View File

@@ -16,8 +16,6 @@
package com.android.settings.connecteddevice.display;
import static android.view.Display.INVALID_DISPLAY;
import static com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.DISPLAY_ID_ARG;
import static com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.EXTERNAL_DISPLAY_HELP_URL;
import static com.android.settings.connecteddevice.display.ExternalDisplaySettingsConfiguration.EXTERNAL_DISPLAY_NOT_FOUND_RESOURCE;
@@ -60,7 +58,6 @@ import com.android.settingslib.display.DisplayDensityUtils;
import com.android.settingslib.widget.FooterPreference;
import com.android.settingslib.widget.IllustrationPreference;
import com.android.settingslib.widget.MainSwitchPreference;
import com.android.settingslib.widget.TwoTargetPreference;
import java.util.ArrayList;
import java.util.HashMap;
@@ -87,10 +84,12 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
EXTERNAL_DISPLAY_RESOLUTION(60, "external_display_resolution",
R.string.external_display_resolution_settings_title),
// Built-in display link is after per-display settings.
// Built-in display link is before per-display settings.
BUILTIN_DISPLAY_LIST(70, "builtin_display_list_preference",
R.string.builtin_display_settings_category),
EXTERNAL_DISPLAY_LIST(-1, "external_display_list", null),
// If shown, footer should appear below everything.
FOOTER(90, "footer_preference", null);
@@ -106,16 +105,27 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
public final String key;
@Nullable public final Integer titleResource;
void apply(Preference preference) {
/**
* Applies this basic data to the given preference.
*
* @param preference object whose properties to set
* @param nth if non-null, disambiguates the key so that other preferences can have the same
* basic properties. Does not affect the order.
*/
void apply(Preference preference, @Nullable Integer nth) {
if (order != -1) {
preference.setOrder(order);
}
if (titleResource != null) {
preference.setTitle(titleResource);
}
preference.setKey(key);
preference.setKey(nth == null ? key : keyForNth(nth));
preference.setPersistent(false);
}
String keyForNth(int nth) {
return key + "_" + nth;
}
}
static final int EXTERNAL_DISPLAY_SETTINGS_RESOURCE = R.xml.external_display_settings;
@@ -131,12 +141,8 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
R.drawable.external_display_mirror_portrait;
static final int EXTERNAL_DISPLAY_SIZE_SUMMARY_RESOURCE = R.string.screen_zoom_short_summary;
@VisibleForTesting
static final String PREVIOUSLY_SHOWN_LIST_KEY = "mPreviouslyShownListOfDisplays";
private boolean mStarted;
@Nullable
private IllustrationPreference mImagePreference;
@Nullable
private Preference mDisplayTopologyPreference;
@Nullable
private PreferenceCategory mBuiltinDisplayPreference;
@@ -156,7 +162,6 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
scheduleUpdate();
}
};
private boolean mPreviouslyShownListOfDisplays;
public ExternalDisplayPreferenceFragment() {}
@@ -175,12 +180,6 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
return EXTERNAL_DISPLAY_HELP_URL;
}
@Override
public void onSaveInstanceStateCallback(@NonNull Bundle outState) {
outState.putSerializable(PREVIOUSLY_SHOWN_LIST_KEY,
mPreviouslyShownListOfDisplays);
}
@Override
public void onCreateCallback(@Nullable Bundle icicle) {
if (mInjector == null) {
@@ -191,7 +190,6 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
@Override
public void onActivityCreatedCallback(@Nullable Bundle savedInstanceState) {
restoreState(savedInstanceState);
View view = getView();
TextView emptyView = null;
if (view != null) {
@@ -241,17 +239,6 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
.setSourceMetricsCategory(getMetricsCategory()).launch();
}
@VisibleForTesting
protected void launchExternalDisplaySettings(final int displayId) {
final Bundle args = new Bundle();
var context = getPrefContext();
args.putInt(DISPLAY_ID_ARG, displayId);
new SubSettingLauncher(context)
.setDestination(this.getClass().getName())
.setArguments(args)
.setSourceMetricsCategory(getMetricsCategory()).launch();
}
@VisibleForTesting
protected void launchBuiltinDisplaySettings() {
final Bundle args = new Bundle();
@@ -275,30 +262,33 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
var pref = refresh.findUnusedPreference(PrefBasics.FOOTER.key);
if (pref == null) {
pref = newFooterPreference(context);
PrefBasics.FOOTER.apply(pref);
PrefBasics.FOOTER.apply(pref, /* nth= */ null);
}
pref.setTitle(title);
refresh.addPreference(pref);
}
@NonNull
private ListPreference reuseRotationPreference(@NonNull Context context, PrefRefresh refresh) {
private ListPreference reuseRotationPreference(@NonNull Context context, PrefRefresh refresh,
int position) {
ListPreference pref = refresh.findUnusedPreference(
PrefBasics.EXTERNAL_DISPLAY_ROTATION.key);
PrefBasics.EXTERNAL_DISPLAY_ROTATION.keyForNth(position));
if (pref == null) {
pref = new ListPreference(context);
PrefBasics.EXTERNAL_DISPLAY_ROTATION.apply(pref);
PrefBasics.EXTERNAL_DISPLAY_ROTATION.apply(pref, position);
}
refresh.addPreference(pref);
return pref;
}
@NonNull
private Preference reuseResolutionPreference(@NonNull Context context, PrefRefresh refresh) {
var pref = refresh.findUnusedPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key);
private Preference reuseResolutionPreference(@NonNull Context context, PrefRefresh refresh,
int position) {
var pref = refresh.findUnusedPreference(
PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(position));
if (pref == null) {
pref = new Preference(context);
PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.apply(pref);
PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.apply(pref, position);
}
refresh.addPreference(pref);
return pref;
@@ -306,41 +296,34 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
@NonNull
private MainSwitchPreference reuseUseDisplayPreference(
@NonNull Context context, @NonNull PrefRefresh refresh) {
Context context, PrefRefresh refresh, int position) {
MainSwitchPreference pref = refresh.findUnusedPreference(
PrefBasics.EXTERNAL_DISPLAY_USE.key);
PrefBasics.EXTERNAL_DISPLAY_USE.keyForNth(position));
if (pref == null) {
pref = new MainSwitchPreference(context);
PrefBasics.EXTERNAL_DISPLAY_USE.apply(pref);
PrefBasics.EXTERNAL_DISPLAY_USE.apply(pref, position);
}
refresh.addPreference(pref);
return pref;
}
@NonNull
@VisibleForTesting
IllustrationPreference getIllustrationPreference(@NonNull Context context) {
if (mImagePreference == null) {
mImagePreference = new IllustrationPreference(context);
PrefBasics.ILLUSTRATION.apply(mImagePreference);
private IllustrationPreference reuseIllustrationPreference(
Context context, PrefRefresh refresh) {
IllustrationPreference pref = refresh.findUnusedPreference(PrefBasics.ILLUSTRATION.key);
if (pref == null) {
pref = new IllustrationPreference(context);
PrefBasics.ILLUSTRATION.apply(pref, /* nth= */ null);
}
return mImagePreference;
}
/**
* @return return display id argument of this settings page.
*/
@VisibleForTesting
protected int getDisplayIdArg() {
var args = getArguments();
return args != null ? args.getInt(DISPLAY_ID_ARG, INVALID_DISPLAY) : INVALID_DISPLAY;
refresh.addPreference(pref);
return pref;
}
@NonNull
private PreferenceCategory getBuiltinDisplayListPreference(@NonNull Context context) {
if (mBuiltinDisplayPreference == null) {
mBuiltinDisplayPreference = new PreferenceCategory(context);
PrefBasics.BUILTIN_DISPLAY_LIST.apply(mBuiltinDisplayPreference);
PrefBasics.BUILTIN_DISPLAY_LIST.apply(mBuiltinDisplayPreference, /* nth= */ null);
}
return mBuiltinDisplayPreference;
}
@@ -356,7 +339,7 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
@NonNull Preference getDisplayTopologyPreference(@NonNull Context context) {
if (mDisplayTopologyPreference == null) {
mDisplayTopologyPreference = new DisplayTopologyPreference(context);
PrefBasics.DISPLAY_TOPOLOGY.apply(mDisplayTopologyPreference);
PrefBasics.DISPLAY_TOPOLOGY.apply(mDisplayTopologyPreference, /* nth= */ null);
}
return mDisplayTopologyPreference;
}
@@ -366,23 +349,23 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
if (pref == null) {
pref = new MirrorPreference(context,
DesktopExperienceFlags.ENABLE_DISPLAY_CONTENT_MODE_MANAGEMENT.isTrue());
PrefBasics.MIRROR.apply(pref);
PrefBasics.MIRROR.apply(pref, /* nth= */ null);
}
refresh.addPreference(pref);
}
@NonNull
private AccessibilitySeekBarPreference reuseSizePreference(Context context,
PrefRefresh refresh, int displayId) {
PrefRefresh refresh, int displayId, int position) {
AccessibilitySeekBarPreference pref =
refresh.findUnusedPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key);
refresh.findUnusedPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(position));
if (pref == null) {
pref = new AccessibilitySeekBarPreference(context, /* attrs= */ null);
pref.setIconStart(R.drawable.ic_remove_24dp);
pref.setIconStartContentDescription(R.string.screen_zoom_make_smaller_desc);
pref.setIconEnd(R.drawable.ic_add_24dp);
pref.setIconEndContentDescription(R.string.screen_zoom_make_larger_desc);
PrefBasics.EXTERNAL_DISPLAY_SIZE.apply(pref);
PrefBasics.EXTERNAL_DISPLAY_SIZE.apply(pref, position);
setStateForDisplaySizePreference(context, displayId, pref);
}
@@ -404,74 +387,43 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
preference.setOnSeekBarChangeListener(seekBarChangeHandler);
}
private void restoreState(@Nullable Bundle savedInstanceState) {
if (savedInstanceState == null) {
return;
}
mPreviouslyShownListOfDisplays = Boolean.TRUE.equals(savedInstanceState.getSerializable(
PREVIOUSLY_SHOWN_LIST_KEY, Boolean.class));
}
private void update() {
final var screen = getPreferenceScreen();
if (screen == null || mInjector == null || mInjector.getContext() == null) {
return;
}
try (var cleanableScreen = new PrefRefresh(screen)) {
updateScreenForDisplayId(getDisplayIdArg(), cleanableScreen, mInjector.getContext());
updateScreen(cleanableScreen, mInjector.getContext());
}
}
private void updateScreenForDisplayId(final int displayId,
@NonNull final PrefRefresh screen, @NonNull Context context) {
final var displaysToShow = externalDisplaysToShow(displayId);
private void updateScreen(final PrefRefresh screen, Context context) {
final var displaysToShow = externalDisplaysToShow();
if (displaysToShow.isEmpty() && displayId == INVALID_DISPLAY) {
showTextWhenNoDisplaysToShow(screen, context);
} else if (displaysToShow.size() == 1
&& ((displayId == INVALID_DISPLAY && !mPreviouslyShownListOfDisplays)
|| displaysToShow.get(0).getDisplayId() == displayId)) {
showDisplaySettings(displaysToShow.get(0), screen, context);
if (displayId == INVALID_DISPLAY && isTopologyPaneEnabled(mInjector)) {
// Only show the topology pane if the user did not arrive via the displays list.
maybeAddV2Components(context, screen);
}
} else if (displayId == INVALID_DISPLAY) {
// If ever shown a list of displays - keep showing it for consistency after
// disconnecting one of the displays, and only one display is left.
mPreviouslyShownListOfDisplays = true;
if (displaysToShow.isEmpty()) {
showTextWhenNoDisplaysToShow(screen, context, /* position= */ 0);
} else {
showDisplaysList(displaysToShow, screen, context);
}
updateSettingsTitle(displaysToShow, displayId);
}
private void updateSettingsTitle(@NonNull final List<Display> displaysToShow, int displayId) {
final Activity activity = getCurrentActivity();
if (activity == null) {
return;
if (activity != null) {
activity.setTitle(EXTERNAL_DISPLAY_TITLE_RESOURCE);
}
if (displaysToShow.size() == 1 && displaysToShow.get(0).getDisplayId() == displayId) {
var displayName = displaysToShow.get(0).getName();
if (!displayName.isEmpty()) {
activity.setTitle(displayName.substring(0, Math.min(displayName.length(), 40)));
return;
}
}
activity.setTitle(EXTERNAL_DISPLAY_TITLE_RESOURCE);
}
private void showTextWhenNoDisplaysToShow(@NonNull final PrefRefresh screen,
@NonNull Context context) {
@NonNull Context context, int position) {
if (isUseDisplaySettingEnabled(mInjector)) {
addUseDisplayPreferenceNoDisplaysFound(context, screen);
addUseDisplayPreferenceNoDisplaysFound(context, screen, position);
}
addFooterPreference(context, screen, EXTERNAL_DISPLAY_NOT_FOUND_FOOTER_RESOURCE);
}
private static PreferenceCategory getCategoryForDisplay(@NonNull Display display,
@NonNull PrefRefresh screen, @NonNull Context context) {
private static PreferenceCategory reuseDisplayCategory(
PrefRefresh screen, Context context, int position) {
// The rest of the settings are in a category with the display name as the title.
String categoryKey = "expanded_display_items_" + display.getDisplayId();
String categoryKey = PrefBasics.EXTERNAL_DISPLAY_LIST.keyForNth(position);
var category = (PreferenceCategory) screen.findUnusedPreference(categoryKey);
if (category != null) {
@@ -479,45 +431,30 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
} else {
category = new PreferenceCategory(context);
screen.addPreference(category);
category.setPersistent(false);
category.setKey(categoryKey);
category.setTitle(display.getName());
category.setOrder(PrefBasics.BUILTIN_DISPLAY_LIST.order + 1);
PrefBasics.EXTERNAL_DISPLAY_LIST.apply(category, position);
category.setOrder(PrefBasics.BUILTIN_DISPLAY_LIST.order + 1 + position);
}
return category;
}
private void showDisplaySettings(@NonNull Display display, @NonNull PrefRefresh screen,
@NonNull Context context) {
private void showDisplaySettings(Display display, PrefRefresh refresh,
Context context, boolean includeV1Helpers, int position) {
final var isEnabled = mInjector != null && mInjector.isDisplayEnabled(display);
if (isUseDisplaySettingEnabled(mInjector)) {
addUseDisplayPreferenceForDisplay(context, screen, display, isEnabled);
addUseDisplayPreferenceForDisplay(context, refresh, display, isEnabled, position);
}
if (!isEnabled) {
// Skip all other settings
return;
}
final var displayRotation = getDisplayRotation(display.getDisplayId());
if (!isTopologyPaneEnabled(mInjector)) {
screen.addPreference(updateIllustrationImage(context, displayRotation));
if (includeV1Helpers) {
addIllustrationImage(context, refresh, displayRotation);
}
if (isTopologyPaneEnabled(mInjector)) {
var displayCategory = getCategoryForDisplay(display, screen, context);
try (var categoryRefresh = new PrefRefresh(displayCategory)) {
addDisplaySettings(context, categoryRefresh, display, displayRotation);
}
} else {
addDisplaySettings(context, screen, display, displayRotation);
}
}
private void addDisplaySettings(Context context, PrefRefresh refresh, Display display,
int displayRotation) {
addResolutionPreference(context, refresh, display);
addRotationPreference(context, refresh, display, displayRotation);
addResolutionPreference(context, refresh, display, position);
addRotationPreference(context, refresh, display, displayRotation, position);
if (isResolutionSettingEnabled(mInjector)) {
// Do not show the footer about changing resolution affecting apps. This is not in the
// UX design for v2, and there is no good place to put it, since (a) if it is on the
@@ -529,13 +466,13 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
// inconsistent with the topology pane, which shows that display.
// TODO(b/352648432): probably remove footer once the pane and rest of v2 UI is in
// place.
if (!isTopologyPaneEnabled(mInjector)) {
if (includeV1Helpers) {
addFooterPreference(
context, refresh, EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE);
}
}
if (isDisplaySizeSettingEnabled(mInjector)) {
addSizePreference(context, refresh, display.getDisplayId());
addSizePreference(context, refresh, display.getDisplayId(), position);
}
}
@@ -555,66 +492,26 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
private void showDisplaysList(@NonNull List<Display> displaysToShow,
@NonNull PrefRefresh screen, @NonNull Context context) {
maybeAddV2Components(context, screen);
int order = PrefBasics.BUILTIN_DISPLAY_LIST.order;
int position = 0;
boolean includeV1Helpers = !isTopologyPaneEnabled(mInjector) && displaysToShow.size() <= 1;
for (var display : displaysToShow) {
var pref = getDisplayPreference(context, display, screen, ++order);
pref.setSummary(display.getMode().getPhysicalWidth() + " x "
+ display.getMode().getPhysicalHeight());
}
}
@VisibleForTesting
static String displayListDisplayCategoryKey(int displayId) {
return "display_list_display_category_" + displayId;
}
@VisibleForTesting
static String resolutionRotationPreferenceKey(int displayId) {
return "display_id_" + displayId;
}
private Preference getDisplayPreference(@NonNull Context context,
@NonNull Display display, @NonNull PrefRefresh groupCleanable, int categoryOrder) {
var itemKey = resolutionRotationPreferenceKey(display.getDisplayId());
var categoryKey = displayListDisplayCategoryKey(display.getDisplayId());
var category = (PreferenceCategory) groupCleanable.findUnusedPreference(categoryKey);
if (category != null) {
groupCleanable.addPreference(category);
return category.findPreference(itemKey);
} else {
category = new PreferenceCategory(context);
category.setPersistent(false);
category.setKey(categoryKey);
category.setOrder(categoryOrder);
// Must add the category to the hierarchy before adding its descendants. Otherwise
// the category will not have a preference manager, which causes an exception when a
// child is added to it.
groupCleanable.addPreference(category);
var prefItem = new DisplayPreference(context, display);
prefItem.setTitle(
context.getString(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.titleResource) + " | "
+ context.getString(PrefBasics.EXTERNAL_DISPLAY_ROTATION.titleResource));
prefItem.setKey(itemKey);
category.addPreference(prefItem);
var category = reuseDisplayCategory(screen, context, position);
category.setTitle(display.getName());
return prefItem;
try (var refresh = new PrefRefresh(category)) {
// The category may have already been populated if it was retrieved from `screen`,
// but we still need to update resolution and rotation items.
showDisplaySettings(display, refresh, context, includeV1Helpers, position);
}
position++;
}
}
private List<Display> externalDisplaysToShow(int displayIdToShow) {
private List<Display> externalDisplaysToShow() {
if (mInjector == null) {
return List.of();
}
if (displayIdToShow != INVALID_DISPLAY) {
var display = mInjector.getDisplay(displayIdToShow);
if (display != null && isDisplayAllowed(display, mInjector)) {
return List.of(display);
}
}
var displaysToShow = new ArrayList<Display>();
for (var display : mInjector.getAllDisplays()) {
if (display != null && isDisplayAllowed(display, mInjector)) {
@@ -624,16 +521,17 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
return displaysToShow;
}
private void addUseDisplayPreferenceNoDisplaysFound(Context context, PrefRefresh refresh) {
final var pref = reuseUseDisplayPreference(context, refresh);
private void addUseDisplayPreferenceNoDisplaysFound(Context context, PrefRefresh refresh,
int position) {
final var pref = reuseUseDisplayPreference(context, refresh, position);
pref.setChecked(false);
pref.setEnabled(false);
pref.setOnPreferenceChangeListener(null);
}
private void addUseDisplayPreferenceForDisplay(final Context context,
PrefRefresh refresh, final Display display, boolean isEnabled) {
final var pref = reuseUseDisplayPreference(context, refresh);
PrefRefresh refresh, final Display display, boolean isEnabled, int position) {
final var pref = reuseUseDisplayPreference(context, refresh, position);
pref.setChecked(isEnabled);
pref.setEnabled(true);
pref.setOnPreferenceChangeListener((p, newValue) -> {
@@ -654,20 +552,19 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
});
}
private Preference updateIllustrationImage(@NonNull final Context context,
private void addIllustrationImage(final Context context, PrefRefresh refresh,
final int displayRotation) {
var pref = getIllustrationPreference(context);
var pref = reuseIllustrationPreference(context, refresh);
if (displayRotation % 2 == 0) {
pref.setLottieAnimationResId(EXTERNAL_DISPLAY_PORTRAIT_DRAWABLE);
} else {
pref.setLottieAnimationResId(EXTERNAL_DISPLAY_LANDSCAPE_DRAWABLE);
}
return pref;
}
private void addRotationPreference(final Context context,
PrefRefresh refresh, final Display display, final int displayRotation) {
var pref = reuseRotationPreference(context, refresh);
PrefRefresh refresh, final Display display, final int displayRotation, int position) {
var pref = reuseRotationPreference(context, refresh, position);
if (mRotationEntries == null || mRotationEntriesValues == null) {
mRotationEntries = new String[] {
context.getString(R.string.external_display_standard_rotation),
@@ -694,8 +591,8 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
}
private void addResolutionPreference(final Context context, PrefRefresh refresh,
final Display display) {
var pref = reuseResolutionPreference(context, refresh);
final Display display, int position) {
var pref = reuseResolutionPreference(context, refresh, position);
pref.setSummary(display.getMode().getPhysicalWidth() + " x "
+ display.getMode().getPhysicalHeight());
pref.setOnPreferenceClickListener((Preference p) -> {
@@ -706,8 +603,9 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
pref.setEnabled(isResolutionSettingEnabled(mInjector));
}
private void addSizePreference(final Context context, PrefRefresh refresh, int displayId) {
var pref = reuseSizePreference(context, refresh, displayId);
private void addSizePreference(final Context context, PrefRefresh refresh, int displayId,
int position) {
var pref = reuseSizePreference(context, refresh, displayId, position);
pref.setSummary(EXTERNAL_DISPLAY_SIZE_SUMMARY_RESOURCE);
pref.setOnPreferenceClickListener(
(Preference p) -> {
@@ -804,27 +702,6 @@ public class ExternalDisplayPreferenceFragment extends SettingsPreferenceFragmen
public void onStopTrackingTouch(@NonNull SeekBar seekBar) {}
}
@VisibleForTesting
class DisplayPreference extends TwoTargetPreference
implements Preference.OnPreferenceClickListener {
private final int mDisplayId;
DisplayPreference(@NonNull final Context context, @NonNull final Display display) {
super(context);
mDisplayId = display.getDisplayId();
setPersistent(false);
setOnPreferenceClickListener(this);
}
@Override
public boolean onPreferenceClick(@NonNull Preference preference) {
launchExternalDisplaySettings(mDisplayId);
writePreferenceClickMetric(preference);
return true;
}
}
private static class PrefRefresh implements AutoCloseable {
private final PreferenceGroup mScreen;
private final HashMap<String, Preference> mUnusedPreferences = new HashMap<>();

View File

@@ -42,6 +42,9 @@ public abstract class SliderPreferenceController extends BasePreferenceControlle
} else if (preference instanceof androidx.preference.SeekBarPreference) {
((androidx.preference.SeekBarPreference) preference)
.setValue(getSliderPosition());
} else if (preference instanceof com.android.settingslib.widget.SliderPreference) {
((com.android.settingslib.widget.SliderPreference) preference)
.setValue(getSliderPosition());
}
}

View File

@@ -0,0 +1,59 @@
/*
* Copyright 2025 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.display.darkmode;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settingslib.widget.TopIntroPreference;
/**
* Controller of the top info preference in the Dark Mode settings page.
*
* This should be removed after the flag android.view.accessibility.force_invert_color is launched.
*/
public class DarkModeTopIntroPreferenceController extends BasePreferenceController {
@Nullable private TopIntroPreference mPreference;
public DarkModeTopIntroPreferenceController(
@NonNull Context context,
@NonNull String preferenceKey) {
super(context, preferenceKey);
}
@Override
public int getAvailabilityStatus() {
return AVAILABLE_UNSEARCHABLE;
}
@Override
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
mPreference = screen.findPreference(getPreferenceKey());
if (android.view.accessibility.Flags.forceInvertColor()) {
mPreference.setTitle(R.string.dark_ui_text_force_invert);
} else {
mPreference.setTitle(R.string.dark_ui_text);
}
}
}

View File

@@ -259,7 +259,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
mDailyChartIndex, mHourlyChartIndex));
refreshUi();
mHandler.post(
() -> mDailyChartView.announceForAccessibility(getAccessibilityAnnounceMessage()));
() -> mDailyChartView.setAccessibilityPaneTitle(getAccessibilityAnnounceMessage()));
if (mOnSelectedIndexUpdatedListener != null) {
mOnSelectedIndexUpdatedListener.onSelectedIndexUpdated();
}
@@ -299,7 +299,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
refreshUi();
mHandler.post(
() ->
mDailyChartView.announceForAccessibility(
mDailyChartView.setAccessibilityPaneTitle(
getAccessibilityAnnounceMessage()));
mMetricsFeatureProvider.action(
mPrefContext,
@@ -326,7 +326,7 @@ public class BatteryChartPreferenceController extends AbstractPreferenceControll
refreshUi();
mHandler.post(
() ->
mHourlyChartView.announceForAccessibility(
mHourlyChartView.setAccessibilityPaneTitle(
getAccessibilityAnnounceMessage()));
mMetricsFeatureProvider.action(
mPrefContext,

View File

@@ -34,6 +34,7 @@ import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.view.ViewCompat;
import androidx.preference.PreferenceCategory;
import androidx.preference.PreferenceScreen;
import androidx.recyclerview.widget.RecyclerView;
import com.android.internal.app.LocaleHelper;
@@ -42,7 +43,6 @@ import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.TopIntroPreference;
import com.google.android.material.appbar.AppBarLayout;
@@ -75,6 +75,7 @@ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment im
private static final String KEY_PREFERENCE_APP_LOCALE_SUGGESTED_LIST =
"app_locale_suggested_list";
private static final String KEY_TOP_INTRO_PREFERENCE = "top_intro_region";
private static final String KEY_PREFERENCE_SCREEN ="key_system_language_picker_page";
private static final String EXTRA_EXPAND_SEARCH_VIEW = "expand_search_view";
@Nullable
@@ -121,9 +122,10 @@ public class RegionAndNumberingSystemPickerFragment extends DashboardFragment im
}
Log.d(TAG, "onCreate, mIsNumberingMode = " + mIsNumberingMode);
if (!mIsNumberingMode) {
mActivity.setTitle(R.string.region_selection_title);
}
PreferenceScreen screen = findPreference(KEY_PREFERENCE_SCREEN);
screen.setTitle(mIsNumberingMode ? R.string.numbering_system_selection_title
: R.string.region_selection_title);
TopIntroPreference topIntroPreference = findPreference(KEY_TOP_INTRO_PREFERENCE);
if (topIntroPreference != null && mIsNumberingMode) {

View File

@@ -70,8 +70,7 @@ public class RedactionInterstitial extends SettingsActivity {
@Override
protected void onCreate(Bundle savedInstance) {
setTheme(SetupWizardUtils.getTheme(this, getIntent()));
ThemeHelper.trySetDynamicColor(this);
ThemeHelper.trySetSuwTheme(this);
super.onCreate(savedInstance);
findViewById(R.id.content_parent).setFitsSystemWindows(false);
}

View File

@@ -8,4 +8,3 @@ himanshuz@google.com
jigarthakkar@google.com
josephpv@google.com
onshimiye@google.com
saumyap@google.com

View File

@@ -27,20 +27,25 @@ import androidx.preference.PreferenceViewHolder
import com.android.settings.R
import com.android.settingslib.spa.framework.theme.SettingsTheme
import com.android.settingslib.widget.GroupSectionDividerMixin
import com.android.settingslib.widget.NormalPaddingMixin
open class ComposeGroupSectionPreference @JvmOverloads constructor(
open class ComposeGroupSectionPreference
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0,
) : ComposePreference(context, attrs, defStyleAttr, defStyleRes), GroupSectionDividerMixin
open class ComposePreference @JvmOverloads constructor(
open class ComposePreference
@JvmOverloads
constructor(
context: Context,
attrs: AttributeSet? = null,
defStyleAttr: Int = 0,
defStyleRes: Int = 0,
) : Preference(context, attrs, defStyleAttr, defStyleRes) {
) : Preference(context, attrs, defStyleAttr, defStyleRes), NormalPaddingMixin {
private var content: @Composable () -> Unit = {}
fun setContent(content: @Composable () -> Unit) {

View File

@@ -545,13 +545,13 @@ public class WifiConfigController implements TextWatcher,
&& !isValidSaePassword(mPasswordView.getText().toString())))) {
passwordInvalid = true;
}
if ((mSsidView != null && mSsidView.length() == 0)
// If Accesspoint is not saved, apply passwordInvalid check
|| ((mAccessPoint == null || !mAccessPoint.isSaved()) && passwordInvalid
// If AccessPoint is saved (modifying network) and password is changed, apply
// Invalid password check
|| mAccessPoint != null && mAccessPoint.isSaved() && passwordInvalid
&& mPasswordView.length() > 0)) {
if ((mAccessPoint == null || !mAccessPoint.isSaved()) && passwordInvalid) {
// If Accesspoint is not saved, apply passwordInvalid check
enabled = false;
} else if (mAccessPoint != null && mAccessPoint.isSaved() && passwordInvalid
&& mPasswordView.length() > 0) {
// If AccessPoint is saved (modifying network) and password is changed, apply
// Invalid password check
enabled = false;
} else {
enabled = ipAndProxyFieldsAreValid();

View File

@@ -28,6 +28,8 @@ import android.widget.TextView;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.wifi.utils.SsidInputGroup;
import com.android.settings.wifi.utils.WifiDialogHelper;
import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.wifi.AccessPoint;
@@ -62,6 +64,7 @@ public class WifiDialog extends AlertDialog implements WifiConfigUiBase,
private View mView;
private WifiConfigController mController;
private boolean mHideSubmitButton;
private WifiDialogHelper mDialogHelper;
/**
* Creates a WifiDialog with no additional style. It displays as a dialog above the current
@@ -115,6 +118,9 @@ public class WifiDialog extends AlertDialog implements WifiConfigUiBase,
if (mAccessPoint == null) {
mController.hideForgetButton();
}
mDialogHelper = new WifiDialogHelper(this,
new SsidInputGroup(getContext(), mView, R.id.ssid_layout, R.id.ssid));
}
@SuppressWarnings("MissingSuperCall") // TODO: Fix me
@@ -155,9 +161,6 @@ public class WifiDialog extends AlertDialog implements WifiConfigUiBase,
public void onClick(DialogInterface dialogInterface, int id) {
if (mListener != null) {
switch (id) {
case BUTTON_SUBMIT:
mListener.onSubmit(this);
break;
case BUTTON_FORGET:
if (WifiUtils.isNetworkLockedDown(getContext(), mAccessPoint.getConfig())) {
RestrictedLockUtils.sendShowAdminSupportDetailsIntent(getContext(),
@@ -170,6 +173,11 @@ public class WifiDialog extends AlertDialog implements WifiConfigUiBase,
}
}
/** Return true to tell the parent activity to call onSubmit before onDismiss. */
public boolean shouldSubmitBeforeFinish() {
return mDialogHelper.isPositive();
}
@Override
public int getMode() {
return mMode;

View File

@@ -346,6 +346,9 @@ public class WifiDialogActivity extends ObservableActivity implements WifiDialog
@Override
public void onDismiss(DialogInterface dialogInterface) {
mDialog2 = null;
if (mDialog != null && mDialog.shouldSubmitBeforeFinish()) {
onSubmit(mDialog);
}
mDialog = null;
finish();
}

View File

@@ -0,0 +1,49 @@
/*
* Copyright (C) 2025 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.utils
import android.content.DialogInterface
import android.util.Log
import androidx.appcompat.app.AlertDialog
open class AlertDialogHelper(val alertDialog: AlertDialog) {
var isPositive = false
init {
alertDialog.setOnShowListener {
alertDialog.getButton(DialogInterface.BUTTON_POSITIVE)?.setOnClickListener {
onPositiveButtonClicked()
} ?: Log.e(TAG, "Can't get the positive button!")
}
}
open fun onPositiveButtonClicked() {
if (!canDismiss()) {
Log.w(TAG, "Can't dismiss dialog!")
return
}
isPositive = true
alertDialog.dismiss()
}
open fun canDismiss() = true
companion object {
const val TAG = "AlertDialogHelper"
}
}

View File

@@ -0,0 +1,36 @@
/*
* Copyright (C) 2025 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.utils
import android.util.Log
import androidx.appcompat.app.AlertDialog
class WifiDialogHelper(
alertDialog: AlertDialog,
private val ssidInputGroup: SsidInputGroup? = null,
) : AlertDialogHelper(alertDialog) {
override fun canDismiss(): Boolean {
val isValid = ssidInputGroup?.validate() ?: true
if (!isValid) Log.w(TAG, "SSID is invalid!")
return isValid
}
companion object {
const val TAG = "WifiDialogHelper"
}
}

View File

@@ -32,8 +32,8 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.SliderPreference;
import org.junit.Before;
import org.junit.Test;
@@ -56,7 +56,7 @@ public class AlarmVibrationIntensityPreferenceControllerTest {
private Context mContext;
private Vibrator mVibrator;
private AlarmVibrationIntensityPreferenceController mController;
private SeekBarPreference mPreference;
private SliderPreference mPreference;
@Before
public void setUp() {
@@ -69,7 +69,7 @@ public class AlarmVibrationIntensityPreferenceControllerTest {
mController = new AlarmVibrationIntensityPreferenceController(mContext, PREFERENCE_KEY,
Vibrator.VIBRATION_INTENSITY_HIGH);
mLifecycle.addObserver(mController);
mPreference = new SeekBarPreference(mContext);
mPreference = new SliderPreference(mContext);
mPreference.setSummary("Test summary");
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen);
@@ -91,7 +91,7 @@ public class AlarmVibrationIntensityPreferenceControllerTest {
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(
assertThat(mPreference.getValue()).isEqualTo(
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_ALARM));
}
@@ -101,17 +101,17 @@ public class AlarmVibrationIntensityPreferenceControllerTest {
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
}
@@ -119,25 +119,25 @@ public class AlarmVibrationIntensityPreferenceControllerTest {
public void updateState_shouldDisplayIntensityInSliderPosition() {
updateSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
updateSetting(Settings.System.ALARM_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
updateSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(Settings.System.ALARM_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
public void setProgress_updatesIntensitySetting() throws Exception {
public void setSliderPosition_updatesIntensitySetting() throws Exception {
mController.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(readSetting(Settings.System.ALARM_VIBRATION_INTENSITY))
.isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);

View File

@@ -32,8 +32,8 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.SliderPreference;
import org.junit.Before;
import org.junit.Test;
@@ -58,7 +58,7 @@ public class HapticFeedbackIntensityPreferenceControllerTest {
private Context mContext;
private Vibrator mVibrator;
private HapticFeedbackIntensityPreferenceController mController;
private SeekBarPreference mPreference;
private SliderPreference mPreference;
@Before
public void setUp() {
@@ -71,7 +71,7 @@ public class HapticFeedbackIntensityPreferenceControllerTest {
mController = new HapticFeedbackIntensityPreferenceController(mContext, PREFERENCE_KEY,
Vibrator.VIBRATION_INTENSITY_HIGH);
mLifecycle.addObserver(mController);
mPreference = new SeekBarPreference(mContext);
mPreference = new SliderPreference(mContext);
mPreference.setSummary("Test summary");
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen);
@@ -91,7 +91,7 @@ public class HapticFeedbackIntensityPreferenceControllerTest {
Settings.System.putString(mContext.getContentResolver(),
Settings.System.HAPTIC_FEEDBACK_INTENSITY, /* value= */ null);
mController.updateState(mPreference);
assertThat(mPreference.getProgress())
assertThat(mPreference.getValue())
.isEqualTo(mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_TOUCH));
}
@@ -101,17 +101,17 @@ public class HapticFeedbackIntensityPreferenceControllerTest {
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
}
@@ -121,44 +121,44 @@ public class HapticFeedbackIntensityPreferenceControllerTest {
updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
public void updateState_shouldDisplayIntensityInSliderPosition() {
updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
public void setProgress_updatesIntensityAndDependentSettings() throws Exception {
public void setSliderPosition_updatesIntensityAndDependentSettings() throws Exception {
mController.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(readSetting(Settings.System.HAPTIC_FEEDBACK_INTENSITY))
.isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);

View File

@@ -156,9 +156,7 @@ public class HearingDevicePairingFragmentTest {
@Test
public void handleLeScanResult_markDeviceAsHearingAid() {
ScanResult scanResult = mock(ScanResult.class);
doReturn(mDevice).when(scanResult).getDevice();
doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice);
ScanResult scanResult = createMockScanResult();
mFragment.handleLeScanResult(scanResult);
@@ -167,9 +165,7 @@ public class HearingDevicePairingFragmentTest {
@Test
public void handleLeScanResult_isAndroidCompatible_addDevice() {
ScanResult scanResult = mock(ScanResult.class);
doReturn(mDevice).when(scanResult).getDevice();
doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice);
ScanResult scanResult = createMockScanResult();
doReturn(true).when(mFragment).isAndroidCompatibleHearingAid(scanResult);
mFragment.handleLeScanResult(scanResult);
@@ -179,9 +175,7 @@ public class HearingDevicePairingFragmentTest {
@Test
public void handleLeScanResult_isNotAndroidCompatible_discoverServices() {
ScanResult scanResult = mock(ScanResult.class);
doReturn(mDevice).when(scanResult).getDevice();
doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice);
ScanResult scanResult = createMockScanResult();
doReturn(false).when(mFragment).isAndroidCompatibleHearingAid(scanResult);
mFragment.handleLeScanResult(scanResult);
@@ -191,9 +185,7 @@ public class HearingDevicePairingFragmentTest {
@Test
public void handleLeScanResult_alreadyBonded_doNothing() {
ScanResult scanResult = mock(ScanResult.class);
doReturn(mDevice).when(scanResult).getDevice();
doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice);
ScanResult scanResult = createMockScanResult();
doReturn(BluetoothDevice.BOND_BONDED).when(mCachedDevice).getBondState();
mFragment.handleLeScanResult(scanResult);
@@ -292,6 +284,14 @@ public class HearingDevicePairingFragmentTest {
assertThat(isCompatible).isFalse();
}
private ScanResult createMockScanResult() {
ScanResult scanResult = mock(ScanResult.class);
doReturn(mDevice).when(scanResult).getDevice();
doReturn(mCachedDevice).when(mCachedDeviceManager).findDevice(mDevice);
doReturn(true).when(mFragment).isDeviceDiscoverable(scanResult);
return scanResult;
}
private ScanResult createAshaScanResult() {
ScanResult scanResult = mock(ScanResult.class);
ScanRecord scanRecord = mock(ScanRecord.class);

View File

@@ -34,8 +34,8 @@ import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.SliderPreference;
import org.junit.After;
import org.junit.Before;
@@ -60,7 +60,7 @@ public class MediaVibrationIntensityPreferenceControllerTest {
private Context mContext;
private Vibrator mVibrator;
private MediaVibrationIntensityPreferenceController mController;
private SeekBarPreference mPreference;
private SliderPreference mPreference;
@Before
public void setUp() {
@@ -73,7 +73,7 @@ public class MediaVibrationIntensityPreferenceControllerTest {
mController = new MediaVibrationIntensityPreferenceController(mContext, PREFERENCE_KEY,
Vibrator.VIBRATION_INTENSITY_HIGH);
mLifecycle.addObserver(mController);
mPreference = new SeekBarPreference(mContext);
mPreference = new SliderPreference(mContext);
mPreference.setSummary("Test summary");
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen);
@@ -100,7 +100,7 @@ public class MediaVibrationIntensityPreferenceControllerTest {
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(
assertThat(mPreference.getValue()).isEqualTo(
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_MEDIA));
}
@@ -110,17 +110,17 @@ public class MediaVibrationIntensityPreferenceControllerTest {
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
}
@@ -128,25 +128,25 @@ public class MediaVibrationIntensityPreferenceControllerTest {
public void updateState_shouldDisplayIntensityInSliderPosition() {
updateSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
updateSetting(Settings.System.MEDIA_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
updateSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(Settings.System.MEDIA_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
public void setProgress_updatesIntensitySetting() throws Exception {
public void setSliderPosition_updatesIntensitySetting() throws Exception {
mController.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(readSetting(Settings.System.MEDIA_VIBRATION_INTENSITY))
.isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);

View File

@@ -30,10 +30,11 @@ import android.provider.Settings;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.SliderPreference;
import org.junit.Before;
import org.junit.Test;
@@ -56,7 +57,7 @@ public class NotificationVibrationIntensityPreferenceControllerTest {
private Context mContext;
private Vibrator mVibrator;
private NotificationVibrationIntensityPreferenceController mController;
private SeekBarPreference mPreference;
private SliderPreference mPreference;
@Before
public void setUp() {
@@ -69,7 +70,7 @@ public class NotificationVibrationIntensityPreferenceControllerTest {
mController = new NotificationVibrationIntensityPreferenceController(mContext,
PREFERENCE_KEY, Vibrator.VIBRATION_INTENSITY_HIGH);
mLifecycle.addObserver(mController);
mPreference = new SeekBarPreference(mContext);
mPreference = new SliderPreference(mContext);
mPreference.setSummary("Test summary");
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen);
@@ -89,7 +90,7 @@ public class NotificationVibrationIntensityPreferenceControllerTest {
Settings.System.putString(mContext.getContentResolver(),
Settings.System.NOTIFICATION_VIBRATION_INTENSITY, /* value= */ null);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(
assertThat(mPreference.getValue()).isEqualTo(
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_NOTIFICATION));
}
@@ -101,22 +102,21 @@ public class NotificationVibrationIntensityPreferenceControllerTest {
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getSummary()).isNull();
assertThat(mPreference.isEnabled()).isTrue();
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
// TODO(b/136805769): summary is broken in SeekBarPreference, enable this once fixed
// assertThat(mPreference.getSummary()).isNotNull();
// assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString(
// R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary));
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getSummary()).isNotNull();
assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString(
R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary));
assertThat(mPreference.isEnabled()).isFalse();
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getSummary()).isNull();
assertThat(mPreference.isEnabled()).isTrue();
}
@@ -126,27 +126,27 @@ public class NotificationVibrationIntensityPreferenceControllerTest {
updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_HIGH);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_LOW);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_OFF);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
public void setProgress_updatesIntensitySetting() throws Exception {
public void setSliderPosition_updatesIntensitySetting() throws Exception {
mController.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(readSetting(Settings.System.NOTIFICATION_VIBRATION_INTENSITY))
.isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);

View File

@@ -30,10 +30,11 @@ import android.provider.Settings;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.SliderPreference;
import org.junit.Before;
import org.junit.Test;
@@ -59,7 +60,7 @@ public class RingVibrationIntensityPreferenceControllerTest {
private Context mContext;
private Vibrator mVibrator;
private RingVibrationIntensityPreferenceController mController;
private SeekBarPreference mPreference;
private SliderPreference mPreference;
@Before
public void setUp() {
@@ -72,7 +73,7 @@ public class RingVibrationIntensityPreferenceControllerTest {
mController = new RingVibrationIntensityPreferenceController(mContext, PREFERENCE_KEY,
Vibrator.VIBRATION_INTENSITY_HIGH);
mLifecycle.addObserver(mController);
mPreference = new SeekBarPreference(mContext);
mPreference = new SliderPreference(mContext);
mPreference.setSummary("Test summary");
when(mScreen.findPreference(mController.getPreferenceKey())).thenReturn(mPreference);
mController.displayPreference(mScreen);
@@ -92,7 +93,7 @@ public class RingVibrationIntensityPreferenceControllerTest {
Settings.System.putString(mContext.getContentResolver(),
Settings.System.RING_VIBRATION_INTENSITY, /* value= */ null);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(
assertThat(mPreference.getValue()).isEqualTo(
mVibrator.getDefaultVibrationIntensity(VibrationAttributes.USAGE_RINGTONE));
}
@@ -102,22 +103,21 @@ public class RingVibrationIntensityPreferenceControllerTest {
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_NORMAL);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getSummary()).isNull();
assertThat(mPreference.isEnabled()).isTrue();
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_SILENT);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
// TODO(b/136805769): summary is broken in SeekBarPreference, enable this once fixed
// assertThat(mPreference.getSummary()).isNotNull();
// assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString(
// R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary));
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getSummary()).isNotNull();
assertThat(mPreference.getSummary().toString()).isEqualTo(mContext.getString(
R.string.accessibility_vibration_setting_disabled_for_silent_mode_summary));
assertThat(mPreference.isEnabled()).isFalse();
when(mAudioManager.getRingerModeInternal()).thenReturn(AudioManager.RINGER_MODE_VIBRATE);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getSummary()).isNull();
assertThat(mPreference.isEnabled()).isTrue();
}
@@ -129,44 +129,44 @@ public class RingVibrationIntensityPreferenceControllerTest {
updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
updateSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
public void updateState_shouldDisplayIntensityInSliderPosition() {
updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_HIGH);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
updateSetting(Settings.System.RING_VIBRATION_INTENSITY,
Vibrator.VIBRATION_INTENSITY_MEDIUM);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_LOW);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(Settings.System.RING_VIBRATION_INTENSITY, Vibrator.VIBRATION_INTENSITY_OFF);
mController.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
public void setProgress_updatesIntensityAndDependentSettings() throws Exception {
public void setSliderPosition_updatesIntensityAndDependentSettings() throws Exception {
mController.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(readSetting(Settings.System.RING_VIBRATION_INTENSITY))
.isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);

View File

@@ -29,8 +29,8 @@ import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
import com.android.settings.widget.SeekBarPreference;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.widget.SliderPreference;
import org.junit.Before;
import org.junit.Test;
@@ -70,7 +70,7 @@ public class VibrationIntensityPreferenceControllerTest {
private Lifecycle mLifecycle;
private Context mContext;
private Vibrator mVibrator;
private SeekBarPreference mPreference;
private SliderPreference mPreference;
@Before
public void setUp() {
@@ -85,7 +85,7 @@ public class VibrationIntensityPreferenceControllerTest {
VibrationIntensityPreferenceController controller = createPreferenceController(3);
Settings.System.putString(mContext.getContentResolver(), SETTING_KEY, /* value= */ null);
controller.updateState(mPreference);
assertThat(mPreference.getProgress())
assertThat(mPreference.getValue())
.isEqualTo(mVibrator.getDefaultVibrationIntensity(VIBRATION_USAGE));
}
@@ -96,17 +96,17 @@ public class VibrationIntensityPreferenceControllerTest {
updateSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, ON);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
updateSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, OFF);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.isEnabled()).isFalse();
updateSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, ON);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.isEnabled()).isTrue();
}
@@ -116,19 +116,19 @@ public class VibrationIntensityPreferenceControllerTest {
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_HIGH);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_HIGH);
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_MEDIUM);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_LOW);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_OFF);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
@@ -137,19 +137,19 @@ public class VibrationIntensityPreferenceControllerTest {
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_HIGH);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_MEDIUM);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_MEDIUM);
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_LOW);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_OFF);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
@@ -158,39 +158,39 @@ public class VibrationIntensityPreferenceControllerTest {
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_HIGH);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_MEDIUM);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_LOW);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_OFF);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
public void setProgress_mainSwitchDisabled_ignoresUpdates() throws Exception {
public void setSliderPosition_mainSwitchDisabled_ignoresUpdates() throws Exception {
VibrationIntensityPreferenceController controller = createPreferenceController(3);
updateSetting(SETTING_KEY, Vibrator.VIBRATION_INTENSITY_LOW);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
updateSetting(VibrationPreferenceConfig.MAIN_SWITCH_SETTING_KEY, OFF);
controller.updateState(mPreference);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.isEnabled()).isFalse();
assertThat(controller.setSliderPosition(Vibrator.VIBRATION_INTENSITY_HIGH)).isFalse();
assertThat(readSetting(SETTING_KEY)).isEqualTo(Vibrator.VIBRATION_INTENSITY_LOW);
assertThat(mPreference.getProgress()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
assertThat(mPreference.getValue()).isEqualTo(Vibrator.VIBRATION_INTENSITY_OFF);
}
@Test
public void setProgress_allSupportedPositions_updatesIntensitySetting() throws Exception {
public void setSliderPosition_allSupportedPositions_updatesIntensitySetting() throws Exception {
VibrationIntensityPreferenceController controller = createPreferenceController(3);
controller.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF);
@@ -207,7 +207,8 @@ public class VibrationIntensityPreferenceControllerTest {
}
@Test
public void setProgress_twoSupportedPositions_updatesMediumPositionToHigh() throws Exception {
public void setSliderPosition_twoSupportedPositions_updatesMediumPositionToHigh()
throws Exception {
VibrationIntensityPreferenceController controller = createPreferenceController(2);
controller.setSliderPosition(Vibrator.VIBRATION_INTENSITY_OFF);
@@ -224,7 +225,7 @@ public class VibrationIntensityPreferenceControllerTest {
}
@Test
public void setProgress_oneSupportedPosition_updatesOnPositionsToDeviceDefault()
public void setSliderPosition_oneSupportedPosition_updatesOnPositionsToDeviceDefault()
throws Exception {
int defaultIntensity = mVibrator.getDefaultVibrationIntensity(VIBRATION_USAGE);
VibrationIntensityPreferenceController controller = createPreferenceController(1);
@@ -255,7 +256,7 @@ public class VibrationIntensityPreferenceControllerTest {
VibrationIntensityPreferenceController controller =
new TestPreferenceController(mContext, supportedIntensityLevels);
mLifecycle.addObserver(controller);
mPreference = new SeekBarPreference(mContext);
mPreference = new SliderPreference(mContext);
mPreference.setSummary("Test summary");
when(mScreen.findPreference(controller.getPreferenceKey())).thenReturn(mPreference);
controller.displayPreference(mScreen);

View File

@@ -35,7 +35,6 @@ import android.content.Context;
import android.content.pm.PackageManager;
import android.content.res.Resources;
import android.hardware.input.InputManager;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.util.FeatureFlagUtils;
import android.view.InputDevice;
@@ -54,7 +53,6 @@ import com.android.settings.connecteddevice.stylus.StylusDeviceUpdater;
import com.android.settings.connecteddevice.usb.ConnectedUsbDeviceUpdater;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.flags.FakeFeatureFlagsImpl;
import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -213,6 +211,7 @@ public class ConnectedDeviceGroupControllerTest {
mConnectedDeviceGroupController.onStart();
verify(mExternalDisplayUpdater).registerCallback();
verify(mExternalDisplayUpdater).refreshPreference();
verify(mConnectedBluetoothDeviceUpdater).registerCallback();
verify(mConnectedUsbDeviceUpdater).registerCallback();
verify(mConnectedDockUpdater).registerCallback();

View File

@@ -0,0 +1,94 @@
/*
* Copyright (C) 2025 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.connecteddevice.audiosharing;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.verify;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.os.Bundle;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settingslib.flags.Flags;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowBluetoothAdapter.class})
public class AudioSharingJoinHandlerActivityTest {
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
private AudioSharingJoinHandlerActivity mActivity;
@Before
public void setUp() {
mActivity = spy(Robolectric.buildActivity(AudioSharingJoinHandlerActivity.class).get());
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
}
@Test
@DisableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
public void onCreate_flagOff_finish() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mActivity.onCreate(new Bundle());
verify(mActivity).finish();
}
@Test
@EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE})
public void onCreate_flagOn_create() {
mActivity.onCreate(new Bundle());
verify(mActivity, never()).finish();
}
@Test
public void isValidFragment_returnsTrue() {
assertThat(mActivity.isValidFragment(
AudioSharingJoinHandlerDashboardFragment.class.getName())).isTrue();
}
@Test
public void isValidFragment_returnsFalse() {
assertThat(mActivity.isValidFragment("")).isFalse();
}
}

View File

@@ -0,0 +1,326 @@
/*
* Copyright (C) 2025 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.connecteddevice.audiosharing;
import static com.android.settings.core.BasePreferenceController.AVAILABLE_UNSEARCHABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast.EXTRA_BLUETOOTH_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.verifyNoInteractions;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.content.Intent;
import android.os.Looper;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import androidx.fragment.app.FragmentActivity;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.bluetooth.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settings.testutils.shadow.ShadowFragment;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LeAudioProfile;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.android.settingslib.flags.Flags;
import com.google.common.collect.ImmutableList;
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.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.concurrent.Executor;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {
ShadowBluetoothAdapter.class,
ShadowBluetoothUtils.class,
ShadowFragment.class,
})
public class AudioSharingJoinHandlerControllerTest {
private static final String PREF_KEY = "audio_sharing_join_handler";
@Rule
public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Spy
Context mContext = ApplicationProvider.getApplicationContext();
private Lifecycle mLifecycle;
private LifecycleOwner mLifecycleOwner;
@Mock
private LocalBluetoothManager mLocalBtManager;
@Mock private BluetoothEventManager mEventManager;
@Mock private LocalBluetoothProfileManager mProfileManager;
@Mock private CachedBluetoothDeviceManager mDeviceManager;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private PreferenceScreen mScreen;
@Mock private DashboardFragment mFragment;
@Mock private FragmentActivity mActivity;
@Mock private AudioSharingDialogHandler mDialogHandler;
private AudioSharingJoinHandlerController mController;
@Before
public void setUp() {
ShadowBluetoothAdapter shadowBluetoothAdapter =
Shadow.extract(BluetoothAdapter.getDefaultAdapter());
shadowBluetoothAdapter.setEnabled(true);
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
shadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
mLocalBtManager = Utils.getLocalBtManager(mContext);
when(mLocalBtManager.getEventManager()).thenReturn(mEventManager);
when(mLocalBtManager.getProfileManager()).thenReturn(mProfileManager);
when(mLocalBtManager.getCachedDeviceManager()).thenReturn(mDeviceManager);
when(mProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
mController = new AudioSharingJoinHandlerController(mContext, PREF_KEY);
doReturn(mActivity).when(mFragment).getActivity();
mController.init(mFragment);
mController.setDialogHandler(mDialogHandler);
}
@After
public void tearDown() {
ShadowBluetoothUtils.reset();
}
@Test
@DisableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
public void onStart_flagOff_doNothing() {
mController.onStart(mLifecycleOwner);
verify(mEventManager, never()).registerCallback(any(BluetoothCallback.class));
verify(mDialogHandler, never()).registerCallbacks(any(Executor.class));
verify(mAssistant, never())
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
@EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE})
public void onStart_flagOn_registerCallbacks() {
mController.onStart(mLifecycleOwner);
verify(mEventManager).registerCallback(any(BluetoothCallback.class));
verify(mDialogHandler).registerCallbacks(any(Executor.class));
verify(mAssistant)
.registerServiceCallBack(
any(Executor.class), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
@DisableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
public void onStop_flagOff_doNothing() {
mController.onStop(mLifecycleOwner);
verify(mEventManager, never()).unregisterCallback(any(BluetoothCallback.class));
verify(mDialogHandler, never()).unregisterCallbacks();
verify(mAssistant, never())
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
@EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE})
public void onStop_flagOn_unregisterCallbacks() {
mController.onStop(mLifecycleOwner);
verify(mEventManager).unregisterCallback(any(BluetoothCallback.class));
verify(mDialogHandler).unregisterCallbacks();
verify(mAssistant)
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
@EnableFlags({Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE,
Flags.FLAG_ENABLE_LE_AUDIO_SHARING})
public void getAvailabilityStatus_flagOn() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE_UNSEARCHABLE);
}
@Test
@DisableFlags(Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE)
public void getAvailabilityStatus_flagOff() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void getPreferenceKey_returnsCorrectKey() {
assertThat(mController.getPreferenceKey()).isEqualTo(PREF_KEY);
}
@Test
public void getSliceHighlightMenuRes_returnsZero() {
assertThat(mController.getSliceHighlightMenuRes()).isEqualTo(0);
}
@Test
@EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE})
public void displayPreference_flagOn_updateDeviceList() {
mController.displayPreference(mScreen);
}
@Test
public void onProfileConnectionStateChanged_notDisconnectedProfile_doNothing() {
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
mController.onProfileConnectionStateChanged(
cachedDevice, BluetoothAdapter.STATE_CONNECTED,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
verifyNoInteractions(mDialogHandler);
}
@Test
public void onProfileConnectionStateChanged_leaDeviceDisconnected_closeOpeningDialogsForIt() {
// Test when LEA device LE_AUDIO_BROADCAST_ASSISTANT disconnected.
BluetoothDevice device = mock(BluetoothDevice.class);
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
LeAudioProfile profile = mock(LeAudioProfile.class);
when(profile.isEnabled(device)).thenReturn(true);
when(cachedDevice.getProfiles()).thenReturn(ImmutableList.of(profile));
when(cachedDevice.isConnected()).thenReturn(true);
when(cachedDevice.getDevice()).thenReturn(device);
mController.onProfileConnectionStateChanged(
cachedDevice,
BluetoothAdapter.STATE_DISCONNECTED,
BluetoothProfile.LE_AUDIO_BROADCAST_ASSISTANT);
verify(mDialogHandler).closeOpeningDialogsForLeaDevice(cachedDevice);
}
@Test
public void
onProfileConnectionStateChanged_classicDeviceDisconnected_closeOpeningDialogsForIt() {
// Test when classic device totally disconnected
BluetoothDevice device = mock(BluetoothDevice.class);
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
LeAudioProfile profile = mock(LeAudioProfile.class);
when(profile.isEnabled(device)).thenReturn(false);
when(cachedDevice.getProfiles()).thenReturn(ImmutableList.of(profile));
when(cachedDevice.isConnected()).thenReturn(false);
when(cachedDevice.getDevice()).thenReturn(device);
mController.onProfileConnectionStateChanged(
cachedDevice, BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.A2DP);
verify(mDialogHandler).closeOpeningDialogsForNonLeaDevice(cachedDevice);
}
@Test
@EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE})
public void handleDeviceConnectedFromIntent_noDevice_doNothing() {
Intent intent = new Intent();
doReturn(intent).when(mActivity).getIntent();
mController.displayPreference(mScreen);
shadowOf(Looper.getMainLooper()).idle();
verify(mDeviceManager, never()).findDevice(any(BluetoothDevice.class));
verify(mDialogHandler, never())
.handleDeviceConnected(any(CachedBluetoothDevice.class), anyBoolean());
}
@Test
@EnableFlags({Flags.FLAG_ENABLE_LE_AUDIO_SHARING,
Flags.FLAG_PROMOTE_AUDIO_SHARING_FOR_SECOND_AUTO_CONNECTED_LEA_DEVICE})
public void handleDeviceClickFromIntent_handle() {
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
BluetoothDevice device = mock(BluetoothDevice.class);
when(mDeviceManager.findDevice(device)).thenReturn(cachedDevice);
Intent intent = new Intent();
intent.putExtra(EXTRA_BLUETOOTH_DEVICE, device);
doReturn(intent).when(mActivity).getIntent();
mController.displayPreference(mScreen);
shadowOf(Looper.getMainLooper()).idle();
verify(mDialogHandler).handleDeviceConnected(cachedDevice, /* userTriggered = */ false);
}
@Test
public void testBluetoothLeBroadcastAssistantCallbacks_closeOpeningDialogsForSourceAdded() {
CachedBluetoothDevice cachedDevice = mock(CachedBluetoothDevice.class);
BluetoothDevice device = mock(BluetoothDevice.class);
when(mDeviceManager.findDevice(device)).thenReturn(cachedDevice);
// onSourceAdded will dismiss stale dialogs
mController.mAssistantCallback.onSourceAdded(device, /* sourceId= */
1, /* reason= */ 1);
verify(mDialogHandler).closeOpeningDialogsForLeaDevice(cachedDevice);
}
@Test
public void testBluetoothLeBroadcastAssistantCallbacks_doNothing() {
BluetoothDevice device = mock(BluetoothDevice.class);
mController.mAssistantCallback.onSearchStarted(/* reason= */ 1);
mController.mAssistantCallback.onSearchStartFailed(/* reason= */ 1);
mController.mAssistantCallback.onSearchStopped(/* reason= */ 1);
mController.mAssistantCallback.onSearchStopFailed(/* reason= */ 1);
BluetoothLeBroadcastReceiveState state = mock(BluetoothLeBroadcastReceiveState.class);
mController.mAssistantCallback.onReceiveStateChanged(device, /* sourceId= */ 1, state);
mController.mAssistantCallback.onSourceModified(device, /* sourceId= */ 1, /* reason= */ 1);
mController.mAssistantCallback.onSourceModifyFailed(device, /* sourceId= */ 1, /* reason= */
1);
BluetoothLeBroadcastMetadata metadata = mock(BluetoothLeBroadcastMetadata.class);
mController.mAssistantCallback.onSourceFound(metadata);
mController.mAssistantCallback.onSourceLost(/* broadcastId= */ 1);
shadowOf(Looper.getMainLooper()).idle();
// Above callbacks won't dismiss stale dialogs
verifyNoInteractions(mDialogHandler);
}
}

View File

@@ -0,0 +1,52 @@
/*
* Copyright (C) 2025 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.connecteddevice.audiosharing;
import static com.google.common.truth.Truth.assertThat;
import com.android.settings.R;
import org.junit.Before;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.robolectric.RobolectricTestRunner;
@RunWith(RobolectricTestRunner.class)
public class AudioSharingJoinHandlerDashboardFragmentTest {
private AudioSharingJoinHandlerDashboardFragment mFragment;
@Before
public void setUp() {
mFragment = new AudioSharingJoinHandlerDashboardFragment();
}
@Test
public void getPreferenceScreenResId_returnsCorrectXml() {
assertThat(mFragment.getPreferenceScreenResId())
.isEqualTo(R.xml.bluetooth_le_audio_sharing_join_handler);
}
@Test
public void getLogTag_returnsCorrectTag() {
assertThat(mFragment.getLogTag()).isEqualTo("AudioSharingJoinHandlerFrag");
}
@Test
public void getMetricsCategory_returnsCorrectCategory() {
assertThat(mFragment.getMetricsCategory()).isEqualTo(0);
}
}

View File

@@ -0,0 +1,86 @@
/*
* Copyright 2025 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.display.darkmode;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.platform.test.annotations.DisableFlags;
import android.platform.test.annotations.EnableFlags;
import android.platform.test.flag.junit.SetFlagsRule;
import android.view.accessibility.Flags;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settingslib.widget.TopIntroPreference;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
/**
* Tests for {@link DarkModeTopIntroPreferenceController}.
*/
@RunWith(RobolectricTestRunner.class)
public class DarkModeTopIntroPreferenceControllerTest {
@Rule
public final MockitoRule mocks = MockitoJUnit.rule();
@Rule
public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final String PREFERENCE_KEY = "preference_key";
@Mock
private PreferenceScreen mScreen;
@Mock
private TopIntroPreference mPreference;
private DarkModeTopIntroPreferenceController mController;
private final Context mContext = ApplicationProvider.getApplicationContext();
@Before
public void setUp() {
MockitoAnnotations.initMocks(this);
mController = new DarkModeTopIntroPreferenceController(mContext, PREFERENCE_KEY);
when(mScreen.findPreference(PREFERENCE_KEY)).thenReturn(mPreference);
}
@Test
@EnableFlags(Flags.FLAG_FORCE_INVERT_COLOR)
public void enableForceInvert_newPreferenceTitle() {
mController.displayPreference(mScreen);
verify(mPreference).setTitle(eq(R.string.dark_ui_text_force_invert));
}
@Test
@DisableFlags(Flags.FLAG_FORCE_INVERT_COLOR)
public void disableForceInvert_originalPreferenceTitle() {
mController.displayPreference(mScreen);
verify(mPreference).setTitle(eq(R.string.dark_ui_text));
}
}

View File

@@ -145,14 +145,6 @@ public class WifiConfigControllerTest {
.isEqualTo(View.GONE);
}
@Test
public void isSubmittable_noSSID_shouldReturnFalse() {
final TextView ssid = mView.findViewById(R.id.ssid);
assertThat(ssid).isNotNull();
ssid.setText("");
assertThat(mController.isSubmittable()).isFalse();
}
@Test
public void isSubmittable_longPsk_shouldReturnFalse() {
final TextView password = mView.findViewById(R.id.password);

View File

@@ -349,4 +349,14 @@ public class WifiDialogActivityTest {
verify(mActivity).dismissDialog();
}
@Test
public void onDismiss_shouldSubmitBeforeFinish_callOnSubmit() {
mActivity.mDialog = mWifiDialog;
when(mWifiDialog.shouldSubmitBeforeFinish()).thenReturn(true);
mActivity.onDismiss(mWifiDialog);
verify(mActivity).onSubmit(mWifiDialog);
}
}

View File

@@ -56,7 +56,6 @@ import com.android.wifitrackerlib.WifiEntry;
import com.android.wifitrackerlib.WifiEntry.ConnectedState;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -73,7 +72,6 @@ import org.robolectric.shadows.ShadowBinder;
import java.util.ArrayList;
import java.util.List;
@Ignore("b/394813533")
@Deprecated(forRemoval = true)
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {

View File

@@ -21,9 +21,6 @@ import static com.android.settings.connecteddevice.display.ExternalDisplayPrefer
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_NOT_FOUND_FOOTER_RESOURCE;
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_SETTINGS_RESOURCE;
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.EXTERNAL_DISPLAY_SIZE_SUMMARY_RESOURCE;
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.PREVIOUSLY_SHOWN_LIST_KEY;
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.displayListDisplayCategoryKey;
import static com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.resolutionRotationPreferenceKey;
import static com.android.settings.flags.Flags.FLAG_DISPLAY_SIZE_CONNECTED_DISPLAY_SETTING;
import static com.android.settings.flags.Flags.FLAG_DISPLAY_TOPOLOGY_PANE_IN_DISPLAY_LIST;
@@ -53,7 +50,6 @@ import androidx.preference.PreferenceScreen;
import androidx.test.annotation.UiThreadTest;
import androidx.test.ext.junit.runners.AndroidJUnit4;
import com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.DisplayPreference;
import com.android.settings.connecteddevice.display.ExternalDisplayPreferenceFragment.PrefBasics;
import com.android.settingslib.widget.MainSwitchPreference;
@@ -67,7 +63,6 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
@Nullable
private ExternalDisplayPreferenceFragment mFragment;
private int mPreferenceIdFromResource;
private int mDisplayIdArg = INVALID_DISPLAY;
private boolean mLaunchedBuiltinSettings;
private int mResolutionSelectorDisplayId = INVALID_DISPLAY;
@Mock
@@ -80,17 +75,20 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
assertThat(mPreferenceIdFromResource).isEqualTo(EXTERNAL_DISPLAY_SETTINGS_RESOURCE);
}
private void assertDisplayList(boolean present, int displayId) {
// In display list fragment, there is a combined resolution/rotation preference key.
var category = mPreferenceScreen.findPreference(displayListDisplayCategoryKey(displayId));
var pref = mPreferenceScreen.findPreference(resolutionRotationPreferenceKey(displayId));
if (present) {
assertThat(category).isNotNull();
assertThat(pref).isNotNull();
} else {
assertThat(category).isNull();
assertThat(pref).isNull();
private PreferenceCategory getExternalDisplayCategory(int positionIndex) {
return mPreferenceScreen.findPreference(
PrefBasics.EXTERNAL_DISPLAY_LIST.keyForNth(positionIndex));
}
private void assertDisplayListCount(int expectedCount) {
int actualCount = 0;
for (int i = 0; i < mPreferenceScreen.getPreferenceCount(); i++) {
Preference child = mPreferenceScreen.getPreference(i);
if (child.getKey().startsWith(PrefBasics.EXTERNAL_DISPLAY_LIST.key)) {
actualCount++;
}
}
assertThat(actualCount).isEqualTo(expectedCount);
}
@Test
@@ -100,27 +98,15 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
var fragment = initFragment();
var outState = new Bundle();
fragment.onSaveInstanceStateCallback(outState);
assertThat(outState.getBoolean(PREVIOUSLY_SHOWN_LIST_KEY)).isFalse();
assertThat(mHandler.getPendingMessages().size()).isEqualTo(1);
// Combined resolution/refresh rate are not available in displays list because the pane is
// disabled (v1 UI).
assertDisplayList(false, EXTERNAL_DISPLAY_ID);
assertDisplayList(false, OVERLAY_DISPLAY_ID);
// Individual resolution preference is not available in displays list.
assertThat(mPreferenceScreen.<Preference>findPreference(
PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key))
.isNull();
assertDisplayListCount(0);
verify(mMockedInjector, never()).getAllDisplays();
mHandler.flush();
assertThat(mHandler.getPendingMessages().size()).isEqualTo(0);
verify(mMockedInjector).getAllDisplays();
assertDisplayList(true, EXTERNAL_DISPLAY_ID);
assertDisplayList(true, OVERLAY_DISPLAY_ID);
fragment.onSaveInstanceStateCallback(outState);
assertThat(outState.getBoolean(PREVIOUSLY_SHOWN_LIST_KEY)).isTrue();
assertDisplayListCount(2);
Preference pref = mPreferenceScreen.findPreference(PrefBasics.DISPLAY_TOPOLOGY.key);
assertThat(pref).isNull();
@@ -143,7 +129,8 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
pref = mPreferenceScreen.findPreference(PrefBasics.MIRROR.key);
assertThat(pref).isNotNull();
assertDisplayList(false, mDisplays[1].getDisplayId());
assertDisplayListCount(1);
assertThat("" + getExternalDisplayCategory(0).getTitle()).isEqualTo("HDMI");
PreferenceCategory listPref =
mPreferenceScreen.findPreference(PrefBasics.BUILTIN_DISPLAY_LIST.key);
@@ -168,8 +155,7 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
pref = mPreferenceScreen.findPreference(PrefBasics.MIRROR.key);
assertThat(pref).isNull();
assertDisplayList(false, EXTERNAL_DISPLAY_ID);
assertDisplayList(false, OVERLAY_DISPLAY_ID);
assertDisplayListCount(0);
var listPref = mPreferenceScreen.findPreference(PrefBasics.BUILTIN_DISPLAY_LIST.key);
assertThat(listPref).isNull();
@@ -179,46 +165,36 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
@UiThreadTest
public void testLaunchDisplaySettingFromList() {
initFragment();
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
mHandler.flush();
assertDisplayList(true, EXTERNAL_DISPLAY_ID);
assertDisplayList(true, OVERLAY_DISPLAY_ID);
PreferenceCategory display1Category = mPreferenceScreen.findPreference(
displayListDisplayCategoryKey(EXTERNAL_DISPLAY_ID));
var display1Pref = (DisplayPreference) display1Category.getPreference(0);
PreferenceCategory display2Category = mPreferenceScreen.findPreference(
displayListDisplayCategoryKey(OVERLAY_DISPLAY_ID));
var display2Pref = (DisplayPreference) display2Category.getPreference(0);
assertThat(display1Pref.getKey()).isEqualTo(
resolutionRotationPreferenceKey(EXTERNAL_DISPLAY_ID));
assertDisplayListCount(2);
var display1Category = getExternalDisplayCategory(0);
var display2Category = getExternalDisplayCategory(1);
assertThat("" + display1Category.getTitle()).isEqualTo("HDMI");
assertThat("" + display1Pref.getSummary()).isEqualTo("1920 x 1080");
display1Pref.onPreferenceClick(display1Pref);
assertThat(mDisplayIdArg).isEqualTo(1);
verify(mMockedMetricsLogger).writePreferenceClickMetric(display1Pref);
assertThat(display2Pref.getKey()).isEqualTo(
resolutionRotationPreferenceKey(OVERLAY_DISPLAY_ID));
var display1Resolution = display1Category.findPreference(
PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(0));
display1Resolution.performClick();
assertThat(mResolutionSelectorDisplayId).isEqualTo(1);
verify(mMockedMetricsLogger).writePreferenceClickMetric(display1Resolution);
var display2Resolution = display2Category.findPreference(
PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(1));
assertThat("" + display2Category.getTitle()).isEqualTo("Overlay #1");
assertThat("" + display2Pref.getSummary()).isEqualTo("1240 x 780");
display2Pref.onPreferenceClick(display2Pref);
assertThat(mDisplayIdArg).isEqualTo(2);
verify(mMockedMetricsLogger).writePreferenceClickMetric(display2Pref);
assertThat("" + display2Resolution.getSummary()).isEqualTo("1240 x 780");
display2Resolution.performClick();
assertThat(mResolutionSelectorDisplayId).isEqualTo(2);
verify(mMockedMetricsLogger).writePreferenceClickMetric(display2Resolution);
}
@Test
@UiThreadTest
public void testShowDisplayListForOnlyOneDisplay_PreviouslyShownList() {
var fragment = initFragment();
// Previously shown list of displays
fragment.onActivityCreatedCallback(createBundleForPreviouslyShownList());
// Only one display available
doReturn(new Display[] {mDisplays[1]}).when(mMockedInjector).getAllDisplays();
mHandler.flush();
int attachedId = mDisplays[1].getDisplayId();
assertDisplayList(true, attachedId);
assertThat(mPreferenceScreen.<Preference>findPreference(
resolutionRotationPreferenceKey(attachedId)))
.isNotNull();
assertDisplayList(false, mDisplays[2].getDisplayId());
assertDisplayListCount(1);
assertThat("" + getExternalDisplayCategory(0).getTitle()).isEqualTo("HDMI");
}
@Test
@@ -231,14 +207,15 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
// Init
initFragment();
mHandler.flush();
assertDisplayList(false, mDisplays[1].getDisplayId());
var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key);
assertDisplayListCount(1);
var category = getExternalDisplayCategory(0);
var pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(0));
assertThat(pref).isNotNull();
pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key);
pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.keyForNth(0));
assertThat(pref).isNotNull();
var footerPref = mPreferenceScreen.findPreference(PrefBasics.FOOTER.key);
var footerPref = category.findPreference(PrefBasics.FOOTER.key);
assertThat(footerPref).isNotNull();
var sizePref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key);
var sizePref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(0));
assertThat(sizePref).isNull();
assertThat("" + footerPref.getTitle())
.isEqualTo(getText(EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE));
@@ -253,15 +230,12 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
// Init
initFragment();
mHandler.flush();
assertDisplayList(false, mDisplays[1].getDisplayId());
assertDisplayList(false, mDisplays[2].getDisplayId());
var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key);
assertThat(pref).isNotNull();
pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key);
assertThat(pref).isNotNull();
var footerPref = mPreferenceScreen.findPreference(PrefBasics.FOOTER.key);
assertDisplayListCount(1);
var category = getExternalDisplayCategory(0);
assertThat("" + category.getTitle()).isEqualTo("HDMI");
var footerPref = category.findPreference(PrefBasics.FOOTER.key);
assertThat(footerPref).isNotNull();
var sizePref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key);
var sizePref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(0));
assertThat(sizePref).isNotNull();
assertThat("" + footerPref.getTitle())
.isEqualTo(getText(EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE));
@@ -270,47 +244,48 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
@Test
@UiThreadTest
public void testShowOneEnabledDisplay_FewAvailable() {
mDisplayIdArg = 1;
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
initFragment();
verify(mMockedInjector, never()).getDisplay(anyInt());
verify(mMockedInjector, never()).getAllDisplays();
mHandler.flush();
verify(mMockedInjector).getDisplay(mDisplayIdArg);
var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key);
verify(mMockedInjector, never()).getDisplay(anyInt());
verify(mMockedInjector).getAllDisplays();
var pref = mPreferenceScreen.findPreference(
PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(0));
assertThat(pref).isNotNull();
pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key);
pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.keyForNth(0));
assertThat(pref).isNotNull();
var footerPref = mPreferenceScreen.findPreference(PrefBasics.FOOTER.key);
assertThat(footerPref).isNotNull();
var sizePref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key);
// No footer for showing multiple displays.
assertThat(footerPref).isNull();
var sizePref = mPreferenceScreen.findPreference(
PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(0));
assertThat(sizePref).isNotNull();
assertThat("" + footerPref.getTitle())
.isEqualTo(getText(EXTERNAL_DISPLAY_CHANGE_RESOLUTION_FOOTER_RESOURCE));
}
@Test
@UiThreadTest
public void testShowDisabledDisplay() {
mDisplayIdArg = 1;
initFragment();
verify(mMockedInjector, never()).getDisplay(anyInt());
mHandler.flush();
verify(mMockedInjector).getDisplay(mDisplayIdArg);
var mainPref = (MainSwitchPreference) mPreferenceScreen.findPreference(
PrefBasics.EXTERNAL_DISPLAY_USE.key);
verify(mMockedInjector, never()).getDisplay(anyInt());
verify(mMockedInjector).getAllDisplays();
var category = getExternalDisplayCategory(0);
var mainPref = (MainSwitchPreference) category.findPreference(
PrefBasics.EXTERNAL_DISPLAY_USE.keyForNth(0));
assertThat(mainPref).isNotNull();
assertThat("" + mainPref.getTitle()).isEqualTo(
getText(PrefBasics.EXTERNAL_DISPLAY_USE.titleResource));
assertThat(mainPref.isChecked()).isFalse();
assertThat(mainPref.isEnabled()).isTrue();
assertThat(mainPref.getOnPreferenceChangeListener()).isNotNull();
var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key);
var pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(0));
assertThat(pref).isNull();
pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key);
pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.keyForNth(0));
assertThat(pref).isNull();
var footerPref = mPreferenceScreen.findPreference(PrefBasics.FOOTER.key);
var footerPref = category.findPreference(PrefBasics.FOOTER.key);
assertThat(footerPref).isNull();
var sizePref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key);
var sizePref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(0));
assertThat(sizePref).isNull();
}
@@ -321,7 +296,7 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
initFragment();
mHandler.flush();
var mainPref = (MainSwitchPreference) mPreferenceScreen.findPreference(
PrefBasics.EXTERNAL_DISPLAY_USE.key);
PrefBasics.EXTERNAL_DISPLAY_USE.keyForNth(0));
assertThat(mainPref).isNotNull();
assertThat("" + mainPref.getTitle()).isEqualTo(
getText(PrefBasics.EXTERNAL_DISPLAY_USE.titleResource));
@@ -337,13 +312,13 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
@Test
@UiThreadTest
public void testDisplayRotationPreference() {
mDisplayIdArg = 1;
final int displayId = 1;
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
var fragment = initFragment();
mHandler.flush();
ListPreference pref =
mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key);
assertThat(pref.getKey()).isEqualTo(PrefBasics.EXTERNAL_DISPLAY_ROTATION.key);
var category = getExternalDisplayCategory(0);
ListPreference pref = category.findPreference(
PrefBasics.EXTERNAL_DISPLAY_ROTATION.keyForNth(0));
assertThat("" + pref.getTitle()).isEqualTo(
getText(PrefBasics.EXTERNAL_DISPLAY_ROTATION.titleResource));
assertThat(pref.getEntries().length).isEqualTo(4);
@@ -359,10 +334,10 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
assertThat(pref.getOnPreferenceChangeListener()).isNotNull();
assertThat(pref.isEnabled()).isTrue();
var rotation = 1;
doReturn(true).when(mMockedInjector).freezeDisplayRotation(mDisplayIdArg, rotation);
doReturn(true).when(mMockedInjector).freezeDisplayRotation(displayId, rotation);
assertThat(pref.getOnPreferenceChangeListener().onPreferenceChange(pref, rotation + ""))
.isTrue();
verify(mMockedInjector).freezeDisplayRotation(mDisplayIdArg, rotation);
verify(mMockedInjector).freezeDisplayRotation(displayId, rotation);
assertThat(pref.getValue()).isEqualTo(rotation + "");
verify(mMockedMetricsLogger).writePreferenceClickMetric(pref);
}
@@ -370,31 +345,30 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
@Test
@UiThreadTest
public void testDisplayResolutionPreference() {
mDisplayIdArg = 1;
final int displayId = 1;
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
var fragment = initFragment();
mHandler.flush();
var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key);
assertThat(pref.getKey()).isEqualTo(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.key);
var category = getExternalDisplayCategory(0);
var pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.keyForNth(0));
assertThat("" + pref.getTitle()).isEqualTo(
getText(PrefBasics.EXTERNAL_DISPLAY_RESOLUTION.titleResource));
assertThat("" + pref.getSummary()).isEqualTo("1920 x 1080");
assertThat(pref.isEnabled()).isTrue();
assertThat(pref.getOnPreferenceClickListener()).isNotNull();
assertThat(pref.getOnPreferenceClickListener().onPreferenceClick(pref)).isTrue();
assertThat(mResolutionSelectorDisplayId).isEqualTo(mDisplayIdArg);
assertThat(mResolutionSelectorDisplayId).isEqualTo(displayId);
verify(mMockedMetricsLogger).writePreferenceClickMetric(pref);
}
@Test
@UiThreadTest
public void testDisplaySizePreference() {
mDisplayIdArg = 1;
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
var fragment = initFragment();
mHandler.flush();
var pref = mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.key);
assertThat(pref.getKey()).isEqualTo(PrefBasics.EXTERNAL_DISPLAY_SIZE.key);
var category = getExternalDisplayCategory(0);
var pref = category.findPreference(PrefBasics.EXTERNAL_DISPLAY_SIZE.keyForNth(0));
assertThat("" + pref.getTitle())
.isEqualTo(getText(PrefBasics.EXTERNAL_DISPLAY_SIZE.titleResource));
assertThat("" + pref.getSummary())
@@ -408,25 +382,25 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
@Test
@UiThreadTest
public void testUseDisplayPreference_EnabledDisplay() {
mDisplayIdArg = 1;
final int displayId = 1;
doReturn(true).when(mMockedInjector).isDisplayEnabled(any());
doReturn(true).when(mMockedInjector).enableConnectedDisplay(mDisplayIdArg);
doReturn(true).when(mMockedInjector).disableConnectedDisplay(mDisplayIdArg);
doReturn(true).when(mMockedInjector).enableConnectedDisplay(displayId);
doReturn(true).when(mMockedInjector).disableConnectedDisplay(displayId);
var fragment = initFragment();
mHandler.flush();
MainSwitchPreference pref =
mPreferenceScreen.findPreference(PrefBasics.EXTERNAL_DISPLAY_USE.key);
assertThat(pref.getKey()).isEqualTo(PrefBasics.EXTERNAL_DISPLAY_USE.key);
MainSwitchPreference pref = getExternalDisplayCategory(0)
.findPreference(PrefBasics.EXTERNAL_DISPLAY_USE.keyForNth(0));
assertThat(pref.getKey()).isEqualTo(PrefBasics.EXTERNAL_DISPLAY_USE.keyForNth(0));
assertThat("" + pref.getTitle())
.isEqualTo(getText(PrefBasics.EXTERNAL_DISPLAY_USE.titleResource));
assertThat(pref.isEnabled()).isTrue();
assertThat(pref.isChecked()).isTrue();
assertThat(pref.getOnPreferenceChangeListener()).isNotNull();
assertThat(pref.getOnPreferenceChangeListener().onPreferenceChange(pref, false)).isTrue();
verify(mMockedInjector).disableConnectedDisplay(mDisplayIdArg);
verify(mMockedInjector).disableConnectedDisplay(displayId);
assertThat(pref.isChecked()).isFalse();
assertThat(pref.getOnPreferenceChangeListener().onPreferenceChange(pref, true)).isTrue();
verify(mMockedInjector).enableConnectedDisplay(mDisplayIdArg);
verify(mMockedInjector).enableConnectedDisplay(displayId);
assertThat(pref.isChecked()).isTrue();
verify(mMockedMetricsLogger, times(2)).writePreferenceClickMetric(pref);
}
@@ -443,13 +417,6 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
return mFragment;
}
@NonNull
private Bundle createBundleForPreviouslyShownList() {
var state = new Bundle();
state.putBoolean(PREVIOUSLY_SHOWN_LIST_KEY, true);
return state;
}
@NonNull
private String getText(int id) {
return mContext.getResources().getText(id).toString();
@@ -501,21 +468,11 @@ public class ExternalDisplayPreferenceFragmentTest extends ExternalDisplayTestBa
mPreferenceIdFromResource = resource;
}
@Override
protected int getDisplayIdArg() {
return mDisplayIdArg;
}
@Override
protected void launchResolutionSelector(@NonNull Context context, int displayId) {
mResolutionSelectorDisplayId = displayId;
}
@Override
protected void launchExternalDisplaySettings(final int displayId) {
mDisplayIdArg = displayId;
}
@Override
Preference newFooterPreference(Context context) {
return new Preference(context);

View File

@@ -63,6 +63,7 @@ public class ExternalDisplayUpdaterTest extends ExternalDisplayTestBase {
return null;
}).when(mMockedCallback).onDeviceAdded(any());
mUpdater.initPreference(mContext, mMockedInjector);
mUpdater.refreshPreference();
mUpdater.registerCallback();
mHandler.flush();
assertThat(mPreferenceAdded).isNotNull();
@@ -85,6 +86,7 @@ public class ExternalDisplayUpdaterTest extends ExternalDisplayTestBase {
return null;
}).when(mMockedCallback).onDeviceRemoved(any());
mUpdater.initPreference(mContext, mMockedInjector);
mUpdater.refreshPreference();
mUpdater.registerCallback();
mHandler.flush();
assertThat(mPreferenceAdded).isNotNull();