Snap for 12800583 from 6a5298ffbf to 25Q2-release

Change-Id: I348aef1007e372687d1df5cc6a7b883d2091bc67
This commit is contained in:
Android Build Coastguard Worker
2024-12-13 18:17:00 -08:00
35 changed files with 1176 additions and 267 deletions

View File

@@ -5439,6 +5439,16 @@
</intent-filter>
</receiver>
<receiver
android:name=".accessibility.HighContrastTextMigrationReceiver"
android:permission="android.permission.MANAGE_ACCESSIBILITY"
android:exported="true"> <!-- Exported for SettingsProvider restore from backup. -->
<intent-filter>
<action android:name="android.intent.action.PRE_BOOT_COMPLETED"/>
<action android:name="com.android.settings.accessibility.ACTION_HIGH_CONTRAST_TEXT_RESTORED"/>
</intent-filter>
</receiver>
<activity
android:name="Settings$ChangeNfcTagAppsActivity"
android:exported="true"

View File

@@ -0,0 +1,14 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24"
android:tint="?android:attr/colorControlNormal">
<group>
<clip-path
android:pathData="M0,0h24v24h-24z"/>
<path
android:pathData="M12,22C10.067,22 8.417,21.317 7.05,19.95C5.683,18.583 5,16.933 5,15V9C5,7.067 5.683,5.417 7.05,4.05C8.417,2.683 10.067,2 12,2C13.933,2 15.583,2.683 16.95,4.05C18.317,5.417 19,7.067 19,9V15C19,16.933 18.317,18.583 16.95,19.95C15.583,21.317 13.933,22 12,22ZM13,9H17C17,7.8 16.617,6.742 15.85,5.825C15.1,4.908 14.15,4.333 13,4.1V9ZM7,9H11V4.1C9.85,4.333 8.892,4.908 8.125,5.825C7.375,6.742 7,7.8 7,9ZM12,20C13.383,20 14.558,19.517 15.525,18.55C16.508,17.567 17,16.383 17,15V11H7V15C7,16.383 7.483,17.567 8.45,18.55C9.433,19.517 10.617,20 12,20Z"
android:fillColor="@android:color/white"/>
</group>
</vector>

View File

