diff --git a/AndroidManifest.xml b/AndroidManifest.xml
index 2295ee3dd1a..551a7dedb69 100644
--- a/AndroidManifest.xml
+++ b/AndroidManifest.xml
@@ -5439,6 +5439,16 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 883147a1783..8faf62a123f 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -4580,17 +4580,17 @@
Bounce keys
- The keyboard ignores quickly repeated presses of the same key
+ Ignore quickly repeated presses of the same key
- Bounce key threshold
+ Bounce key delay
- Choose the duration of time your keyboard ignores repeated key presses
+ Choose how long the keyboard ignores repeated keystrokes
- 0.2s
+ 0.2 seconds
- 0.4s
+ 0.4 seconds
- 0.6s
+ 0.6 seconds
Custom
@@ -4599,7 +4599,7 @@
Slow keys
- Adjusts the time it takes for a key press to activate
+ Change how long you need to hold down a key before it\'s registered
Sticky keys
@@ -4650,6 +4650,8 @@
Touchpad & mouse
Mouse
+
+ Pointer speed, swap buttons, button customisation
Pointer speed, gestures
@@ -5395,6 +5397,10 @@
High contrast text
Change text color to black or white. Maximizes contrast with the background.
+
+ High contrast text has a new look and feel.
+
+ Open Settings
Maximize text contrast
@@ -8806,13 +8812,13 @@
Full list
-
+
The current default placement is a full shelf and notification stack.
Compact
-
+
New notifications are collapsed into a shelf on your lockscreen.
@@ -8877,16 +8883,16 @@
When your device is locked, how do you want profile notifications to show?
-
+
Hide seen notifications
-
+
Seen notifications are removed from the lock screen.
-
+
Hide silent notifications
-
+
Silent notifications and conversations are removed from the lock screen.
diff --git a/res/xml/accessibility_text_reading_options.xml b/res/xml/accessibility_text_reading_options.xml
index 795c4ffb9fc..8eed107d60a 100644
--- a/res/xml/accessibility_text_reading_options.xml
+++ b/res/xml/accessibility_text_reading_options.xml
@@ -25,7 +25,7 @@
android:key="preview"
android:selectable="false"/>
-
-
+
+
+
+
mSizeData;
- private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
private boolean mSeekByTouch;
private Optional mInteractionListener = Optional.empty();
- private LabeledSeekBarPreference mSeekBarPreference;
+ private AccessibilitySeekBarPreference mSeekBarPreference;
private int mLastProgress;
- private boolean mNeedsQSTooltipReshow = false;
- private AccessibilityQuickSettingsTooltipWindow mTooltipWindow;
private final Handler mHandler;
private String[] mStateLabels = null;
@@ -101,30 +96,21 @@ abstract class PreviewSizeSeekBarController extends BasePreferenceController imp
}
@Override
- public void onCreate(Bundle savedInstanceState) {
- // Restore the tooltip.
- if (savedInstanceState != null
- && savedInstanceState.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
- mNeedsQSTooltipReshow = savedInstanceState.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW);
+ public void onStart() {
+ if (mSeekBarPreference.getNeedsQSTooltipReshow()) {
+ mHandler.post(this::showQuickSettingsTooltipIfNeeded);
}
}
+ @Override
+ public void onStop() {
+ // all the messages/callbacks will be removed.
+ mHandler.removeCallbacksAndMessages(null);
+ }
+
@Override
public void onDestroy() {
- // remove runnables in the queue.
- mHandler.removeCallbacksAndMessages(null);
- final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
- if (isTooltipWindowShowing) {
- mTooltipWindow.dismiss();
- }
- }
-
- @Override
- public void onSaveInstanceState(Bundle outState) {
- final boolean isTooltipWindowShowing = mTooltipWindow != null && mTooltipWindow.isShowing();
- if (mNeedsQSTooltipReshow || isTooltipWindowShowing) {
- outState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
- }
+ mSeekBarPreference.dismissTooltip();
}
void setInteractionListener(ProgressInteractionListener interactionListener) {
@@ -148,9 +134,6 @@ abstract class PreviewSizeSeekBarController extends BasePreferenceController imp
mSeekBarPreference.setProgress(initialIndex);
mSeekBarPreference.setContinuousUpdates(true);
mSeekBarPreference.setOnSeekBarChangeListener(mSeekBarChangeListener);
- if (mNeedsQSTooltipReshow) {
- mHandler.post(this::showQuickSettingsTooltipIfNeeded);
- }
setSeekbarStateDescription(mSeekBarPreference.getProgress());
}
@@ -216,7 +199,8 @@ abstract class PreviewSizeSeekBarController extends BasePreferenceController imp
return;
}
- if (!mNeedsQSTooltipReshow && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
+ if (!mSeekBarPreference.getNeedsQSTooltipReshow()
+ && AccessibilityQuickSettingUtils.hasValueInSharedPreferences(
mContext, tileComponentName)) {
// Returns if quick settings tooltip only show once.
return;
@@ -228,14 +212,15 @@ abstract class PreviewSizeSeekBarController extends BasePreferenceController imp
// is not ready when we would like to show the tooltip. If the seekbar is not ready,
// we give up showing the tooltip and also do not reshow it in the future.
if (mSeekBarPreference.getSeekbar() != null) {
- mTooltipWindow = new AccessibilityQuickSettingsTooltipWindow(mContext);
- mTooltipWindow.setup(getTileTooltipContent(),
+ final AccessibilityQuickSettingsTooltipWindow tooltipWindow =
+ mSeekBarPreference.createTooltipWindow();
+ tooltipWindow.setup(getTileTooltipContent(),
R.drawable.accessibility_auto_added_qs_tooltip_illustration);
- mTooltipWindow.showAtTopCenter(mSeekBarPreference.getSeekbar());
+ tooltipWindow.showAtTopCenter(mSeekBarPreference.getSeekbar());
}
AccessibilityQuickSettingUtils.optInValueToSharedPreferences(mContext,
tileComponentName);
- mNeedsQSTooltipReshow = false;
+ mSeekBarPreference.setNeedsQSTooltipReshow(false);
}
/** Returns the accessibility Quick Settings tile component name. */
diff --git a/src/com/android/settings/accessibility/TextReadingPreviewController.java b/src/com/android/settings/accessibility/TextReadingPreviewController.java
index a983105cfad..99f1f3fa0c3 100644
--- a/src/com/android/settings/accessibility/TextReadingPreviewController.java
+++ b/src/com/android/settings/accessibility/TextReadingPreviewController.java
@@ -33,7 +33,6 @@ import com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoi
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.instrumentation.SettingsStatsLog;
import com.android.settings.display.PreviewPagerAdapter;
-import com.android.settings.widget.LabeledSeekBarPreference;
import java.util.Objects;
@@ -58,8 +57,8 @@ class TextReadingPreviewController extends BasePreferenceController implements
private int mLastDisplayProgress;
private long mLastCommitTime;
private TextReadingPreviewPreference mPreviewPreference;
- private LabeledSeekBarPreference mFontSizePreference;
- private LabeledSeekBarPreference mDisplaySizePreference;
+ private AccessibilitySeekBarPreference mFontSizePreference;
+ private AccessibilitySeekBarPreference mDisplaySizePreference;
@EntryPoint
private int mEntryPoint;
diff --git a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
index 14f55b81264..bd160e17527 100644
--- a/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdater.java
@@ -39,6 +39,7 @@ public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater
private final AudioManager mAudioManager;
private final LocalBluetoothManager mLocalBtManager;
+ private int mAudioMode;
public AvailableMediaBluetoothDeviceUpdater(
Context context,
@@ -47,21 +48,23 @@ public class AvailableMediaBluetoothDeviceUpdater extends BluetoothDeviceUpdater
super(context, devicePreferenceCallback, metricsCategory);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
mLocalBtManager = Utils.getLocalBtManager(context);
+ mAudioMode = mAudioManager.getMode();
}
@Override
public void onAudioModeChanged() {
+ // TODO: move to background thread
+ mAudioMode = mAudioManager.getMode();
forceUpdate();
}
@Override
public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
- final int audioMode = mAudioManager.getMode();
final int currentAudioProfile;
- if (audioMode == AudioManager.MODE_RINGTONE
- || audioMode == AudioManager.MODE_IN_CALL
- || audioMode == AudioManager.MODE_IN_COMMUNICATION) {
+ if (mAudioMode == AudioManager.MODE_RINGTONE
+ || mAudioMode == AudioManager.MODE_IN_CALL
+ || mAudioMode == AudioManager.MODE_IN_COMMUNICATION) {
// in phone call
currentAudioProfile = BluetoothProfile.HEADSET;
} else {
diff --git a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
index 2107569d86b..7cc874caba9 100644
--- a/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
+++ b/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdater.java
@@ -39,26 +39,29 @@ public class ConnectedBluetoothDeviceUpdater extends BluetoothDeviceUpdater {
private static final String PREF_KEY_PREFIX = "connected_bt_";
private final AudioManager mAudioManager;
+ private int mAudioMode;
public ConnectedBluetoothDeviceUpdater(Context context,
DevicePreferenceCallback devicePreferenceCallback, int metricsCategory) {
super(context, devicePreferenceCallback, metricsCategory);
mAudioManager = (AudioManager) context.getSystemService(Context.AUDIO_SERVICE);
+ mAudioMode = mAudioManager.getMode();
}
@Override
public void onAudioModeChanged() {
+ // TODO: move to background thread
+ mAudioMode = mAudioManager.getMode();
forceUpdate();
}
@Override
public boolean isFilterMatched(CachedBluetoothDevice cachedDevice) {
- final int audioMode = mAudioManager.getMode();
final int currentAudioProfile;
- if (audioMode == AudioManager.MODE_RINGTONE
- || audioMode == AudioManager.MODE_IN_CALL
- || audioMode == AudioManager.MODE_IN_COMMUNICATION) {
+ if (mAudioMode == AudioManager.MODE_RINGTONE
+ || mAudioMode == AudioManager.MODE_IN_CALL
+ || mAudioMode == AudioManager.MODE_IN_COMMUNICATION) {
// in phone call
currentAudioProfile = BluetoothProfile.HEADSET;
} else {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
index 25a9135701b..c86222ca362 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsHelper.java
@@ -21,6 +21,8 @@ import static com.android.settings.connecteddevice.audiosharing.audiostreams.Aud
import static com.android.settings.connecteddevice.audiosharing.audiostreams.AudioStreamMediaService.DEVICES;
import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
+import static java.util.stream.Collectors.toMap;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeAudioContentMetadata;
@@ -48,7 +50,9 @@ import com.google.common.base.Strings;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
+import java.util.function.Function;
import java.util.stream.Stream;
import javax.annotation.Nullable;
@@ -149,6 +153,16 @@ public class AudioStreamsHelper {
.toList();
}
+ /** Retrieves a list of all LE broadcast receive states keyed by each active device. */
+ public Map> getAllSourcesByDevice() {
+ if (mLeBroadcastAssistant == null) {
+ Log.w(TAG, "getAllSourcesByDevice(): LeBroadcastAssistant is null!");
+ return emptyMap();
+ }
+ return getConnectedBluetoothDevices(mBluetoothManager, /* inSharingOnly= */ true).stream()
+ .collect(toMap(Function.identity(), mLeBroadcastAssistant::getAllSources));
+ }
+
/** Retrieves a list of all LE broadcast receive states from sinks with source present. */
@VisibleForTesting
public List getAllPresentSources() {
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java
index f0034316372..87cea2c1e94 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallback.java
@@ -16,7 +16,6 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
-
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
@@ -43,13 +42,13 @@ public class AudioStreamsProgressCategoryCallback extends AudioStreamsBroadcastA
super.onReceiveStateChanged(sink, sourceId, state);
if (AudioStreamsHelper.isConnected(state)) {
- mCategoryController.handleSourceConnected(state);
+ mCategoryController.handleSourceConnected(sink, state);
} else if (AudioStreamsHelper.isBadCode(state)) {
mCategoryController.handleSourceConnectBadCode(state);
} else if (BluetoothUtils.isAudioSharingHysteresisModeFixAvailable(mContext)
&& AudioStreamsHelper.hasSourcePresent(state)) {
// Keep this check as the last, source might also present in above states
- mCategoryController.handleSourcePresent(state);
+ mCategoryController.handleSourcePresent(sink, state);
}
}
diff --git a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
index f0a0c5b8f7f..6831c5a12e3 100644
--- a/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
+++ b/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryController.java
@@ -17,10 +17,12 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams;
import static java.util.Collections.emptyList;
+import static java.util.stream.Collectors.toMap;
import android.app.AlertDialog;
import android.app.settings.SettingsEnums;
import android.bluetooth.BluetoothAdapter;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
@@ -49,6 +51,8 @@ import com.android.settingslib.utils.ThreadUtils;
import java.util.Comparator;
import java.util.List;
+import java.util.Map;
+import java.util.Optional;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
@@ -391,34 +395,19 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
// Expect one of the following:
// 1) No preference existed, create new preference with state SOURCE_ADDED
// 2) Any other state, move to SOURCE_ADDED
- void handleSourceConnected(BluetoothLeBroadcastReceiveState receiveState) {
+ void handleSourceConnected(
+ BluetoothDevice device, BluetoothLeBroadcastReceiveState receiveState) {
if (DEBUG) {
Log.d(TAG, "handleSourceConnected()");
}
if (!AudioStreamsHelper.isConnected(receiveState)) {
return;
}
-
var broadcastIdConnected = receiveState.getBroadcastId();
- if (mSourceFromQrCode != null && mSourceFromQrCode.getBroadcastId() == UNSET_BROADCAST_ID) {
- // mSourceFromQrCode could have no broadcast Id, we fill in the broadcast Id from the
- // connected source receiveState.
- if (DEBUG) {
- Log.d(
- TAG,
- "handleSourceConnected() : processing mSourceFromQrCode with broadcastId"
- + " unset");
- }
- boolean updated =
- maybeUpdateId(
- AudioStreamsHelper.getBroadcastName(receiveState),
- receiveState.getBroadcastId());
- if (updated && mBroadcastIdToPreferenceMap.containsKey(UNSET_BROADCAST_ID)) {
- var preference = mBroadcastIdToPreferenceMap.remove(UNSET_BROADCAST_ID);
- mBroadcastIdToPreferenceMap.put(receiveState.getBroadcastId(), preference);
- }
- }
-
+ Optional metadata =
+ getMetadataMatchingByBroadcastId(
+ device, receiveState.getSourceId(), broadcastIdConnected);
+ handleQrCodeWithUnsetBroadcastIdIfNeeded(metadata, receiveState);
mBroadcastIdToPreferenceMap.compute(
broadcastIdConnected,
(k, existingPreference) -> {
@@ -428,7 +417,12 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
// we retrieves the connected source during onStart() from
// AudioStreamsHelper#getAllConnectedSources() even before the source is
// founded by scanning.
- return addNewPreference(receiveState, AudioStreamState.SOURCE_ADDED);
+ return metadata.isPresent()
+ ? addNewPreference(
+ metadata.get(),
+ AudioStreamState.SOURCE_ADDED,
+ SourceOriginForLogging.UNKNOWN)
+ : addNewPreference(receiveState, AudioStreamState.SOURCE_ADDED);
}
if (existingPreference.getAudioStreamState() == AudioStreamState.WAIT_FOR_SYNC
&& existingPreference.getAudioStreamBroadcastId() == UNSET_BROADCAST_ID
@@ -473,7 +467,8 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
// Find preference by receiveState and decide next state.
// Expect one preference existed, move to SOURCE_PRESENT
- void handleSourcePresent(BluetoothLeBroadcastReceiveState receiveState) {
+ void handleSourcePresent(
+ BluetoothDevice device, BluetoothLeBroadcastReceiveState receiveState) {
if (DEBUG) {
Log.d(TAG, "handleSourcePresent()");
}
@@ -482,25 +477,10 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
}
var broadcastIdConnected = receiveState.getBroadcastId();
- if (mSourceFromQrCode != null && mSourceFromQrCode.getBroadcastId() == UNSET_BROADCAST_ID) {
- // mSourceFromQrCode could have no broadcast Id, we fill in the broadcast Id from the
- // connected source receiveState.
- if (DEBUG) {
- Log.d(
- TAG,
- "handleSourcePresent() : processing mSourceFromQrCode with broadcastId"
- + " unset");
- }
- boolean updated =
- maybeUpdateId(
- AudioStreamsHelper.getBroadcastName(receiveState),
- receiveState.getBroadcastId());
- if (updated && mBroadcastIdToPreferenceMap.containsKey(UNSET_BROADCAST_ID)) {
- var preference = mBroadcastIdToPreferenceMap.remove(UNSET_BROADCAST_ID);
- mBroadcastIdToPreferenceMap.put(receiveState.getBroadcastId(), preference);
- }
- }
-
+ Optional metadata =
+ getMetadataMatchingByBroadcastId(
+ device, receiveState.getSourceId(), broadcastIdConnected);
+ handleQrCodeWithUnsetBroadcastIdIfNeeded(metadata, receiveState);
mBroadcastIdToPreferenceMap.compute(
broadcastIdConnected,
(k, existingPreference) -> {
@@ -511,7 +491,12 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
// we retrieves the connected source during onStart() from
// AudioStreamsHelper#getAllPresentSources() even before the source is
// founded by scanning.
- return addNewPreference(receiveState, AudioStreamState.SOURCE_PRESENT);
+ return metadata.isPresent()
+ ? addNewPreference(
+ metadata.get(),
+ AudioStreamState.SOURCE_PRESENT,
+ SourceOriginForLogging.UNKNOWN)
+ : addNewPreference(receiveState, AudioStreamState.SOURCE_PRESENT);
}
if (existingPreference.getAudioStreamState() == AudioStreamState.WAIT_FOR_SYNC
&& existingPreference.getAudioStreamBroadcastId() == UNSET_BROADCAST_ID
@@ -598,28 +583,85 @@ public class AudioStreamsProgressCategoryController extends BasePreferenceContro
// Handle QR code scan, display currently connected streams then start scanning
// sequentially
handleSourceFromQrCodeIfExists();
+ Map> sources =
+ mAudioStreamsHelper.getAllSourcesByDevice();
+ Map> connectedSources =
+ getConnectedSources(sources);
if (isAudioSharingHysteresisModeFixAvailable(mContext)) {
// With hysteresis mode, we prioritize showing connected sources first.
// If no connected sources are found, we then show present sources.
- List sources =
- mAudioStreamsHelper.getAllConnectedSources();
- if (!sources.isEmpty()) {
- sources.forEach(this::handleSourceConnected);
+ if (!connectedSources.isEmpty()) {
+ connectedSources.forEach(
+ (device, stateList) ->
+ stateList.forEach(
+ state -> handleSourceConnected(device, state)));
} else {
- mAudioStreamsHelper
- .getAllPresentSources()
- .forEach(this::handleSourcePresent);
+ Map>
+ presentSources = getPresentSources(sources);
+ presentSources.forEach(
+ (device, stateList) ->
+ stateList.forEach(
+ state -> handleSourcePresent(device, state)));
}
} else {
- mAudioStreamsHelper
- .getAllConnectedSources()
- .forEach(this::handleSourceConnected);
+ connectedSources.forEach(
+ (device, stateList) ->
+ stateList.forEach(
+ state -> handleSourceConnected(device, state)));
}
mLeBroadcastAssistant.startSearchingForSources(emptyList());
mMediaControlHelper.start();
});
}
+ private Map> getConnectedSources(
+ Map> sources) {
+ return sources.entrySet().stream()
+ .filter(
+ entry ->
+ entry.getValue().stream().anyMatch(AudioStreamsHelper::isConnected))
+ .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ private Map> getPresentSources(
+ Map> sources) {
+ return sources.entrySet().stream()
+ .filter(
+ entry ->
+ entry.getValue().stream()
+ .anyMatch(AudioStreamsHelper::hasSourcePresent))
+ .collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
+ }
+
+ private Optional getMetadataMatchingByBroadcastId(
+ BluetoothDevice device, int sourceId, int broadcastId) {
+ return Optional.ofNullable(
+ mLeBroadcastAssistant != null
+ ? mLeBroadcastAssistant.getSourceMetadata(device, sourceId)
+ : null)
+ .filter(m -> m.getBroadcastId() == broadcastId);
+ }
+
+ private void handleQrCodeWithUnsetBroadcastIdIfNeeded(
+ Optional metadata,
+ BluetoothLeBroadcastReceiveState receiveState) {
+ if (mSourceFromQrCode != null && mSourceFromQrCode.getBroadcastId() == UNSET_BROADCAST_ID) {
+ if (DEBUG) {
+ Log.d(TAG, "Processing mSourceFromQrCode with unset broadcastId");
+ }
+ boolean updated =
+ maybeUpdateId(
+ metadata.isPresent()
+ ? AudioStreamsHelper.getBroadcastName(metadata.get())
+ : AudioStreamsHelper.getBroadcastName(receiveState),
+ receiveState.getBroadcastId());
+ if (updated && mBroadcastIdToPreferenceMap.containsKey(UNSET_BROADCAST_ID)) {
+ var preference = mBroadcastIdToPreferenceMap.remove(UNSET_BROADCAST_ID);
+ mBroadcastIdToPreferenceMap.put(receiveState.getBroadcastId(), preference);
+ }
+ }
+ }
+
private void stopScanning() {
if (mLeBroadcastAssistant == null) {
Log.w(TAG, "stopScanning(): LeBroadcastAssistant is null!");
diff --git a/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsController.java b/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsController.java
index 86e044c4291..163027bf54e 100644
--- a/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsController.java
+++ b/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsController.java
@@ -26,6 +26,7 @@ import androidx.preference.Preference;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerMixin;
+import com.android.settings.keyboard.Flags;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
@@ -85,13 +86,15 @@ public class TouchpadAndMouseSettingsController extends BasePreferenceController
@Override
public int getAvailabilityStatus() {
+ boolean isNewPageFlagDisabled = !Flags.keyboardAndTouchpadA11yNewPageEnabled();
boolean isFeatureOn = FeatureFlagUtils
.isEnabled(mContext, FeatureFlagUtils.SETTINGS_NEW_KEYBOARD_TRACKPAD);
boolean isTouchpad = InputPeripheralsSettingsUtils.isTouchpad();
boolean isPointerCustomizationEnabled =
android.view.flags.Flags.enableVectorCursorA11ySettings();
boolean isMouse = InputPeripheralsSettingsUtils.isMouse();
- return (isFeatureOn && isTouchpad) || (isPointerCustomizationEnabled && isMouse) ? AVAILABLE
+ return ((isFeatureOn && isTouchpad) || (isPointerCustomizationEnabled && isMouse))
+ && isNewPageFlagDisabled ? AVAILABLE
: CONDITIONALLY_UNAVAILABLE;
}
}
diff --git a/src/com/android/settings/network/NetworkProviderSettings.java b/src/com/android/settings/network/NetworkProviderSettings.java
index c776987856e..1fc91014f10 100644
--- a/src/com/android/settings/network/NetworkProviderSettings.java
+++ b/src/com/android/settings/network/NetworkProviderSettings.java
@@ -706,7 +706,7 @@ public class NetworkProviderSettings extends RestrictedDashboardFragment
forget(mSelectedWifiEntry);
return true;
case MENU_ID_SHARE:
- WifiDppUtils.showLockScreen(getContext(),
+ WifiDppUtils.showLockScreenForWifiSharing(getContext(),
() -> launchWifiDppConfiguratorActivity(mSelectedWifiEntry));
return true;
case MENU_ID_MODIFY:
diff --git a/src/com/android/settings/network/OWNERS b/src/com/android/settings/network/OWNERS
index a63a825b0ea..ad3c9dada67 100644
--- a/src/com/android/settings/network/OWNERS
+++ b/src/com/android/settings/network/OWNERS
@@ -1,12 +1,8 @@
# Default reviewers for this and subdirectories.
-allenwtsu@google.com
-andychou@google.com
-bonianchen@google.com
-changbetty@google.com
-leechou@google.com
+chaohuiw@google.com
+evanwu@google.com
songferngwang@google.com
tomhsu@google.com
wengsu@google.com
-zoeychen@google.com
# Emergency approvers in case the above are not available
diff --git a/src/com/android/settings/service/PreferenceService.kt b/src/com/android/settings/service/PreferenceService.kt
index 3a677629afd..d07e78d333e 100644
--- a/src/com/android/settings/service/PreferenceService.kt
+++ b/src/com/android/settings/service/PreferenceService.kt
@@ -16,6 +16,7 @@
package com.android.settings.service
+import android.app.Application
import android.os.Binder
import android.os.OutcomeReceiver
import android.os.Process
@@ -26,38 +27,49 @@ import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceService
+import com.android.settingslib.graph.GetPreferenceGraphApiHandler
+import com.android.settingslib.graph.GetPreferenceGraphRequest
import com.android.settingslib.graph.PreferenceGetterApiHandler
+import com.android.settingslib.graph.PreferenceGetterFlags
import com.android.settingslib.graph.PreferenceSetterApiHandler
import com.android.settingslib.ipc.ApiPermissionChecker
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
-import kotlinx.coroutines.Job
+import kotlinx.coroutines.SupervisorJob
import kotlinx.coroutines.launch
import java.lang.Exception
class PreferenceService : SettingsPreferenceService() {
- private val scope = CoroutineScope(Job() + Dispatchers.Main)
+ private val scope = CoroutineScope(SupervisorJob() + Dispatchers.IO)
private val getApiHandler = PreferenceGetterApiHandler(1, ApiPermissionChecker.alwaysAllow())
private val setApiHandler = PreferenceSetterApiHandler(2, ApiPermissionChecker.alwaysAllow())
+ private val graphApi = GraphProvider(3)
override fun onGetAllPreferenceMetadata(
request: MetadataRequest,
callback: OutcomeReceiver
) {
- // TODO(379750656): Update graph API to be usable outside SettingsLib
- callback.onError(UnsupportedOperationException("Not yet supported"))
+ scope.launch {
+ val graphProto = graphApi.invoke(application, Process.myUid(), Binder.getCallingUid(),
+ GetPreferenceGraphRequest(
+ includeValue = false,
+ flags = PreferenceGetterFlags.METADATA
+ ))
+ val result = transformCatalystGetMetadataResponse(this@PreferenceService, graphProto)
+ callback.onResult(result)
+ }
}
override fun onGetPreferenceValue(
request: GetValueRequest,
callback: OutcomeReceiver
) {
- scope.launch(Dispatchers.IO) {
+ scope.launch {
val apiRequest = transformFrameworkGetValueRequest(request)
val response = getApiHandler.invoke(application, Process.myUid(),
- Binder.getCallingPid(), apiRequest)
+ Binder.getCallingUid(), apiRequest)
val result = transformCatalystGetValueResponse(
this@PreferenceService,
request,
@@ -75,7 +87,7 @@ class PreferenceService : SettingsPreferenceService() {
request: SetValueRequest,
callback: OutcomeReceiver
) {
- scope.launch(Dispatchers.IO) {
+ scope.launch {
val apiRequest = transformFrameworkSetValueRequest(request)
if (apiRequest == null) {
callback.onResult(
@@ -83,10 +95,20 @@ class PreferenceService : SettingsPreferenceService() {
)
} else {
val response = setApiHandler.invoke(application, Process.myUid(),
- Binder.getCallingPid(), apiRequest)
+ Binder.getCallingUid(), apiRequest)
callback.onResult(transformCatalystSetValueResponse(response))
}
}
}
+
+ // Basic implementation - we already have permission to access Graph for Metadata via superclass
+ private class GraphProvider(override val id: Int) : GetPreferenceGraphApiHandler(emptySet()) {
+ override fun hasPermission(
+ application: Application,
+ myUid: Int,
+ callingUid: Int,
+ request: GetPreferenceGraphRequest
+ ) = true
+ }
}
diff --git a/src/com/android/settings/service/PreferenceServiceRequestTransformer.kt b/src/com/android/settings/service/PreferenceServiceRequestTransformer.kt
index 7a4c7fc9dff..18307e0baf3 100644
--- a/src/com/android/settings/service/PreferenceServiceRequestTransformer.kt
+++ b/src/com/android/settings/service/PreferenceServiceRequestTransformer.kt
@@ -19,6 +19,7 @@ package com.android.settings.service
import android.content.Context
import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult
+import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceMetadata
@@ -34,9 +35,55 @@ import com.android.settingslib.graph.preferenceValueProto
import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.graph.getText
+import com.android.settingslib.graph.proto.PreferenceGraphProto
+import com.android.settingslib.graph.proto.PreferenceOrGroupProto
import com.android.settingslib.graph.toIntent
import com.android.settingslib.metadata.SensitivityLevel
+/** Transform Catalyst Graph result to Framework GET METADATA result */
+fun transformCatalystGetMetadataResponse(
+ context: Context,
+ graph: PreferenceGraphProto
+): MetadataResult {
+ val preferences = mutableSetOf()
+ // recursive function to visit all nodes in preference group
+ fun traverseGroupOrPref(
+ screenKey: String,
+ groupOrPref: PreferenceOrGroupProto,
+ ) {
+ when (groupOrPref.kindCase) {
+ PreferenceOrGroupProto.KindCase.PREFERENCE ->
+ preferences.add(
+ PreferenceWithScreen(screenKey, groupOrPref.preference)
+ )
+ PreferenceOrGroupProto.KindCase.GROUP -> {
+ for (child in groupOrPref.group.preferencesList) {
+ traverseGroupOrPref(screenKey, child)
+ }
+ }
+ else -> {}
+ }
+ }
+ // traverse all screens and all preferences on screen
+ for ((screenKey, screen) in graph.screensMap) {
+ for (groupOrPref in screen.root.preferencesList) {
+ traverseGroupOrPref(screenKey, groupOrPref)
+ }
+ }
+
+ return if (preferences.isNotEmpty()) {
+ MetadataResult.Builder(MetadataResult.RESULT_OK)
+ .setMetadataList(
+ preferences.map {
+ it.preference.toMetadata(context, it.screenKey)
+ }
+ )
+ .build()
+ } else {
+ MetadataResult.Builder(MetadataResult.RESULT_UNSUPPORTED).build()
+ }
+}
+
/** Translate Framework GET VALUE request to Catalyst GET VALUE request */
fun transformFrameworkGetValueRequest(
request: GetValueRequest,
@@ -133,6 +180,11 @@ fun transformCatalystSetValueResponse(@PreferenceSetterResult response: Int): Se
return SetValueResult.Builder(resultCode).build()
}
+private data class PreferenceWithScreen(
+ val screenKey: String,
+ val preference: PreferenceProto,
+)
+
private fun PreferenceProto.toMetadata(
context: Context,
screenKey: String
diff --git a/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java b/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java
index 8f9741a6b34..4ffe279d6d4 100644
--- a/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/AddDevicePreferenceController2.java
@@ -57,7 +57,8 @@ public class AddDevicePreferenceController2 extends BasePreferenceController {
@Override
public boolean handlePreferenceTreeClick(Preference preference) {
if (KEY_ADD_DEVICE.equals(preference.getKey())) {
- WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorQrCodeScanner());
+ WifiDppUtils.showLockScreenForWifiSharing(mContext,
+ () -> launchWifiDppConfiguratorQrCodeScanner());
return true; /* click is handled */
}
diff --git a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
index a8d7f417a4a..ecddecfce74 100644
--- a/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
+++ b/src/com/android/settings/wifi/details2/WifiDetailPreferenceController2.java
@@ -980,7 +980,8 @@ public class WifiDetailPreferenceController2 extends AbstractPreferenceControlle
* Share the wifi network with QR code.
*/
private void shareNetwork() {
- WifiDppUtils.showLockScreen(mContext, () -> launchWifiDppConfiguratorActivity());
+ WifiDppUtils.showLockScreenForWifiSharing(mContext,
+ () -> launchWifiDppConfiguratorActivity());
}
/**
diff --git a/src/com/android/settings/wifi/dpp/WifiDppUtils.java b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
index 23a6a5423e4..24ab496cc3f 100644
--- a/src/com/android/settings/wifi/dpp/WifiDppUtils.java
+++ b/src/com/android/settings/wifi/dpp/WifiDppUtils.java
@@ -16,6 +16,8 @@
package com.android.settings.wifi.dpp;
+import android.annotation.NonNull;
+import android.annotation.SuppressLint;
import android.app.KeyguardManager;
import android.content.Context;
import android.content.Intent;
@@ -33,6 +35,9 @@ import android.os.Vibrator;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties;
import android.text.TextUtils;
+import android.util.Log;
+
+import androidx.annotation.VisibleForTesting;
import com.android.settings.R;
import com.android.settings.Utils;
@@ -58,6 +63,8 @@ import javax.crypto.SecretKey;
* @see WifiQrCode
*/
public class WifiDppUtils {
+ private static final String TAG = "WifiDppUtils";
+
/**
* The fragment tag specified to FragmentManager for container activities to manage fragments.
*/
@@ -109,7 +116,15 @@ public class WifiDppUtils {
private static final Duration VIBRATE_DURATION_QR_CODE_RECOGNITION = Duration.ofMillis(3);
- private static final String AES_CBC_PKCS7_PADDING = "AES/CBC/PKCS7Padding";
+ /**
+ * Parameters to check whether the device has been locked recently
+ */
+ @VisibleForTesting
+ public static final String AES_CBC_PKCS7_PADDING = "AES/CBC/PKCS7Padding";
+ @VisibleForTesting
+ public static final String WIFI_SHARING_KEY_ALIAS = "wifi_sharing_auth_key";
+ @VisibleForTesting
+ public static final int WIFI_SHARING_MAX_UNLOCK_SECONDS = 60;
/**
* Returns whether the device support WiFi DPP.
@@ -426,51 +441,75 @@ public class WifiDppUtils {
* Shows authentication screen to confirm credentials (pin, pattern or password) for the current
* user of the device.
*
- * @param context The {@code Context} used to get {@code KeyguardManager} service
+ * @param context The {@code Context} used to get {@code KeyguardManager} service
* @param successRunnable The {@code Runnable} which will be executed if the user does not setup
* device security or if lock screen is unlocked
*/
- public static void showLockScreen(Context context, Runnable successRunnable) {
- final KeyguardManager keyguardManager = (KeyguardManager) context.getSystemService(
- Context.KEYGUARD_SERVICE);
-
- if (keyguardManager.isKeyguardSecure()) {
- final BiometricPrompt.AuthenticationCallback authenticationCallback =
- new BiometricPrompt.AuthenticationCallback() {
- @Override
- public void onAuthenticationSucceeded(
- BiometricPrompt.AuthenticationResult result) {
- successRunnable.run();
- }
-
- @Override
- public void onAuthenticationError(int errorCode, CharSequence errString) {
- //Do nothing
- }
- };
-
- final int userId = UserHandle.myUserId();
-
- final BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context)
- .setTitle(context.getText(R.string.wifi_dpp_lockscreen_title));
-
- if (keyguardManager.isDeviceSecure()) {
- builder.setDeviceCredentialAllowed(true);
- builder.setTextForDeviceCredential(
- null /* title */,
- Utils.getConfirmCredentialStringForUser(
- context, userId, Utils.getCredentialType(context, userId)),
- null /* description */);
- }
-
- final BiometricPrompt bp = builder.build();
- final Handler handler = new Handler(Looper.getMainLooper());
- bp.authenticate(new CancellationSignal(),
- runnable -> handler.post(runnable),
- authenticationCallback);
- } else {
+ public static void showLockScreen(@NonNull Context context, @NonNull Runnable successRunnable) {
+ KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+ if (keyguardManager == null || !keyguardManager.isKeyguardSecure()) {
successRunnable.run();
+ return;
}
+ showLockScreen(context, successRunnable, keyguardManager);
+ }
+
+ /**
+ * Shows authentication screen to confirm credentials (pin, pattern or password) for the
+ * current user of the device. But if the device has been unlocked recently, the
+ * authentication screen will be skipped.
+ *
+ * @param context The {@code Context} used to get {@code KeyguardManager} service
+ * @param successRunnable The {@code Runnable} which will be executed if the user does not setup
+ * device security or if lock screen is unlocked
+ */
+ public static void showLockScreenForWifiSharing(@NonNull Context context,
+ @NonNull Runnable successRunnable) {
+ KeyguardManager keyguardManager = context.getSystemService(KeyguardManager.class);
+ if (keyguardManager == null || !keyguardManager.isKeyguardSecure()) {
+ successRunnable.run();
+ return;
+ }
+ if (isUnlockedWithinSeconds(WIFI_SHARING_KEY_ALIAS, WIFI_SHARING_MAX_UNLOCK_SECONDS)) {
+ Log.d(TAG, "Bypassing the lock screen because the device was unlocked recently.");
+ successRunnable.run();
+ return;
+ }
+ showLockScreen(context, successRunnable, keyguardManager);
+ }
+
+ @SuppressLint("MissingPermission")
+ private static void showLockScreen(@NonNull Context context, @NonNull Runnable successRunnable,
+ @NonNull KeyguardManager keyguardManager) {
+ BiometricPrompt.AuthenticationCallback authenticationCallback =
+ new BiometricPrompt.AuthenticationCallback() {
+ @Override
+ public void onAuthenticationSucceeded(
+ BiometricPrompt.AuthenticationResult result) {
+ successRunnable.run();
+ }
+
+ @Override
+ public void onAuthenticationError(int errorCode, CharSequence errString) {
+ //Do nothing
+ }
+ };
+ int userId = UserHandle.myUserId();
+ BiometricPrompt.Builder builder = new BiometricPrompt.Builder(context)
+ .setTitle(context.getText(R.string.wifi_dpp_lockscreen_title));
+ if (keyguardManager.isDeviceSecure()) {
+ builder.setDeviceCredentialAllowed(true);
+ builder.setTextForDeviceCredential(
+ null /* title */,
+ Utils.getConfirmCredentialStringForUser(
+ context, userId, Utils.getCredentialType(context, userId)),
+ null /* description */);
+ }
+ BiometricPrompt bp = builder.build();
+ Handler handler = new Handler(Looper.getMainLooper());
+ bp.authenticate(new CancellationSignal(),
+ runnable -> handler.post(runnable),
+ authenticationCallback);
}
/**
diff --git a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
index 1bcff1ea45e..d2d26ab84fa 100644
--- a/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
+++ b/src/com/android/settings/wifi/tether/WifiTetherSSIDPreferenceController.java
@@ -123,7 +123,7 @@ public class WifiTetherSSIDPreferenceController extends WifiTetherBasePreference
}
private void shareHotspotNetwork(Intent intent) {
- WifiDppUtils.showLockScreen(mContext, () -> {
+ WifiDppUtils.showLockScreenForWifiSharing(mContext, () -> {
mMetricsFeatureProvider.action(SettingsEnums.PAGE_UNKNOWN,
SettingsEnums.ACTION_SETTINGS_SHARE_WIFI_HOTSPOT_QR_CODE,
SettingsEnums.SETTINGS_WIFI_DPP_CONFIGURATOR,
diff --git a/tests/robotests/src/com/android/settings/accessibility/HighContrastTextMigrationReceiverTest.java b/tests/robotests/src/com/android/settings/accessibility/HighContrastTextMigrationReceiverTest.java
new file mode 100644
index 00000000000..0fedddc7416
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/accessibility/HighContrastTextMigrationReceiverTest.java
@@ -0,0 +1,241 @@
+/*
+ * Copyright (C) 2024 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.accessibility;
+
+import static com.android.settings.SettingsActivity.EXTRA_FRAGMENT_ARG_KEY;
+import static com.android.settings.SettingsActivity.EXTRA_SHOW_FRAGMENT_ARGUMENTS;
+import static com.android.settings.accessibility.AccessibilityUtil.State.OFF;
+import static com.android.settings.accessibility.AccessibilityUtil.State.ON;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.ACTION_RESTORED;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.NOTIFICATION_CHANNEL;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.NOTIFICATION_ID;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.PROMPT_SHOWN;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.PROMPT_UNNECESSARY;
+import static com.android.settings.accessibility.HighContrastTextMigrationReceiver.PromptState.UNKNOWN;
+
+import static com.google.common.truth.Truth.assertThat;
+
+import android.app.Notification;
+import android.app.NotificationManager;
+import android.app.PendingIntent;
+import android.content.ComponentName;
+import android.content.Context;
+import android.content.Intent;
+import android.content.pm.ActivityInfo;
+import android.content.pm.ApplicationInfo;
+import android.os.Bundle;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.annotations.EnableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
+import android.provider.Settings;
+
+import androidx.test.core.app.ApplicationProvider;
+
+import com.android.graphics.hwui.flags.Flags;
+import com.android.settings.R;
+import com.android.settings.Utils;
+
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.Shadows;
+import org.robolectric.shadows.ShadowNotification;
+import org.robolectric.shadows.ShadowNotificationManager;
+import org.robolectric.shadows.ShadowPackageManager;
+
+/** Tests for {@link HighContrastTextMigrationReceiver}. */
+@RunWith(RobolectricTestRunner.class)
+public class HighContrastTextMigrationReceiverTest {
+
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
+ private final Context mContext = ApplicationProvider.getApplicationContext();
+ private HighContrastTextMigrationReceiver mReceiver;
+ private ShadowNotificationManager mShadowNotificationManager;
+
+ @Before
+ public void setUp() {
+ NotificationManager notificationManager =
+ mContext.getSystemService(NotificationManager.class);
+ mShadowNotificationManager = Shadows.shadowOf(notificationManager);
+
+ // Setup Settings app as a system app
+ ShadowPackageManager shadowPm = Shadows.shadowOf(mContext.getPackageManager());
+ ComponentName textReadingComponent = new ComponentName(Utils.SETTINGS_PACKAGE_NAME,
+ com.android.settings.Settings.TextReadingSettingsActivity.class.getName());
+ ActivityInfo activityInfo = shadowPm.addActivityIfNotPresent(textReadingComponent);
+ activityInfo.applicationInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
+ shadowPm.addOrUpdateActivity(activityInfo);
+
+ mReceiver = new HighContrastTextMigrationReceiver();
+ }
+
+ @Test
+ @DisableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onReceive_flagOff_settingsNotSet() {
+ mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED));
+
+ assertPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF);
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onRestored_hctStateOn_showPromptHctKeepsOn() {
+ setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ ON);
+
+ mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED));
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, ON);
+ verifyNotificationSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onRestored_hctStateOff_showPromptHctKeepsOff() {
+ setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF);
+
+ mReceiver.onReceive(mContext, new Intent(ACTION_RESTORED));
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, OFF);
+ verifyNotificationSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateUnknownHctOn_showPromptAndAutoDisableHct() {
+ setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ ON);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF);
+ verifyNotificationSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateUnknownAndHctOff_promptIsUnnecessaryHctKeepsOff() {
+ setPromptStateAndHctState(/* promptState= */ UNKNOWN, /* hctState= */ OFF);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF);
+ verifyNotificationNotSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateShownAndHctOn_promptStateUnchangedHctKeepsOn() {
+ setPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ ON);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ ON);
+ verifyNotificationNotSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateShownAndHctOff_promptStateUnchangedHctKeepsOff() {
+ setPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_SHOWN, /* hctState= */ OFF);
+ verifyNotificationNotSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateUnnecessaryAndHctOn_promptStateUnchangedHctKeepsOn() {
+ setPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ ON);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ ON);
+ verifyNotificationNotSent();
+ }
+
+ @Test
+ @EnableFlags(Flags.FLAG_HIGH_CONTRAST_TEXT_SMALL_TEXT_RECT)
+ public void onPreBootCompleted_promptStateUnnecessaryHctOff_promptStateUnchangedHctKeepsOff() {
+ setPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF);
+
+ Intent intent = new Intent(Intent.ACTION_PRE_BOOT_COMPLETED);
+ mReceiver.onReceive(mContext, intent);
+
+ assertPromptStateAndHctState(/* promptState= */ PROMPT_UNNECESSARY, /* hctState= */ OFF);
+ verifyNotificationNotSent();
+ }
+
+ private void verifyNotificationNotSent() {
+ Notification notification = mShadowNotificationManager.getNotification(NOTIFICATION_ID);
+ assertThat(notification).isNull();
+ }
+
+ private void verifyNotificationSent() {
+ // Verify hct channel created
+ assertThat(mShadowNotificationManager.getNotificationChannels().stream().anyMatch(
+ channel -> channel.getId().equals(NOTIFICATION_CHANNEL))).isTrue();
+
+ // Verify hct notification is sent with correct content
+ Notification notification = mShadowNotificationManager.getNotification(NOTIFICATION_ID);
+ assertThat(notification).isNotNull();
+
+ ShadowNotification shadowNotification = Shadows.shadowOf(notification);
+ assertThat(shadowNotification.getContentTitle()).isEqualTo(mContext.getString(
+ R.string.accessibility_toggle_high_text_contrast_preference_title));
+ assertThat(shadowNotification.getContentText()).isEqualTo(
+ mContext.getString(R.string.accessibility_notification_high_contrast_text_content));
+
+ assertThat(notification.actions.length).isEqualTo(1);
+ assertThat(notification.actions[0].title.toString()).isEqualTo(
+ mContext.getString(R.string.accessibility_notification_high_contrast_text_action));
+
+ PendingIntent pendingIntent = notification.actions[0].actionIntent;
+ Intent settingsIntent = Shadows.shadowOf(pendingIntent).getSavedIntent();
+ Bundle fragmentArgs = settingsIntent.getBundleExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS);
+ assertThat(fragmentArgs).isNotNull();
+ assertThat(fragmentArgs.getString(EXTRA_FRAGMENT_ARG_KEY))
+ .isEqualTo(TextReadingPreferenceFragment.HIGH_TEXT_CONTRAST_KEY);
+ }
+
+ private void assertPromptStateAndHctState(
+ @HighContrastTextMigrationReceiver.PromptState int promptState,
+ @AccessibilityUtil.State int hctState) {
+ assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, UNKNOWN))
+ .isEqualTo(promptState);
+ assertThat(Settings.Secure.getInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, OFF))
+ .isEqualTo(hctState);
+ }
+
+ private void setPromptStateAndHctState(
+ @HighContrastTextMigrationReceiver.PromptState int promptState,
+ @AccessibilityUtil.State int hctState) {
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, promptState);
+ Settings.Secure.putInt(mContext.getContentResolver(),
+ Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, hctState);
+ }
+}
diff --git a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
index 05273fc050d..ba9eaa5871e 100644
--- a/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/PreviewSizeSeekBarControllerTest.java
@@ -29,7 +29,6 @@ import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Intent;
-import android.os.Bundle;
import android.view.LayoutInflater;
import android.widget.PopupWindow;
import android.widget.SeekBar;
@@ -44,7 +43,6 @@ import androidx.test.ext.junit.rules.ActivityScenarioRule;
import com.android.settings.R;
import com.android.settings.SettingsPreferenceFragment;
import com.android.settings.testutils.shadow.ShadowFragment;
-import com.android.settings.widget.LabeledSeekBarPreference;
import com.android.settingslib.testutils.shadow.ShadowInteractionJankMonitor;
import com.google.android.setupcompat.util.WizardManagerHelper;
@@ -77,7 +75,7 @@ public class PreviewSizeSeekBarControllerTest {
private Activity mContext;
private PreviewSizeSeekBarController mSeekBarController;
private FontSizeData mFontSizeData;
- private LabeledSeekBarPreference mSeekBarPreference;
+ private AccessibilitySeekBarPreference mSeekBarPreference;
private PreferenceScreen mPreferenceScreen;
private TestFragment mFragment;
@@ -109,7 +107,7 @@ public class PreviewSizeSeekBarControllerTest {
mPreferenceScreen = spy(new PreferenceScreen(mContext, /* attrs= */ null));
when(mPreferenceScreen.getPreferenceManager()).thenReturn(mPreferenceManager);
doReturn(mPreferenceScreen).when(mFragment).getPreferenceScreen();
- mSeekBarPreference = spy(new LabeledSeekBarPreference(mContext, /* attrs= */ null));
+ mSeekBarPreference = spy(new AccessibilitySeekBarPreference(mContext, /* attrs= */ null));
mSeekBarPreference.setKey(FONT_SIZE_KEY);
LayoutInflater inflater = LayoutInflater.from(mContext);
@@ -246,12 +244,11 @@ public class PreviewSizeSeekBarControllerTest {
@Test
@Config(shadows = ShadowFragment.class)
- public void restoreValueFromSavedInstanceState_showTooltipView() {
- final Bundle savedInstanceState = new Bundle();
- savedInstanceState.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true);
- mSeekBarController.onCreate(savedInstanceState);
+ public void enabledNeedsQSTooltipReshow_showTooltipView() {
+ mSeekBarPreference.setNeedsQSTooltipReshow(true);
mSeekBarController.displayPreference(mPreferenceScreen);
+ mSeekBarController.onStart();
ShadowLooper.idleMainLooper();
assertThat(getLatestPopupWindow().isShowing()).isTrue();
diff --git a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreviewControllerTest.java b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreviewControllerTest.java
index f768e42f2e6..375952f725c 100644
--- a/tests/robotests/src/com/android/settings/accessibility/TextReadingPreviewControllerTest.java
+++ b/tests/robotests/src/com/android/settings/accessibility/TextReadingPreviewControllerTest.java
@@ -28,7 +28,6 @@ import androidx.test.core.app.ApplicationProvider;
import com.android.settings.display.PreviewPagerAdapter;
import com.android.settings.testutils.shadow.ShadowInteractionJankMonitor;
-import com.android.settings.widget.LabeledSeekBarPreference;
import org.junit.Before;
import org.junit.Test;
@@ -54,8 +53,8 @@ public class TextReadingPreviewControllerTest {
private final Context mContext = ApplicationProvider.getApplicationContext();
private TextReadingPreviewController mPreviewController;
private TextReadingPreviewPreference mPreviewPreference;
- private LabeledSeekBarPreference mFontSizePreference;
- private LabeledSeekBarPreference mDisplaySizePreference;
+ private AccessibilitySeekBarPreference mFontSizePreference;
+ private AccessibilitySeekBarPreference mDisplaySizePreference;
@Mock
private DisplaySizeData mDisplaySizeData;
@@ -73,8 +72,8 @@ public class TextReadingPreviewControllerTest {
mPreviewPreference = spy(new TextReadingPreviewPreference(mContext, /* attr= */ null));
mPreviewController = new TextReadingPreviewController(mContext, PREVIEW_KEY, fontSizeData,
mDisplaySizeData);
- mFontSizePreference = new LabeledSeekBarPreference(mContext, /* attr= */ null);
- mDisplaySizePreference = new LabeledSeekBarPreference(mContext, /* attr= */ null);
+ mFontSizePreference = new AccessibilitySeekBarPreference(mContext, /* attr= */ null);
+ mDisplaySizePreference = new AccessibilitySeekBarPreference(mContext, /* attr= */ null);
}
@Test
diff --git a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
index 9609af4a5e3..2251c3bff5a 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/AvailableMediaBluetoothDeviceUpdaterTest.java
@@ -124,24 +124,17 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
when(mCachedBluetoothDevice.getMemberDevice()).thenReturn(ImmutableSet.of());
- mBluetoothDeviceUpdater =
- spy(
- new AvailableMediaBluetoothDeviceUpdater(
- mContext, mDevicePreferenceCallback, /* metricsCategory= */ 0));
- mBluetoothDeviceUpdater.setPrefContext(mContext);
mPreference =
new BluetoothDevicePreference(
mContext,
mCachedBluetoothDevice,
false,
BluetoothDevicePreference.SortType.TYPE_DEFAULT);
- doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
- doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
}
@Test
public void onAudioModeChanged_hfpDeviceConnected_inCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -153,7 +146,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onAudioModeChanged_hfpDeviceConnected_notInCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -165,7 +158,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onAudioModeChanged_a2dpDeviceConnected_inCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -177,7 +170,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onAudioModeChanged_a2dpDeviceConnected_notInCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -189,7 +182,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -202,7 +195,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_a2dpDeviceConnected_inCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -215,7 +208,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_hfpDeviceConnected_notInCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -228,7 +221,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_hfpDeviceConnected_inCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -241,7 +234,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_ashaHearingAidConnected_notInCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
@@ -256,7 +249,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_ashaHearingAidConnected_inCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
@@ -272,7 +265,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_leaConnected_notInCallSharingFlagOff_addPref() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -292,7 +285,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_leaConnected_notInCallNotInSharing_addPref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -309,7 +302,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_leaConnected_inCallSharingFlagOff_addPref() {
mSetFlagsRule.disableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -326,7 +319,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_leaConnected_inCallNotInSharing_addPref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -344,7 +337,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
public void onProfileConnectionStateChanged_leaConnected_notInCallInSharing_removePref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -367,7 +360,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
onProfileConnectionStateChanged_leaConnected_noInCallInSharing_hysteresis_removePref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -388,7 +381,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
public void onProfileConnectionStateChanged_leaConnected_inCallSharing_removePref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mSetFlagsRule.disableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -410,7 +403,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
public void onProfileConnectionStateChanged_leaConnected_inCallSharing_hysteresis_removePref() {
mSetFlagsRule.enableFlags(Flags.FLAG_ENABLE_LE_AUDIO_SHARING);
mSetFlagsRule.enableFlags(Flags.FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -430,7 +423,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void
onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovePreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -446,7 +439,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.isDeviceConnected(any(CachedBluetoothDevice.class)))
.thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -462,6 +455,7 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() {
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(
mCachedBluetoothDevice, BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP);
@@ -470,8 +464,19 @@ public class AvailableMediaBluetoothDeviceUpdaterTest {
@Test
public void onClick_Preference_setActive() {
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
mBluetoothDeviceUpdater.onPreferenceClick(mPreference);
verify(mDevicePreferenceCallback).onDeviceClick(mPreference);
}
+
+ private void setUpDeviceUpdaterWithAudioMode(int audioMode) {
+ mAudioManager.setMode(audioMode);
+ mBluetoothDeviceUpdater =
+ spy(new AvailableMediaBluetoothDeviceUpdater(
+ mContext, mDevicePreferenceCallback, /* metricsCategory= */ 0));
+ mBluetoothDeviceUpdater.setPrefContext(mContext);
+ doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
+ doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
+ }
}
diff --git a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
index b2449dab39e..f68a8d4cf6a 100644
--- a/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
+++ b/tests/robotests/src/com/android/settings/bluetooth/ConnectedBluetoothDeviceUpdaterTest.java
@@ -112,16 +112,11 @@ public class ConnectedBluetoothDeviceUpdaterTest {
when(mCachedBluetoothDevice.getAddress()).thenReturn(MAC_ADDRESS);
when(mCachedBluetoothDevice.getDrawableWithDescription()).thenReturn(pairs);
mShadowCachedBluetoothDeviceManager.setCachedDevicesCopy(mCachedDevices);
- mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mContext,
- mDevicePreferenceCallback, /* metricsCategory= */ 0));
- mBluetoothDeviceUpdater.setPrefContext(mContext);
- doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
- doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
}
@Test
public void onAudioModeChanged_hfpDeviceConnected_notInCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -133,7 +128,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onAudioModeChanged_hfpDeviceConnected_inCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -145,7 +140,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onAudioModeChanged_a2dpDeviceConnected_notInCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -157,7 +152,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onAudioModeChanged_a2dpDeviceConnected_inCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -169,7 +164,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_a2dpDeviceConnected_inCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -182,7 +177,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -196,7 +191,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_a2dpDeviceConnected_notInCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedA2dpDevice()).thenReturn(true);
@@ -209,7 +204,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_hfpDeviceConnected_inCall_removePreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -222,7 +217,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_hfpDeviceConnected_notInCall_addPreference() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -236,7 +231,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_ashaHearingAidConnected_inCall_removePreference()
{
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
@@ -250,7 +245,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_ashaHearingAidConnected_notInCall_removePreference()
{
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater.
isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedAshaHearingAidDevice()).thenReturn(true);
@@ -263,7 +258,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_leAudioDeviceConnected_inCall_removesPreference() {
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -277,7 +272,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_leAudioDeviceConnected_notInCall_removesPreference()
{
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -290,7 +285,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_deviceIsNotInList_inCall_invokesRemovesPreference()
{
- mAudioManager.setMode(AudioManager.MODE_IN_CALL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_IN_CALL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -305,7 +300,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_deviceIsNotInList_notInCall_invokesRemovesPreference
() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedLeAudioDevice()).thenReturn(true);
@@ -319,6 +314,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void onProfileConnectionStateChanged_deviceDisconnected_removePreference() {
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
mBluetoothDeviceUpdater.onProfileConnectionStateChanged(mCachedBluetoothDevice,
BluetoothProfile.STATE_DISCONNECTED, BluetoothProfile.A2DP);
@@ -327,6 +323,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
public void addPreference_addPreference_shouldHideSecondTarget() {
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
BluetoothDevicePreference btPreference =
new BluetoothDevicePreference(mContext, mCachedBluetoothDevice,
true, BluetoothDevicePreference.SortType.TYPE_DEFAULT);
@@ -340,7 +337,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_notExclusiveManagedDevice_addDevice() {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -356,7 +353,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_exclusivelyManagedDevice_packageNotInstalled_addDevice()
throws Exception {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -376,7 +373,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
throws Exception {
ApplicationInfo appInfo = new ApplicationInfo();
appInfo.enabled = false;
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -393,7 +390,7 @@ public class ConnectedBluetoothDeviceUpdaterTest {
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_HIDE_EXCLUSIVELY_MANAGED_BLUETOOTH_DEVICE)
public void update_exclusivelyManagedDevice_packageInstalledAndEnabled_removePreference()
throws Exception {
- mAudioManager.setMode(AudioManager.MODE_NORMAL);
+ setUpDeviceUpdaterWithAudioMode(AudioManager.MODE_NORMAL);
when(mBluetoothDeviceUpdater
.isDeviceConnected(any(CachedBluetoothDevice.class))).thenReturn(true);
when(mCachedBluetoothDevice.isConnectedHfpDevice()).thenReturn(true);
@@ -407,4 +404,13 @@ public class ConnectedBluetoothDeviceUpdaterTest {
verify(mBluetoothDeviceUpdater).removePreference(mCachedBluetoothDevice);
verify(mBluetoothDeviceUpdater, never()).addPreference(mCachedBluetoothDevice);
}
+
+ private void setUpDeviceUpdaterWithAudioMode(int audioMode) {
+ mAudioManager.setMode(audioMode);
+ mBluetoothDeviceUpdater = spy(new ConnectedBluetoothDeviceUpdater(mContext,
+ mDevicePreferenceCallback, /* metricsCategory= */ 0));
+ mBluetoothDeviceUpdater.setPrefContext(mContext);
+ doNothing().when(mBluetoothDeviceUpdater).addPreference(any());
+ doNothing().when(mBluetoothDeviceUpdater).removePreference(any());
+ }
}
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java
index 4e962c7deb3..6aff8c38d7e 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryCallbackTest.java
@@ -54,7 +54,7 @@ import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
- ShadowBluetoothAdapter.class,
+ ShadowBluetoothAdapter.class,
})
public class AudioStreamsProgressCategoryCallbackTest {
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@@ -70,8 +70,8 @@ public class AudioStreamsProgressCategoryCallbackTest {
@Before
public void setUp() {
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
- ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(
- BluetoothAdapter.getDefaultAdapter());
+ ShadowBluetoothAdapter shadowBluetoothAdapter =
+ Shadow.extract(BluetoothAdapter.getDefaultAdapter());
shadowBluetoothAdapter.setEnabled(true);
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
@@ -87,7 +87,7 @@ public class AudioStreamsProgressCategoryCallbackTest {
when(mState.getBisSyncState()).thenReturn(bisSyncState);
mCallback.onReceiveStateChanged(mDevice, /* sourceId= */ 0, mState);
- verify(mController).handleSourceConnected(any());
+ verify(mController).handleSourceConnected(any(), any());
}
@Test
@@ -102,7 +102,7 @@ public class AudioStreamsProgressCategoryCallbackTest {
when(mSourceDevice.getAddress()).thenReturn(address);
mCallback.onReceiveStateChanged(mDevice, /* sourceId= */ 0, mState);
- verify(mController).handleSourcePresent(any());
+ verify(mController).handleSourcePresent(any(), any());
}
@Test
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java
index 78d4d6e1361..f042329200a 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/AudioStreamsProgressCategoryControllerTest.java
@@ -32,6 +32,7 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
+import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
@@ -41,7 +42,7 @@ import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
-import static java.util.Collections.emptyList;
+import static java.util.Collections.emptyMap;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
@@ -93,6 +94,7 @@ import org.robolectric.shadows.androidx.fragment.FragmentController;
import java.util.ArrayList;
import java.util.List;
+import java.util.Map;
@RunWith(RobolectricTestRunner.class)
@Config(
@@ -134,8 +136,8 @@ public class AudioStreamsProgressCategoryControllerTest {
@Before
public void setUp() {
- ShadowBluetoothAdapter shadowBluetoothAdapter = Shadow.extract(
- BluetoothAdapter.getDefaultAdapter());
+ ShadowBluetoothAdapter shadowBluetoothAdapter =
+ Shadow.extract(BluetoothAdapter.getDefaultAdapter());
shadowBluetoothAdapter.setEnabled(true);
shadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
@@ -143,7 +145,7 @@ public class AudioStreamsProgressCategoryControllerTest {
BluetoothStatusCodes.FEATURE_SUPPORTED);
ShadowAudioStreamsHelper.setUseMock(mAudioStreamsHelper);
when(mAudioStreamsHelper.getLeBroadcastAssistant()).thenReturn(mLeBroadcastAssistant);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(emptyList());
+ when(mAudioStreamsHelper.getAllSourcesByDevice()).thenReturn(emptyMap());
mSetFlagsRule.disableFlags(FLAG_AUDIO_SHARING_HYSTERESIS_MODE_FIX);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
@@ -310,14 +312,12 @@ public class AudioStreamsProgressCategoryControllerTest {
// Setup a device
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);
- List connectedList = new ArrayList<>();
// Empty connected device list
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(connectedList);
+ when(mAudioStreamsHelper.getAllSourcesByDevice()).thenReturn(emptyMap());
mController.onStart(mLifecycleOwner);
shadowOf(Looper.getMainLooper()).idle();
- verify(mAudioStreamsHelper).getAllPresentSources();
verify(mLeBroadcastAssistant).startSearchingForSources(any());
var dialog = ShadowAlertDialog.getLatestAlertDialog();
@@ -355,7 +355,7 @@ public class AudioStreamsProgressCategoryControllerTest {
}
@Test
- public void testOnStart_handleSourceAlreadyConnected() {
+ public void testOnStart_handleSourceAlreadyConnected_useNameFromMetadata() {
// Setup a device
ShadowAudioStreamsHelper.setCachedBluetoothDeviceInSharingOrLeConnected(mDevice);
@@ -363,8 +363,14 @@ public class AudioStreamsProgressCategoryControllerTest {
BluetoothLeBroadcastReceiveState connected =
createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID);
List list = new ArrayList<>();
+ var data = mock(BluetoothLeAudioContentMetadata.class);
+ when(connected.getSubgroupMetadata()).thenReturn(ImmutableList.of(data));
+ when(data.getProgramInfo()).thenReturn(BROADCAST_NAME_1);
list.add(connected);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(list);
+ when(mMetadata.getBroadcastId()).thenReturn(ALREADY_CONNECTED_BROADCAST_ID);
+ when(mMetadata.getBroadcastName()).thenReturn(BROADCAST_NAME_2);
+ when(mLeBroadcastAssistant.getSourceMetadata(any(), anyInt())).thenReturn(mMetadata);
+ when(mAudioStreamsHelper.getAllSourcesByDevice()).thenReturn(Map.of(mSourceDevice, list));
// Handle already connected source in onStart
mController.displayPreference(mScreen);
@@ -382,6 +388,7 @@ public class AudioStreamsProgressCategoryControllerTest {
assertThat(preference.getValue()).isNotNull();
assertThat(preference.getValue().getAudioStreamBroadcastId())
.isEqualTo(ALREADY_CONNECTED_BROADCAST_ID);
+ assertThat(preference.getValue().getTitle()).isEqualTo(BROADCAST_NAME_2);
assertThat(state.getValue()).isEqualTo(SOURCE_ADDED);
}
@@ -409,7 +416,8 @@ public class AudioStreamsProgressCategoryControllerTest {
var data = mock(BluetoothLeAudioContentMetadata.class);
when(connected.getSubgroupMetadata()).thenReturn(ImmutableList.of(data));
when(data.getProgramInfo()).thenReturn(BROADCAST_NAME_1);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected));
+ when(mAudioStreamsHelper.getAllSourcesByDevice())
+ .thenReturn(Map.of(mSourceDevice, ImmutableList.of(connected)));
// Handle both source from qr code and already connected source in onStart
mController.displayPreference(mScreen);
@@ -578,8 +586,8 @@ public class AudioStreamsProgressCategoryControllerTest {
// Setup source already connected
BluetoothLeBroadcastReceiveState connected =
createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected));
-
+ when(mAudioStreamsHelper.getAllSourcesByDevice())
+ .thenReturn(Map.of(mSourceDevice, List.of(connected)));
// Handle source already connected in onStart
mController.displayPreference(mScreen);
mController.onStart(mLifecycleOwner);
@@ -687,7 +695,8 @@ public class AudioStreamsProgressCategoryControllerTest {
// Setup already connected source
BluetoothLeBroadcastReceiveState connected =
createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected));
+ when(mAudioStreamsHelper.getAllSourcesByDevice())
+ .thenReturn(Map.of(mSourceDevice, List.of(connected)));
// Handle connected source in onStart
mController.displayPreference(mScreen);
@@ -695,7 +704,7 @@ public class AudioStreamsProgressCategoryControllerTest {
shadowOf(Looper.getMainLooper()).idle();
// The connect source is no longer connected
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(emptyList());
+ when(mAudioStreamsHelper.getAllSourcesByDevice()).thenReturn(emptyMap());
mController.handleSourceRemoved();
shadowOf(Looper.getMainLooper()).idle();
@@ -728,7 +737,8 @@ public class AudioStreamsProgressCategoryControllerTest {
// Setup a connected source
BluetoothLeBroadcastReceiveState connected =
createConnectedMock(ALREADY_CONNECTED_BROADCAST_ID);
- when(mAudioStreamsHelper.getAllConnectedSources()).thenReturn(ImmutableList.of(connected));
+ when(mAudioStreamsHelper.getAllSourcesByDevice())
+ .thenReturn(Map.of(mSourceDevice, List.of(connected)));
// Handle connected source in onStart
mController.displayPreference(mScreen);
@@ -834,7 +844,7 @@ public class AudioStreamsProgressCategoryControllerTest {
when(receiveState.getBisSyncState()).thenReturn(bisSyncState);
// The new found source is identified as failed to connect
- mController.handleSourcePresent(receiveState);
+ mController.handleSourcePresent(mSourceDevice, receiveState);
shadowOf(Looper.getMainLooper()).idle();
ArgumentCaptor preference =
diff --git a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java
index c7d0c60efa8..e5e51fce717 100644
--- a/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java
+++ b/tests/robotests/src/com/android/settings/connecteddevice/audiosharing/audiostreams/testshadows/ShadowAudioStreamsHelper.java
@@ -16,6 +16,7 @@
package com.android.settings.connecteddevice.audiosharing.audiostreams.testshadows;
+import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
@@ -31,6 +32,7 @@ import org.robolectric.annotation.Implements;
import org.robolectric.annotation.Resetter;
import java.util.List;
+import java.util.Map;
import java.util.Optional;
@Implements(value = AudioStreamsHelper.class, callThroughByDefault = true)
@@ -59,6 +61,11 @@ public class ShadowAudioStreamsHelper {
return sMockHelper.getAllConnectedSources();
}
+ @Implementation
+ public Map> getAllSourcesByDevice() {
+ return sMockHelper.getAllSourcesByDevice();
+ }
+
@Implementation
public List getAllPresentSources() {
return sMockHelper.getAllPresentSources();
diff --git a/tests/robotests/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsControllerTest.java b/tests/robotests/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsControllerTest.java
index 9d3cc5ff77f..f3fe93dec4b 100644
--- a/tests/robotests/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsControllerTest.java
+++ b/tests/robotests/src/com/android/settings/inputmethod/TouchpadAndMouseSettingsControllerTest.java
@@ -23,12 +23,15 @@ import static com.google.common.truth.Truth.assertThat;
import static org.junit.Assume.assumeTrue;
import android.content.Context;
+import android.platform.test.annotations.DisableFlags;
+import android.platform.test.flag.junit.SetFlagsRule;
import android.view.InputDevice;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.core.BasePreferenceController;
+import com.android.settings.keyboard.Flags;
import com.android.settings.testutils.shadow.ShadowInputDevice;
import org.junit.Before;
@@ -47,6 +50,8 @@ import org.robolectric.annotation.Config;
ShadowInputDevice.class,
})
public class TouchpadAndMouseSettingsControllerTest {
+ @Rule
+ public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
@Rule
public MockitoRule rule = MockitoJUnit.rule();
@@ -80,6 +85,7 @@ public class TouchpadAndMouseSettingsControllerTest {
}
@Test
+ @DisableFlags(Flags.FLAG_KEYBOARD_AND_TOUCHPAD_A11Y_NEW_PAGE_ENABLED)
public void getAvailabilityStatus_isTouchpadAvailable() {
int deviceId = 1;
ShadowInputDevice.sDeviceIds = new int[]{deviceId};
@@ -92,6 +98,7 @@ public class TouchpadAndMouseSettingsControllerTest {
}
@Test
+ @DisableFlags(Flags.FLAG_KEYBOARD_AND_TOUCHPAD_A11Y_NEW_PAGE_ENABLED)
public void getAvailabilityStatus_isMouseAvailable() {
assumeTrue(enableVectorCursorA11ySettings());
diff --git a/tests/robotests/src/com/android/settings/service/PreferenceServiceRequestTransformerTest.kt b/tests/robotests/src/com/android/settings/service/PreferenceServiceRequestTransformerTest.kt
index f064b221282..7631a00e9d1 100644
--- a/tests/robotests/src/com/android/settings/service/PreferenceServiceRequestTransformerTest.kt
+++ b/tests/robotests/src/com/android/settings/service/PreferenceServiceRequestTransformerTest.kt
@@ -16,7 +16,6 @@
package com.android.settings.service
-import android.content.ComponentName
import android.content.Context
import android.content.Intent
import android.platform.test.annotations.RequiresFlagsEnabled
@@ -24,6 +23,7 @@ import android.platform.test.flag.junit.CheckFlagsRule
import android.platform.test.flag.junit.DeviceFlagsValueProvider
import android.service.settings.preferences.GetValueRequest
import android.service.settings.preferences.GetValueResult
+import android.service.settings.preferences.MetadataResult
import android.service.settings.preferences.SetValueRequest
import android.service.settings.preferences.SetValueResult
import android.service.settings.preferences.SettingsPreferenceMetadata
@@ -37,9 +37,15 @@ import com.android.settingslib.graph.PreferenceGetterErrorCode
import com.android.settingslib.graph.PreferenceGetterFlags
import com.android.settingslib.graph.PreferenceGetterResponse
import com.android.settingslib.graph.PreferenceSetterResult
+import com.android.settingslib.graph.preferenceGroupProto
+import com.android.settingslib.graph.preferenceOrGroupProto
+import com.android.settingslib.graph.preferenceProto
+import com.android.settingslib.graph.preferenceScreenProto
+import com.android.settingslib.graph.proto.PreferenceGraphProto
import com.android.settingslib.graph.proto.PreferenceProto
import com.android.settingslib.graph.proto.PreferenceValueProto
import com.android.settingslib.graph.proto.TextProto
+import com.android.settingslib.graph.textProto
import com.android.settingslib.graph.toProto
import com.android.settingslib.metadata.SensitivityLevel
import com.google.common.truth.Truth.assertThat
@@ -54,6 +60,73 @@ class PreferenceServiceRequestTransformerTest {
@get:Rule
val checkFlagsRule: CheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule()
+ @Test
+ fun transformCatalystGetMetadataResponse_emptyGraph_returnsFrameworkResponseWithError() {
+ val context: Context = ApplicationProvider.getApplicationContext()
+ val graphProto = PreferenceGraphProto.newBuilder().build()
+ val fResult = transformCatalystGetMetadataResponse(context, graphProto)
+ with(fResult) {
+ assertThat(resultCode).isEqualTo(MetadataResult.RESULT_UNSUPPORTED)
+ assertThat(metadataList).isEmpty()
+ }
+ }
+
+ @Test
+ fun transformCatalystGetMetadataResponse_populatedGraph_returnsFrameworkResponseWithSuccess() {
+ val context: Context = ApplicationProvider.getApplicationContext()
+ val screen = preferenceScreenProto {
+ root = preferenceGroupProto {
+ addAllPreferences(
+ listOf(
+ preferenceOrGroupProto {
+ group = preferenceGroupProto {
+ addPreferences(
+ preferenceOrGroupProto {
+ preference = preferenceProto {
+ key = "key1"
+ title = textProto { string = "title1" }
+ enabled = true
+ }
+ }
+ )
+ }
+ },
+ preferenceOrGroupProto {
+ preference = preferenceProto {
+ key = "key2"
+ title = textProto { string = "title2" }
+ enabled = false
+ }
+ }
+ )
+ )
+ }
+ }
+ val graphProto = PreferenceGraphProto.newBuilder().putScreens("screen", screen).build()
+
+ val fResult = transformCatalystGetMetadataResponse(context, graphProto)
+ with(fResult) {
+ assertThat(resultCode).isEqualTo(MetadataResult.RESULT_OK)
+ assertThat(metadataList.size).isEqualTo(2)
+ }
+ assertThat(
+ fResult.metadataList.any {
+ it.key == "key1" &&
+ it.screenKey == "screen" &&
+ it.title == "title1" &&
+ it.isEnabled == true
+ }
+ ).isTrue()
+ assertThat(
+ fResult.metadataList.any {
+ it.key == "key2" &&
+ it.screenKey == "screen" &&
+ it.title == "title2" &&
+ it.isEnabled == false
+ }
+ ).isTrue()
+ }
+
@Test
fun transformFrameworkGetValueRequest_returnsValidCatalystRequest() {
val fRequest = GetValueRequest.Builder("screen", "pref").build()
diff --git a/tests/spa_unit/src/com/android/settings/spa/wifi/dpp/WifiDppUtilsTest.kt b/tests/spa_unit/src/com/android/settings/spa/wifi/dpp/WifiDppUtilsTest.kt
new file mode 100644
index 00000000000..31ee9e6a2c7
--- /dev/null
+++ b/tests/spa_unit/src/com/android/settings/spa/wifi/dpp/WifiDppUtilsTest.kt
@@ -0,0 +1,118 @@
+/*
+ * Copyright (C) 2024 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.spa.wifi.dpp
+
+import android.app.KeyguardManager
+import android.content.Context
+import android.hardware.biometrics.BiometricPrompt
+import androidx.test.core.app.ApplicationProvider
+import androidx.test.ext.junit.runners.AndroidJUnit4
+import com.android.dx.mockito.inline.extended.ExtendedMockito
+import com.android.settings.wifi.dpp.WifiDppUtils
+import java.security.InvalidKeyException
+import java.security.Key
+import javax.crypto.Cipher
+import org.junit.After
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.any
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.never
+import org.mockito.Mockito.verify
+import org.mockito.MockitoSession
+import org.mockito.kotlin.doReturn
+import org.mockito.kotlin.doThrow
+import org.mockito.kotlin.mock
+import org.mockito.kotlin.spy
+import org.mockito.kotlin.stub
+import org.mockito.kotlin.whenever
+import org.mockito.quality.Strictness
+
+@RunWith(AndroidJUnit4::class)
+class WifiDppUtilsTest {
+ private lateinit var mockSession: MockitoSession
+
+ private val runnable = mock()
+ private val cipher = mock()
+ private var mockKeyguardManager = mock()
+ private var context: Context =
+ spy(ApplicationProvider.getApplicationContext()) {
+ on { getSystemService(KeyguardManager::class.java) } doReturn mockKeyguardManager
+ }
+
+ @Before
+ fun setUp() {
+ mockSession =
+ ExtendedMockito.mockitoSession()
+ .initMocks(this)
+ .mockStatic(Cipher::class.java)
+ .mockStatic(BiometricPrompt::class.java)
+ .mockStatic(BiometricPrompt.Builder::class.java)
+ .strictness(Strictness.LENIENT)
+ .startMocking()
+ whenever(context.applicationContext).thenReturn(context)
+ }
+
+ @After
+ fun tearDown() {
+ mockSession.finishMocking()
+ }
+
+ @Test
+ fun showLockScreen_notKeyguardSecure_runRunnable() {
+ mockKeyguardManager.stub { on { isKeyguardSecure } doReturn false }
+
+ WifiDppUtils.showLockScreen(context, runnable)
+
+ verify(runnable).run()
+ }
+
+ @Test
+ fun showLockScreen_isKeyguardSecure_doNotRunRunnable() {
+ mockKeyguardManager.stub { on { isKeyguardSecure } doReturn true }
+
+ try {
+ WifiDppUtils.showLockScreen(context, runnable)
+ } catch (_: Exception) {}
+
+ verify(runnable, never()).run()
+ }
+
+ @Test
+ fun showLockScreenForWifiSharing_deviceUnlockedRecently_runRunnable() {
+ mockKeyguardManager.stub { on { isKeyguardSecure } doReturn true }
+ whenever(Cipher.getInstance(WifiDppUtils.AES_CBC_PKCS7_PADDING)).thenReturn(cipher)
+
+ WifiDppUtils.showLockScreenForWifiSharing(context, runnable)
+
+ verify(runnable).run()
+ }
+
+ @Test
+ fun showLockScreenForWifiSharing_deviceNotUnlockedRecently_doNotRunRunnable() {
+ mockKeyguardManager.stub { on { isKeyguardSecure } doReturn true }
+ whenever(Cipher.getInstance(WifiDppUtils.AES_CBC_PKCS7_PADDING)).thenReturn(cipher)
+ doThrow(InvalidKeyException()).whenever(cipher).init(anyInt(), any())
+
+ try {
+ WifiDppUtils.showLockScreenForWifiSharing(context, runnable)
+ } catch (_: Exception) {}
+
+ verify(runnable, never()).run()
+ }
+}