@@ -4580,17 +4580,17 @@
<!-- Title for the 'Bounce keys' preference switch. [CHAR LIMIT=35] -->
<string name="bounce_keys">Bounce keys</string>
<!-- Summary text for the 'Bounce keys' preference sub-screen. [CHAR LIMIT=300] -->
<string name="bounce_keys_summary">The keyboard ignores quickly repeated presses of the same key </string>
<string name="bounce_keys_summary">Ignore quickly repeated presses of the same key</string>
<!-- Title for the 'Bounce keys' threshold dialog. [CHAR LIMIT=35] -->
<string name="bounce_keys_dialog_title">Bounce key threshold</string>
<string name="bounce_keys_dialog_title">Bounce key delay</string>
<!-- Subtitle for the 'Bounce keys' threshold dialog. [CHAR LIMIT=300] -->
<string name="bounce_keys_dialog_subtitle">Choose the duration of time your keyboard ignores repeated key presses</string>
<string name="bounce_keys_dialog_subtitle">Choose how long the keyboard ignores repeated keystrokes</string>
<!-- Option title for the input setting keys threshold dialog for 200 millisecond. [CHAR LIMIT=35] -->
<string name="input_setting_keys_dialog_option_200">0.2s</string>
<string name="input_setting_keys_dialog_option_200">0.2 seconds</string>
<!-- Option title for the input setting keys threshold dialog for 400 millisecond. [CHAR LIMIT=35] -->
<string name="input_setting_keys_dialog_option_400">0.4s</string>
<string name="input_setting_keys_dialog_option_400">0.4 seconds</string>
<!-- Option title for the input setting keys threshold dialog for 600 millisecond. [CHAR LIMIT=35] -->
<string name="input_setting_keys_dialog_option_600">0.6s</string>
<string name="input_setting_keys_dialog_option_600">0.6 seconds</string>
<!-- Option title for the input setting keys threshold dialog for custom value. [CHAR LIMIT=35] -->
<string name="input_setting_keys_custom_title">Custom</string>
<!-- Option subtitle for the input setting keys threshold dialog for custom value. [CHAR LIMIT=35] -->
@@ -4599,7 +4599,7 @@
<!-- Title for the 'Slow keys' preference switch. [CHAR LIMIT=35] -->
<string name="slow_keys">Slow keys</string>
<!-- Summary text for the 'Slow keys' preference sub-screen. [CHAR LIMIT=300] -->
<string name="slow_keys_summary">Adjusts the time it takes for a key press to activate</string>
<string name="slow_keys_summary">Change how long you need to hold down a key before it\'s registered</string>
<!-- Title for the 'Sticky keys' preference switch. [CHAR LIMIT=35] -->
<string name="sticky_keys">Sticky keys</string>
<!-- Summary text for the 'Sticky keys' preference sub-screen. [CHAR LIMIT=300] -->
@@ -4650,6 +4650,8 @@
<string name="trackpad_mouse_settings">Touchpad &amp; mouse</string>
<!-- Title for the button to trigger the 'mouse settings' page if connect with a mouse. [CHAR LIMIT=35] -->
<string name="mouse_settings">Mouse</string>
<!-- Summary text for mouse setting page. [CHAR LIMIT=35] -->
<string name="mouse_settings_summary">Pointer speed, swap buttons, button customisation</string>
<!-- Summary text for the 'trackpad settings' page. [CHAR LIMIT=100] -->
<string name="trackpad_settings_summary">Pointer speed, gestures</string>
@@ -5395,6 +5397,10 @@
<string name="accessibility_toggle_high_text_contrast_preference_title">High contrast text</string>
<!-- Summary for the accessibility preference to high contrast text. [CHAR LIMIT=NONE] -->
<string name="accessibility_toggle_high_text_contrast_preference_summary">Change text color to black or white. Maximizes contrast with the background.</string>
<!-- Content for the notification to high contrast text. [CHAR LIMIT=NONE] -->
<string name="accessibility_notification_high_contrast_text_content">High contrast text has a new look and feel.</string>
<!-- Action for the notification to high contrast text. [CHAR LIMIT=35] -->
<string name="accessibility_notification_high_contrast_text_action">Open Settings</string>
<!-- Title for the accessibility preference to high contrast text. [CHAR LIMIT=35] -->
<string name="accessibility_toggle_maximize_text_contrast_preference_title">Maximize text contrast</string>
<!-- Summary for the accessibility preference to high contrast text. [CHAR LIMIT=NONE] -->
@@ -8806,13 +8812,13 @@
<!-- Configure notifications: Value for lockscreen notifications: show all notifications [CHAR LIMIT=60] -->
<string name="lock_screen_notifs_show_full_list">Full list</string>
<!-- Configure notifications: Summary for lockscreen notifications: show all notifications [CHAR LIMIT=100] -->
<!-- Configure notifications: Summary for lockscreen notifications: show all notifications [CHAR LIMIT=NONE] -->
<string name="lock_screen_notifs_full_list_desc">The current default placement is a full shelf and notification stack.</string>
<!-- Configure notifications: Value for lockscreen notifications: show compact notifications (minimalism on) [CHAR LIMIT=60] -->
<string name="lock_screen_notifs_show_compact">Compact</string>
<!-- Configure notifications: Summary for lockscreen notifications: show compact notifications (minimalism on) [CHAR LIMIT=60] -->
<!-- Configure notifications: Summary for lockscreen notifications: show compact notifications (minimalism on) [CHAR LIMIT=NONE] -->
<string name="lock_screen_notifs_compact_desc">New notifications are collapsed into a shelf on your lockscreen.</string>
<!-- Configure notifications: Title for determining which notifications appear on the lock screen [CHAR LIMIT=60] -->
@@ -8877,16 +8883,16 @@
<!-- Security > Choose PIN/PW/Pattern > Notification redaction interstitial: Message asking the user how they want their profile notifications to appear when the device is locked [CHAR LIMIT=NONE] -->
<string name="lock_screen_notifications_interstitial_message_profile">When your device is locked, how do you want profile notifications to show?</string>
<!-- Notification Settings > Notifications on lock screen > Title for hiding seen notifications toggle. [CHAR LIMIT=30] -->
<!-- Notification Settings > Notifications on lock screen > Title for hiding seen notifications toggle. [CHAR LIMIT=60] -->
<string name="lock_screen_notification_hide_seen_title">Hide seen notifications</string>
<!-- Notification Settings > Notifications on lock screen > Summary for hiding seen notifications toggle. [CHAR LIMIT=30] -->
<!-- Notification Settings > Notifications on lock screen > Summary for hiding seen notifications toggle. [CHAR LIMIT=100] -->
<string name="lock_screen_notification_hide_seen_summary">Seen notifications are removed from the lock screen.</string>
<!-- Notification Settings > Notifications on lock screen > Title for hiding silent notifications toggle. [CHAR LIMIT=30] -->
<!-- Notification Settings > Notifications on lock screen > Title for hiding silent notifications toggle. [CHAR LIMIT=60] -->
<string name="lock_screen_notification_hide_silent_title">Hide silent notifications</string>
<!-- Notification Settings > Notifications on lock screen > Summary for hiding seen notifications toggle. [CHAR LIMIT=30] -->
<!-- Notification Settings > Notifications on lock screen > Summary for hiding silent notifications toggle. [CHAR LIMIT=100] -->
<string name="lock_screen_notification_hide_silent_summary">Silent notifications and conversations are removed from the lock screen.</string>
<!-- Security > Choose PIN/PW/Pattern > Notification redaction interstitial: Title for the screen asking the user how they want their profile notifications to appear when the device is locked [CHAR LIMIT=30] -->

View File

@@ -25,7 +25,7 @@
android:key="preview"
android:selectable="false"/>
<com.android.settings.widget.LabeledSeekBarPreference
<com.android.settings.accessibility.AccessibilitySeekBarPreference
android:key="font_size"
android:summary="@string/short_summary_font_size"
android:title="@string/title_font_size"
@@ -35,7 +35,7 @@
settings:iconStartContentDescription="@string/font_size_make_smaller_desc"
settings:keywords="@string/keywords_font_size" />
<com.android.settings.widget.LabeledSeekBarPreference
<com.android.settings.accessibility.AccessibilitySeekBarPreference
android:key="display_size"
android:summary="@string/screen_zoom_short_summary"
android:title="@string/screen_zoom_title"

View File

@@ -46,6 +46,24 @@
android:fragment="com.android.settings.inputmethod.TouchpadAndMouseSettings"
settings:controller="com.android.settings.inputmethod.TouchpadAndMouseSettingsController"/>
<Preference
android:key="touchpad_settings"
android:title="@string/trackpad_settings"
android:summary="@string/trackpad_settings_summary"
android:icon="@drawable/ic_settings_trackpad"
android:order="-253"
android:fragment="com.android.settings.inputmethod.TouchpadSettingFragment"
settings:controller="com.android.settings.inputmethod.TouchpadSettingsController"/>
<Preference
android:key="mouse_settings"
android:title="@string/mouse_settings"
android:summary="@string/mouse_settings_summary"
android:icon="@drawable/ic_settings_mouse"
android:order="-252"
android:fragment="com.android.settings.inputmethod.MouseSettingFragment"
settings:controller="com.android.settings.inputmethod.MouseSettingsController"/>
<Preference
android:key="gesture_settings"
android:title="@string/gesture_preference_title"

View File

@@ -0,0 +1,66 @@
/*
* 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 android.content.Context
import android.os.Bundle
import android.os.Parcelable
import android.util.AttributeSet
import com.android.settings.widget.LabeledSeekBarPreference
/**
* Add a custom AccessibilitySeekBarPreference with tool tip window for font size and display size.
*/
open class AccessibilitySeekBarPreference(context: Context, attrs: AttributeSet?) :
LabeledSeekBarPreference(context, attrs) {
var needsQSTooltipReshow = false
private var tooltipWindow: AccessibilityQuickSettingsTooltipWindow? = null
override fun onSaveInstanceState(): Parcelable {
val state = Bundle()
state.putParcelable(null, super.onSaveInstanceState())
if (needsQSTooltipReshow || tooltipWindow?.isShowing == true) {
state.putBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW, /* value= */ true)
}
return state
}
override fun onRestoreInstanceState(state: Parcelable?) {
val bundle = state as Bundle
super.onRestoreInstanceState(bundle.getParcelable(null, Parcelable::class.java))
if (bundle.containsKey(KEY_SAVED_QS_TOOLTIP_RESHOW)) {
needsQSTooltipReshow = bundle.getBoolean(KEY_SAVED_QS_TOOLTIP_RESHOW)
}
}
/** To generate a tooltip window and return it. */
fun createTooltipWindow(): AccessibilityQuickSettingsTooltipWindow =
AccessibilityQuickSettingsTooltipWindow(context).also { tooltipWindow = it }
/** To dismiss the tooltip window. */
fun dismissTooltip() {
val tooltip = tooltipWindow
if (tooltip?.isShowing == true) {
tooltip.dismiss()
tooltipWindow = null
}
}
companion object {
private const val KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow"
}
}

View File

@@ -0,0 +1,158 @@
/*
* 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 android.annotation.IntDef;
import android.app.Notification;
import android.app.NotificationChannel;
import android.app.NotificationManager;
import android.app.PendingIntent;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.provider.Settings;
import android.util.Log;
import androidx.annotation.NonNull;
import com.android.graphics.hwui.flags.Flags;
import com.android.settings.R;
import com.google.common.annotations.VisibleForTesting;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/**
* Handling smooth migration to the new high contrast text appearance
*/
public class HighContrastTextMigrationReceiver extends BroadcastReceiver {
private static final String TAG = HighContrastTextMigrationReceiver.class.getSimpleName();
@VisibleForTesting
static final String NOTIFICATION_CHANNEL = "high_contrast_text_notification_channel";
@VisibleForTesting
static final String ACTION_RESTORED =
"com.android.settings.accessibility.ACTION_HIGH_CONTRAST_TEXT_RESTORED";
@VisibleForTesting
static final int NOTIFICATION_ID = 1;
@Retention(RetentionPolicy.SOURCE)
@IntDef({
PromptState.UNKNOWN,
PromptState.PROMPT_SHOWN,
PromptState.PROMPT_UNNECESSARY,
})
public @interface PromptState {
int UNKNOWN = 0;
int PROMPT_SHOWN = 1;
int PROMPT_UNNECESSARY = 2;
}
@Override
public void onReceive(@NonNull Context context, @NonNull Intent intent) {
if (!Flags.highContrastTextSmallTextRect()) {
return;
}
if (ACTION_RESTORED.equals(intent.getAction())) {
Log.i(TAG, "HCT attempted to be restored from backup; showing notification for userId: "
+ context.getUserId());
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
PromptState.PROMPT_SHOWN);
showNotification(context);
} else if (Intent.ACTION_PRE_BOOT_COMPLETED.equals(intent.getAction())) {
final boolean hasSeenPromptIfNecessary = Settings.Secure.getInt(
context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS, PromptState.UNKNOWN)
!= PromptState.UNKNOWN;
if (hasSeenPromptIfNecessary) {
Log.i(TAG, "Has seen HCT prompt if necessary; skip HCT migration for userId: "
+ context.getUserId());
return;
}
final boolean isHctEnabled = Settings.Secure.getInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, OFF) == ON;
if (isHctEnabled) {
Log.i(TAG, "HCT enabled before OTA update; performing migration for userId: "
+ context.getUserId());
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED,
OFF);
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
PromptState.PROMPT_SHOWN);
showNotification(context);
} else {
Log.i(TAG,
"HCT was not enabled before OTA update; not performing migration for "
+ "userId: " + context.getUserId());
Settings.Secure.putInt(context.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
PromptState.PROMPT_UNNECESSARY);
}
}
}
private void showNotification(Context context) {
Notification.Builder notificationBuilder = new Notification.Builder(context,
NOTIFICATION_CHANNEL)
.setSmallIcon(R.drawable.ic_settings_24dp)
.setContentTitle(context.getString(
R.string.accessibility_toggle_high_text_contrast_preference_title))
.setContentText(context.getString(
R.string.accessibility_notification_high_contrast_text_content))
.setAutoCancel(true);
Intent settingsIntent = new Intent(Settings.ACTION_TEXT_READING_SETTINGS);
settingsIntent.setPackage(context.getPackageName());
if (settingsIntent.resolveActivity(context.getPackageManager()) != null) {
Bundle fragmentArgs = new Bundle();
fragmentArgs.putString(EXTRA_FRAGMENT_ARG_KEY,
TextReadingPreferenceFragment.HIGH_TEXT_CONTRAST_KEY);
settingsIntent.putExtra(EXTRA_SHOW_FRAGMENT_ARGUMENTS, fragmentArgs);
PendingIntent settingsPendingIntent = PendingIntent.getActivity(context,
/* requestCode = */ 0, settingsIntent, PendingIntent.FLAG_IMMUTABLE);
Notification.Action settingsAction = new Notification.Action.Builder(
/* icon= */ null,
context.getString(
R.string.accessibility_notification_high_contrast_text_action),
settingsPendingIntent
).build();
notificationBuilder.addAction(settingsAction);
}
NotificationManager notificationManager =
context.getSystemService(NotificationManager.class);
NotificationChannel notificationChannel = new NotificationChannel(
NOTIFICATION_CHANNEL,
context.getString(
R.string.accessibility_toggle_high_text_contrast_preference_title),
NotificationManager.IMPORTANCE_LOW);
notificationManager.createNotificationChannel(notificationChannel);
notificationManager.notify(NOTIFICATION_ID, notificationBuilder.build());
}
}

View File

@@ -22,6 +22,7 @@ import android.provider.Settings;
import androidx.preference.PreferenceScreen;
import androidx.preference.TwoStatePreference;
import com.android.graphics.hwui.flags.Flags;
import com.android.settings.R;
import com.android.settings.accessibility.TextReadingPreferenceFragment.EntryPoint;
import com.android.settings.core.TogglePreferenceController;
@@ -60,6 +61,20 @@ public class HighTextContrastPreferenceController extends TogglePreferenceContro
isChecked ? 1 : 0,
AccessibilityStatsLogUtils.convertToEntryPoint(mEntryPoint));
if (Flags.highContrastTextSmallTextRect()) {
// Set PROMPT_UNNECESSARY when the user modifies the HighContrastText setting
// This is needed for the following scenario:
// On Android 16, create secondary user, ACTION_PRE_BOOT_COMPLETED won't be sent to
// the secondary user. The user enables HCT.
// When updating OS to Android 17, ACTION_PRE_BOOT_COMPLETED will be sent to the
// secondary user when switch to the secondary user.
// If the prompt status is not updated in Android 16, we would automatically disable
// HCT and show the HCT prompt, which is an undesired behavior.
Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HCT_RECT_PROMPT_STATUS,
HighContrastTextMigrationReceiver.PromptState.PROMPT_UNNECESSARY);
}
return Settings.Secure.putInt(mContext.getContentResolver(),
Settings.Secure.ACCESSIBILITY_HIGH_TEXT_CONTRAST_ENABLED, (isChecked ? 1 : 0));
}

View File

@@ -19,7 +19,6 @@ package com.android.settings.accessibility;
import android.app.Activity;
import android.content.ComponentName;
import android.content.Context;
import android.os.Bundle;
import android.os.Handler;
import android.widget.SeekBar;
@@ -28,31 +27,27 @@ import androidx.preference.PreferenceScreen;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.widget.LabeledSeekBarPreference;
import com.android.settingslib.core.lifecycle.LifecycleObserver;
import com.android.settingslib.core.lifecycle.events.OnCreate;
import com.android.settingslib.core.lifecycle.events.OnDestroy;
import com.android.settingslib.core.lifecycle.events.OnSaveInstanceState;
import com.android.settingslib.core.lifecycle.events.OnStart;
import com.android.settingslib.core.lifecycle.events.OnStop;
import com.google.android.setupcompat.util.WizardManagerHelper;
import java.util.Optional;
/**
* The controller of {@link LabeledSeekBarPreference} that listens to display size and font size
* settings changes and updates preview size threshold smoothly.
* The controller of {@link AccessibilitySeekBarPreference} that listens to display size and font
* size settings changes and updates preview size threshold smoothly.
*/
abstract class PreviewSizeSeekBarController extends BasePreferenceController implements
TextReadingResetController.ResetStateListener, LifecycleObserver, OnCreate,
OnDestroy, OnSaveInstanceState {
TextReadingResetController.ResetStateListener, LifecycleObserver, OnStart, OnStop,
OnDestroy {
private final PreviewSizeData<? extends Number> mSizeData;
private static final String KEY_SAVED_QS_TOOLTIP_RESHOW = "qs_tooltip_reshow";
private boolean mSeekByTouch;
private Optional<ProgressInteractionListener> 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. */

View File

@@ -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;

View File

@@ -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 {

View File

@@ -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 {

View File

@@ -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<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> 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<BluetoothLeBroadcastReceiveState> getAllPresentSources() {

View File

@@ -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);
}
}

View File

@@ -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<BluetoothLeBroadcastMetadata> 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<BluetoothLeBroadcastMetadata> 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<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> sources =
mAudioStreamsHelper.getAllSourcesByDevice();
Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> 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<BluetoothLeBroadcastReceiveState> 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<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>>
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<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> getConnectedSources(
Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> sources) {
return sources.entrySet().stream()
.filter(
entry ->
entry.getValue().stream().anyMatch(AudioStreamsHelper::isConnected))
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
}
private Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> getPresentSources(
Map<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> sources) {
return sources.entrySet().stream()
.filter(
entry ->
entry.getValue().stream()
.anyMatch(AudioStreamsHelper::hasSourcePresent))
.collect(toMap(Map.Entry::getKey, Map.Entry::getValue));
}
private Optional<BluetoothLeBroadcastMetadata> 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<BluetoothLeBroadcastMetadata> 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!");

View File

@@ -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;
}
}

View File

@@ -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:

View File

@@ -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

View File

@@ -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<MetadataResult, Exception>
) {
// 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<GetValueResult, Exception>
) {
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<SetValueResult, Exception>
) {
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
}
}

View File

@@ -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<PreferenceWithScreen>()
// 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

View File

@@ -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 */
}

View File

@@ -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());
}
/**

View File

@@ -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);
}
/**

View File

@@ -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,

View File

@@ -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);
}
}

View File

@@ -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();

View File

@@ -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

View File

@@ -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());
}
}

View File

@@ -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());
}
}

View File

@@ -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

View File

@@ -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<BluetoothLeBroadcastReceiveState> 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<BluetoothLeBroadcastReceiveState> 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<AudioStreamPreference> preference =

View File

@@ -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<BluetoothDevice, List<BluetoothLeBroadcastReceiveState>> getAllSourcesByDevice() {
return sMockHelper.getAllSourcesByDevice();
}
@Implementation
public List<BluetoothLeBroadcastReceiveState> getAllPresentSources() {
return sMockHelper.getAllPresentSources();

View File

@@ -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());

View File

@@ -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()

View File

@@ -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<Runnable>()
private val cipher = mock<Cipher>()
private var mockKeyguardManager = mock<KeyguardManager>()
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<Key>())
try {
WifiDppUtils.showLockScreenForWifiSharing(context, runnable)
} catch (_: Exception) {}
verify(runnable, never()).run()
}
}