Snap for 11399958 from 5eccc6327b to 24Q2-release

Change-Id: Ib385b1d03c111cb2786bde8b0be3d0fcb9052427
This commit is contained in:
Android Build Coastguard Worker
2024-02-03 02:20:55 +00:00
38 changed files with 1070 additions and 388 deletions

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -32,6 +32,7 @@ import android.content.IntentFilter;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.Flags;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
@@ -434,10 +435,17 @@ public class AppButtonsPreferenceController extends BasePreferenceController imp
// No preferred default, so permit uninstall only when
// there is more than one candidate
enabled = (mHomePackages.size() > 1);
} else {
// There is an explicit default home app -- forbid uninstall of
// that one, but permit it for installed-but-inactive ones.
enabled = !mPackageInfo.packageName.equals(currentDefaultHome.getPackageName());
} else if (mPackageInfo.packageName.equals(currentDefaultHome.getPackageName())) {
if (Flags.improveHomeAppBehavior()) {
// Allow uninstallation of current home app if it is a non-system app
// and/or there are other candidate apps available.
if (mPackageInfo.applicationInfo.isSystemApp()
|| mHomePackages.size() == 1) {
enabled = false;
}
} else {
enabled = false;
}
}
}
}

View File

@@ -40,6 +40,7 @@ import com.android.settings.biometrics.fingerprint.FingerprintEnrollEnrolling;
import com.android.settings.core.InstrumentedActivity;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.activityembedding.ActivityEmbeddingUtils;
import com.android.systemui.unfold.compat.ScreenSizeFoldProvider;
import com.android.systemui.unfold.updates.FoldProvider;
@@ -173,6 +174,14 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
mUserId = getIntent().getIntExtra(Intent.EXTRA_USER_ID, UserHandle.myUserId());
mPostureGuidanceIntent = FeatureFactory.getFeatureFactory()
.getFaceFeatureProvider().getPostureGuidanceIntent(getApplicationContext());
// Remove the existing split screen dialog.
BiometricsSplitScreenDialog dialog =
(BiometricsSplitScreenDialog) getSupportFragmentManager()
.findFragmentByTag(BiometricsSplitScreenDialog.class.getName());
if (dialog != null) {
getSupportFragmentManager().beginTransaction().remove(dialog).commit();
}
}
@Override
@@ -338,4 +347,8 @@ public abstract class BiometricEnrollBase extends InstrumentedActivity {
final ColorStateList stateList = Utils.getColorAttr(this, android.R.attr.windowBackground);
return stateList != null ? stateList.getDefaultColor() : Color.TRANSPARENT;
}
protected boolean shouldShowSplitScreenDialog() {
return isInMultiWindowMode() && !ActivityEmbeddingUtils.isActivityEmbedded(this);
}
}

View File

@@ -154,6 +154,12 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (shouldShowSplitScreenDialog()) {
BiometricsSplitScreenDialog
.newInstance(getModality(), !WizardManagerHelper.isAnySetupWizard(getIntent()))
.show(getSupportFragmentManager(), BiometricsSplitScreenDialog.class.getName());
}
if (savedInstanceState != null) {
mConfirmingCredentials = savedInstanceState.getBoolean(KEY_CONFIRMING_CREDENTIALS);
mHasScrolledToBottom = savedInstanceState.getBoolean(KEY_SCROLLED_TO_BOTTOM);
@@ -293,6 +299,13 @@ public abstract class BiometricEnrollIntroduction extends BiometricEnrollBase
@Override
protected void onNextButtonClick(View view) {
// If it's not on suw, this method shouldn't be accessed.
if (shouldShowSplitScreenDialog() && WizardManagerHelper.isAnySetupWizard(getIntent())) {
BiometricsSplitScreenDialog.newInstance(getModality(), false /*destroyActivity*/)
.show(getSupportFragmentManager(), BiometricsSplitScreenDialog.class.getName());
return;
}
mNextClicked = true;
if (checkMaxEnrolled() == 0) {
// Lock thingy is already set up, launch directly to the next page

View File

@@ -97,6 +97,14 @@ public abstract class BiometricsEnrollEnrolling extends BiometricEnrollBase
}
public void startEnrollment() {
// If it's in multi window mode, dialog is shown, do not start enrollment.
if (shouldShowSplitScreenDialog()) {
return;
}
startEnrollmentInternal();
}
protected void startEnrollmentInternal() {
mSidecar = (BiometricEnrollSidecar) getSupportFragmentManager()
.findFragmentByTag(TAG_SIDECAR);
if (mSidecar == null) {

View File

@@ -18,6 +18,8 @@ package com.android.settings.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static com.android.settings.biometrics.BiometricEnrollBase.RESULT_SKIP;
import android.app.Dialog;
import android.app.settings.SettingsEnums;
import android.content.DialogInterface;
@@ -34,23 +36,33 @@ import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
*/
public class BiometricsSplitScreenDialog extends InstrumentedDialogFragment {
private static final String KEY_BIOMETRICS_MODALITY = "biometrics_modality";
private static final String KEU_DESTROY_ACTIVITY = "destroy_activity";
@BiometricAuthenticator.Modality
private int mBiometricsModality;
private boolean mDestroyActivity;
/** Returns the new instance of the class */
/**
* Returns the new instance of the class
* @param biometricsModality Biometric modality.
* @param destroyActivity Whether to destroy the activity
* @return the current {@link BiometricsSplitScreenDialog}
*/
public static BiometricsSplitScreenDialog newInstance(
@BiometricAuthenticator.Modality int biometricsModality) {
@BiometricAuthenticator.Modality int biometricsModality, boolean destroyActivity) {
final BiometricsSplitScreenDialog dialog = new BiometricsSplitScreenDialog();
final Bundle args = new Bundle();
args.putInt(KEY_BIOMETRICS_MODALITY, biometricsModality);
args.putBoolean(KEU_DESTROY_ACTIVITY, destroyActivity);
dialog.setArguments(args);
dialog.setCancelable(false);
return dialog;
}
@Override
public Dialog onCreateDialog(Bundle savedInstanceState) {
mBiometricsModality = getArguments().getInt(KEY_BIOMETRICS_MODALITY);
mDestroyActivity = getArguments().getBoolean(KEU_DESTROY_ACTIVITY);
int titleId;
int messageId;
switch (mBiometricsModality) {
@@ -65,9 +77,16 @@ public class BiometricsSplitScreenDialog extends InstrumentedDialogFragment {
AlertDialog.Builder builder = new AlertDialog.Builder(getActivity());
builder.setTitle(titleId)
.setMessage(messageId)
.setCancelable(false)
.setPositiveButton(
R.string.biometric_settings_add_biometrics_in_split_mode_ok,
(DialogInterface.OnClickListener) (dialog, which) -> dialog.dismiss());
(DialogInterface.OnClickListener) (dialog, which) -> {
dialog.dismiss();
if (mDestroyActivity) {
getActivity().setResult(RESULT_SKIP);
getActivity().finish();
}
});
return builder.create();
}

View File

@@ -16,8 +16,6 @@
package com.android.settings.biometrics.combination;
import static android.app.Activity.RESULT_OK;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static com.android.settings.password.ChooseLockPattern.RESULT_FINISHED;
@@ -48,12 +46,10 @@ import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.core.SettingsBaseActivity;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.password.ChooseLockGeneric;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settingslib.activityembedding.ActivityEmbeddingUtils;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.transition.SettingsTransitionHelper;
@@ -167,18 +163,6 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
// since FingerprintSettings and FaceSettings revoke the challenge when finishing.
if (getFacePreferenceKey().equals(key)) {
mDoNotFinishActivity = true;
// If it's split mode and there is no enrolled face, show the dialog. (if there is
// enrolled face, FaceSettingsEnrollButtonPreferenceController#onClick will handle
// the dialog)
if (getActivity().isInMultiWindowMode() && !ActivityEmbeddingUtils.isActivityEmbedded(
getActivity()) && !mFaceManager.hasEnrolledTemplates(mUserId)) {
BiometricsSplitScreenDialog.newInstance(TYPE_FACE).show(
getActivity().getSupportFragmentManager(),
BiometricsSplitScreenDialog.class.getName());
return true;
}
mFaceManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
final Activity activity = getActivity();
if (activity == null || activity.isFinishing()) {
@@ -209,18 +193,6 @@ public abstract class BiometricsSettingsBase extends DashboardFragment {
return true;
} else if (getFingerprintPreferenceKey().equals(key)) {
mDoNotFinishActivity = true;
// If it's split mode and there is no enrolled fingerprint, show the dialog. (if
// there is enrolled fingerprint, FingerprintSettingsFragment#onPreferenceTreeClick
// will handle the dialog)
if (getActivity().isInMultiWindowMode() && !ActivityEmbeddingUtils.isActivityEmbedded(
getActivity()) && !mFingerprintManager.hasEnrolledFingerprints(mUserId)) {
BiometricsSplitScreenDialog.newInstance(TYPE_FINGERPRINT).show(
getActivity().getSupportFragmentManager(),
BiometricsSplitScreenDialog.class.getName());
return true;
}
mFingerprintManager.generateChallenge(mUserId, (sensorId, userId, challenge) -> {
final Activity activity = getActivity();
if (activity == null || activity.isFinishing()) {

View File

@@ -16,6 +16,8 @@
package com.android.settings.biometrics.face;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import android.app.settings.SettingsEnums;
import android.content.Intent;
import android.hardware.face.FaceManager;
@@ -33,6 +35,7 @@ import com.android.settings.biometrics.BiometricEnrollSidecar;
import com.android.settings.biometrics.BiometricErrorDialog;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.slices.CustomSliceRegistry;
import com.google.android.setupcompat.template.FooterBarMixin;
@@ -88,6 +91,10 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (shouldShowSplitScreenDialog()) {
BiometricsSplitScreenDialog.newInstance(TYPE_FACE, true /*destroyActivity*/)
.show(getSupportFragmentManager(), BiometricsSplitScreenDialog.class.getName());
}
setContentView(R.layout.face_enroll_enrolling);
setHeaderText(R.string.security_settings_face_enroll_repeat_title);
mErrorText = findViewById(R.id.error_text);
@@ -134,7 +141,7 @@ public class FaceEnrollEnrolling extends BiometricsEnrollEnrolling {
}
@Override
public void startEnrollment() {
protected void startEnrollmentInternal() {
super.startEnrollment();
mPreviewFragment = (FaceEnrollPreviewFragment) getSupportFragmentManager()
.findFragmentByTag(TAG_FACE_PREVIEW);

View File

@@ -18,7 +18,6 @@ package com.android.settings.biometrics.face;
import static android.app.Activity.RESULT_OK;
import static android.app.admin.DevicePolicyResources.Strings.Settings.FACE_SETTINGS_FOR_WORK_TITLE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static com.android.settings.biometrics.BiometricEnrollBase.CONFIRM_REQUEST;
import static com.android.settings.biometrics.BiometricEnrollBase.ENROLL_REQUEST;
@@ -43,12 +42,10 @@ import com.android.settings.SettingsActivity;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settings.overlay.FeatureFactory;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.activityembedding.ActivityEmbeddingUtils;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.widget.LayoutPreference;
@@ -104,26 +101,8 @@ public class FaceSettings extends DashboardFragment {
mEnrollButton.setVisible(true);
};
private final FaceSettingsEnrollButtonPreferenceController.Listener mEnrollListener =
new FaceSettingsEnrollButtonPreferenceController.Listener() {
@Override
public boolean onShowSplitScreenDialog() {
if (getActivity().isInMultiWindowMode()
&& !ActivityEmbeddingUtils.isActivityEmbedded(getActivity())) {
// If it's in split mode, show the error dialog.
BiometricsSplitScreenDialog.newInstance(TYPE_FACE).show(
getActivity().getSupportFragmentManager(),
BiometricsSplitScreenDialog.class.getName());
return true;
}
return false;
}
@Override
public void onStartEnrolling(Intent intent) {
FaceSettings.this.startActivityForResult(intent, ENROLL_REQUEST);
}
};
private final FaceSettingsEnrollButtonPreferenceController.Listener mEnrollListener = intent ->
startActivityForResult(intent, ENROLL_REQUEST);
/**
* @param context

View File

@@ -75,11 +75,6 @@ public class FaceSettingsEnrollButtonPreferenceController extends BasePreference
@Override
public void onClick(View v) {
// If it's in multi window mode, do not start the introduction intent.
if (mListener != null && mListener.onShowSplitScreenDialog()) {
return;
}
mIsClicked = true;
final Intent intent = new Intent();
intent.setClassName(SETTINGS_PACKAGE_NAME, FaceEnrollIntroduction.class.getName());
@@ -120,12 +115,6 @@ public class FaceSettingsEnrollButtonPreferenceController extends BasePreference
* Interface for registering callbacks related to the face enroll preference button.
*/
public interface Listener {
/**
* Called to check whether to show dialog in split screen mode
* @return Whether split screen warning dialog shown.
*/
boolean onShowSplitScreenDialog();
/**
* Called when the user has indicated an intent to begin enrolling a new face.
* @param intent The Intent that should be used to launch face enrollment.

View File

@@ -16,6 +16,7 @@
package com.android.settings.biometrics.fingerprint;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static android.text.Layout.HYPHENATION_FREQUENCY_NONE;
import android.animation.Animator;
@@ -67,6 +68,7 @@ import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollSidecar;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsEnrollEnrolling;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.biometrics.fingerprint.feature.SfpsEnrollmentFeature;
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
import com.android.settings.flags.Flags;
@@ -223,7 +225,10 @@ public class FingerprintEnrollEnrolling extends BiometricsEnrollEnrolling {
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (shouldShowSplitScreenDialog()) {
BiometricsSplitScreenDialog.newInstance(TYPE_FINGERPRINT, true /*destroyActivity*/)
.show(getSupportFragmentManager(), BiometricsSplitScreenDialog.class.getName());
}
if (savedInstanceState != null) {
restoreSavedState(savedInstanceState);
}

View File

@@ -20,7 +20,6 @@ package com.android.settings.biometrics.fingerprint;
import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRINT_UNLOCK_DISABLED_EXPLANATION;
import static android.app.admin.DevicePolicyResources.Strings.Settings.WORK_PROFILE_FINGERPRINT_LAST_DELETE_MESSAGE;
import static android.app.admin.DevicePolicyResources.UNDEFINED;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
import static com.android.settings.biometrics.BiometricEnrollBase.EXTRA_FROM_SETTINGS_SUMMARY;
@@ -68,7 +67,6 @@ import com.android.settings.SubSettings;
import com.android.settings.Utils;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.biometrics.GatekeeperPasswordProvider;
import com.android.settings.biometrics2.ui.model.EnrollmentRequest;
import com.android.settings.biometrics2.ui.view.FingerprintEnrollmentActivity;
@@ -84,7 +82,6 @@ import com.android.settingslib.RestrictedLockUtils;
import com.android.settingslib.RestrictedLockUtils.EnforcedAdmin;
import com.android.settingslib.RestrictedLockUtilsInternal;
import com.android.settingslib.RestrictedSwitchPreference;
import com.android.settingslib.activityembedding.ActivityEmbeddingUtils;
import com.android.settingslib.core.AbstractPreferenceController;
import com.android.settingslib.search.SearchIndexable;
import com.android.settingslib.transition.SettingsTransitionHelper;
@@ -790,17 +787,6 @@ public class FingerprintSettings extends SubSettings {
public boolean onPreferenceTreeClick(Preference pref) {
final String key = pref.getKey();
if (KEY_FINGERPRINT_ADD.equals(key)) {
// If it's in split mode, show the error dialog and don't need to show adding
// fingerprint intent.
final boolean isActivityEmbedded = ActivityEmbeddingUtils.isActivityEmbedded(
getActivity());
if (getActivity().isInMultiWindowMode() && !isActivityEmbedded) {
BiometricsSplitScreenDialog.newInstance(TYPE_FINGERPRINT).show(
getActivity().getSupportFragmentManager(),
BiometricsSplitScreenDialog.class.getName());
return true;
}
mIsEnrolling = true;
Intent intent = new Intent();
if (FeatureFlagUtils.isEnabled(getContext(),

View File

@@ -134,6 +134,7 @@ class FingerprintEnrollmentV2Activity : FragmentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
// TODO(b/299573056): Show split screen dialog when it's in multi window mode.
setContentView(R.layout.fingerprint_v2_enroll_main)
setTheme(SetupWizardUtils.getTheme(applicationContext, intent))

View File

@@ -22,9 +22,12 @@ import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.provider.Settings;
import android.util.Log;
import android.widget.Toast;
import androidx.annotation.NonNull;
import com.android.settings.flags.Flags;
import com.android.settingslib.bluetooth.BluetoothUtils;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
@@ -113,20 +116,7 @@ public class AudioSharingUtils {
boolean filterByInSharing) {
List<CachedBluetoothDevice> orderedDevices = new ArrayList<>();
for (List<CachedBluetoothDevice> devices : groupedConnectedDevices.values()) {
CachedBluetoothDevice leadDevice = null;
for (CachedBluetoothDevice device : devices) {
if (!device.getMemberDevice().isEmpty()) {
leadDevice = device;
break;
}
}
if (leadDevice == null && !devices.isEmpty()) {
leadDevice = devices.get(0);
Log.d(
TAG,
"Empty member device, pick arbitrary device as the lead: "
+ leadDevice.getDevice().getAnonymizedAddress());
}
@Nullable CachedBluetoothDevice leadDevice = getLeadDevice(devices);
if (leadDevice == null) {
Log.d(TAG, "Skip due to no lead device");
continue;
@@ -166,6 +156,29 @@ public class AudioSharingUtils {
return orderedDevices;
}
/**
* Get the lead device from a list of devices with same group id.
*
* @param devices A list of devices with same group id.
* @return The lead device
*/
@Nullable
public static CachedBluetoothDevice getLeadDevice(
@NonNull List<CachedBluetoothDevice> devices) {
if (devices.isEmpty()) return null;
for (CachedBluetoothDevice device : devices) {
if (!device.getMemberDevice().isEmpty()) {
return device;
}
}
CachedBluetoothDevice leadDevice = devices.get(0);
Log.d(
TAG,
"No lead device in the group, pick arbitrary device as the lead: "
+ leadDevice.getDevice().getAnonymizedAddress());
return leadDevice;
}
/**
* Fetch a list of ordered connected lead {@link AudioSharingDeviceItem}s eligible for audio
* sharing. The active device is placed in the first place if it exists. The devices can be
@@ -268,7 +281,7 @@ public class AudioSharingUtils {
var groupedDevices = fetchConnectedDevicesByGroupId(manager);
var leadDevices = buildOrderedConnectedLeadDevices(manager, groupedDevices, false);
if (!leadDevices.isEmpty() && AudioSharingUtils.isActiveLeAudioDevice(leadDevices.get(0))) {
if (!leadDevices.isEmpty() && isActiveLeAudioDevice(leadDevices.get(0))) {
return Optional.of(leadDevices.get(0));
} else {
Log.w(TAG, "getActiveSinksOnAssistant(): No active lead device!");
@@ -379,4 +392,17 @@ public class AudioSharingUtils {
Log.d(TAG, "getGroupId return invalid id for device: " + anonymizedAddress);
return BluetoothCsipSetCoordinator.GROUP_ID_INVALID;
}
/** Get the fallback active group id from SettingsProvider. */
public static int getFallbackActiveGroupId(@NonNull Context context) {
return Settings.Secure.getInt(
context.getContentResolver(),
"bluetooth_le_broadcast_fallback_active_group_id",
BluetoothCsipSetCoordinator.GROUP_ID_INVALID);
}
/** Post the runnable to main thread. */
public static void postOnMainThread(@NonNull Context context, @NonNull Runnable runnable) {
context.getMainExecutor().execute(runnable);
}
}

View File

@@ -81,8 +81,12 @@ public class CallsAndAlarmsDialogFragment extends InstrumentedDialogFragment {
ArrayList<AudioSharingDeviceItem> deviceItems =
arguments.getParcelableArrayList(BUNDLE_KEY_DEVICE_ITEMS);
int checkedItem = -1;
// deviceItems is ordered. The active device is put in the first place if it does exist
if (!deviceItems.isEmpty() && deviceItems.get(0).isActive()) checkedItem = 0;
for (AudioSharingDeviceItem item : deviceItems) {
int fallbackActiveGroupId = AudioSharingUtils.getFallbackActiveGroupId(getContext());
if (item.getGroupId() == fallbackActiveGroupId) {
checkedItem = deviceItems.indexOf(item);
}
}
String[] choices =
deviceItems.stream().map(AudioSharingDeviceItem::getName).toArray(String[]::new);
AlertDialog.Builder builder =

View File

@@ -16,6 +16,12 @@
package com.android.settings.connecteddevice.audiosharing;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothCsipSetCoordinator;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastMetadata;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
import android.content.Context;
import android.util.Log;
@@ -29,6 +35,7 @@ import com.android.settings.bluetooth.Utils;
import com.android.settings.dashboard.DashboardFragment;
import com.android.settingslib.bluetooth.BluetoothCallback;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.utils.ThreadUtils;
@@ -36,6 +43,8 @@ import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/** PreferenceController to control the dialog to choose the active device for calls and alarms */
public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferenceController
@@ -45,13 +54,74 @@ public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferen
private static final String PREF_KEY = "calls_and_alarms";
private final LocalBluetoothManager mLocalBtManager;
private final Executor mExecutor;
@Nullable private LocalBluetoothLeBroadcastAssistant mAssistant = null;
private DashboardFragment mFragment;
Map<Integer, List<CachedBluetoothDevice>> mGroupedConnectedDevices = new HashMap<>();
private ArrayList<AudioSharingDeviceItem> mDeviceItemsInSharingSession = new ArrayList<>();
private BluetoothLeBroadcastAssistant.Callback mBroadcastAssistantCallback =
new BluetoothLeBroadcastAssistant.Callback() {
@Override
public void onSearchStarted(int reason) {}
@Override
public void onSearchStartFailed(int reason) {}
@Override
public void onSearchStopped(int reason) {}
@Override
public void onSearchStopFailed(int reason) {}
@Override
public void onSourceFound(@NonNull BluetoothLeBroadcastMetadata source) {}
@Override
public void onSourceAdded(@NonNull BluetoothDevice sink, int sourceId, int reason) {
Log.d(TAG, "onSourceAdded");
updatePreference();
}
@Override
public void onSourceAddFailed(
@NonNull BluetoothDevice sink,
@NonNull BluetoothLeBroadcastMetadata source,
int reason) {}
@Override
public void onSourceModified(
@NonNull BluetoothDevice sink, int sourceId, int reason) {}
@Override
public void onSourceModifyFailed(
@NonNull BluetoothDevice sink, int sourceId, int reason) {}
@Override
public void onSourceRemoved(
@NonNull BluetoothDevice sink, int sourceId, int reason) {
Log.d(TAG, "onSourceRemoved");
updatePreference();
}
@Override
public void onSourceRemoveFailed(
@NonNull BluetoothDevice sink, int sourceId, int reason) {}
@Override
public void onReceiveStateChanged(
BluetoothDevice sink,
int sourceId,
BluetoothLeBroadcastReceiveState state) {}
};
public CallsAndAlarmsPreferenceController(Context context) {
super(context, PREF_KEY);
mLocalBtManager = Utils.getLocalBtManager(mContext);
if (mLocalBtManager != null) {
mAssistant = mLocalBtManager.getProfileManager().getLeAudioBroadcastAssistantProfile();
}
mExecutor = Executors.newSingleThreadExecutor();
}
@Override
@@ -60,7 +130,7 @@ public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferen
}
@Override
public void displayPreference(PreferenceScreen screen) {
public void displayPreference(@NonNull PreferenceScreen screen) {
super.displayPreference(screen);
mPreference.setOnPreferenceClickListener(
preference -> {
@@ -69,14 +139,31 @@ public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferen
return true;
}
updateDeviceItemsInSharingSession();
if (mDeviceItemsInSharingSession.size() >= 2) {
if (mDeviceItemsInSharingSession.size() >= 1) {
CallsAndAlarmsDialogFragment.show(
mFragment,
mDeviceItemsInSharingSession,
(AudioSharingDeviceItem item) -> {
for (CachedBluetoothDevice device :
mGroupedConnectedDevices.get(item.getGroupId())) {
device.setActive();
if (!mGroupedConnectedDevices.containsKey(item.getGroupId())) {
return;
}
List<CachedBluetoothDevice> devices =
mGroupedConnectedDevices.get(item.getGroupId());
@Nullable
CachedBluetoothDevice lead =
AudioSharingUtils.getLeadDevice(devices);
if (lead != null) {
Log.d(
TAG,
"Set fallback active device: "
+ lead.getDevice().getAnonymizedAddress());
lead.setActive();
updatePreference();
} else {
Log.w(
TAG,
"Fail to set fallback active device: no lead"
+ " device");
}
});
}
@@ -90,6 +177,9 @@ public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferen
if (mLocalBtManager != null) {
mLocalBtManager.getEventManager().registerCallback(this);
}
if (mAssistant != null) {
mAssistant.registerServiceCallBack(mExecutor, mBroadcastAssistantCallback);
}
}
@Override
@@ -98,52 +188,58 @@ public class CallsAndAlarmsPreferenceController extends AudioSharingBasePreferen
if (mLocalBtManager != null) {
mLocalBtManager.getEventManager().unregisterCallback(this);
}
if (mAssistant != null) {
mAssistant.unregisterServiceCallBack(mBroadcastAssistantCallback);
}
}
@Override
public void updateVisibility() {
if (mPreference == null) return;
var unused =
ThreadUtils.postOnBackgroundThread(
() -> {
boolean isVisible = isBroadcasting() && isBluetoothStateOn();
if (!isVisible) {
ThreadUtils.postOnMainThread(() -> mPreference.setVisible(false));
} else {
updateDeviceItemsInSharingSession();
// mDeviceItemsInSharingSession is ordered. The active device is the
// first
// place if exits.
if (!mDeviceItemsInSharingSession.isEmpty()
&& mDeviceItemsInSharingSession.get(0).isActive()) {
ThreadUtils.postOnMainThread(
() -> {
mPreference.setVisible(true);
mPreference.setSummary(
mDeviceItemsInSharingSession
.get(0)
.getName());
});
} else {
ThreadUtils.postOnMainThread(
() -> {
mPreference.setVisible(true);
mPreference.setSummary(
"No active device in sharing");
});
}
}
});
var unused = ThreadUtils.postOnBackgroundThread(() -> updatePreference());
}
private void updatePreference() {
boolean isVisible = isBroadcasting() && isBluetoothStateOn();
if (!isVisible) {
AudioSharingUtils.postOnMainThread(mContext, () -> mPreference.setVisible(false));
return;
}
updateDeviceItemsInSharingSession();
int fallbackActiveGroupId = AudioSharingUtils.getFallbackActiveGroupId(mContext);
Log.d(TAG, "updatePreference: get fallback active group " + fallbackActiveGroupId);
if (fallbackActiveGroupId != BluetoothCsipSetCoordinator.GROUP_ID_INVALID) {
for (AudioSharingDeviceItem item : mDeviceItemsInSharingSession) {
if (item.getGroupId() == fallbackActiveGroupId) {
AudioSharingUtils.postOnMainThread(
mContext,
() -> {
mPreference.setSummary(item.getName());
mPreference.setVisible(true);
});
return;
}
}
}
AudioSharingUtils.postOnMainThread(
mContext,
() -> {
mPreference.setSummary("No active device in sharing");
mPreference.setVisible(true);
});
}
@Override
public void onActiveDeviceChanged(
@Nullable CachedBluetoothDevice activeDevice, int bluetoothProfile) {
if (bluetoothProfile != BluetoothProfile.LE_AUDIO) {
Log.d(TAG, "Ignore onActiveDeviceChanged, not LE_AUDIO profile");
return;
public void onProfileConnectionStateChanged(
@NonNull CachedBluetoothDevice cachedDevice,
@ConnectionState int state,
int bluetoothProfile) {
if (state == BluetoothAdapter.STATE_DISCONNECTED
&& bluetoothProfile == BluetoothProfile.LE_AUDIO) {
// The fallback active device could be updated if the previous fallback device is
// disconnected.
updatePreference();
}
mPreference.setSummary(activeDevice == null ? "" : activeDevice.getName());
}
/**

View File

@@ -34,6 +34,7 @@ import com.android.settings.accessibility.TextReadingPreferenceFragmentForSetupW
import com.android.settings.accessibility.ToggleColorInversionPreferenceFragment;
import com.android.settings.accessibility.ToggleDaltonizerPreferenceFragment;
import com.android.settings.accessibility.ToggleReduceBrightColorsPreferenceFragment;
import com.android.settings.accessibility.VibrationIntensitySettingsFragment;
import com.android.settings.accessibility.shortcuts.EditShortcutsPreferenceFragment;
import com.android.settings.accounts.AccountDashboardFragment;
import com.android.settings.accounts.AccountSyncSettings;
@@ -386,6 +387,7 @@ public class SettingsGateway {
UserAspectRatioDetails.class.getName(),
ScreenTimeoutSettings.class.getName(),
ResetNetwork.class.getName(),
VibrationIntensitySettingsFragment.class.getName(),
};
public static final String[] SETTINGS_FOR_RESTRICTED = {

View File

@@ -160,8 +160,13 @@ public class FoldLockBehaviorSettings extends RadioButtonPickerFragment implemen
IllustrationType.LOTTIE_ANIMATION);
break;
case SETTING_VALUE_SELECTIVE_STAY_AWAKE:
setIllustration(R.raw.fold_setting_selective_stay_awake_lottie,
IllustrationType.LOTTIE_ANIMATION);
if (mFoldGracePeriodProvider.isEnabled()) {
setIllustration(R.raw.fold_setting_grace_period_selective_stay_awake_lottie,
IllustrationType.LOTTIE_ANIMATION);
} else {
setIllustration(R.raw.fold_setting_selective_stay_awake_lottie,
IllustrationType.LOTTIE_ANIMATION);
}
break;
case SETTING_VALUE_SLEEP_ON_FOLD:
setIllustration(R.raw.fold_setting_sleep_on_fold_lottie,

View File

@@ -21,6 +21,7 @@ import android.content.ComponentName
import android.content.Context
import android.content.om.OverlayManager
import android.content.pm.ApplicationInfo
import android.content.pm.Flags
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.util.Log
@@ -95,7 +96,7 @@ class AppButtonRepository(private val context: Context) {
isDisallowControl(app) -> return false
uninstallDisallowedDueToHomeApp(app.packageName) -> return false
uninstallDisallowedDueToHomeApp(app) -> return false
// Resource overlays can be uninstalled iff they are public (installed on /data) and
// disabled. ("Enabled" means they are in use by resource management.)
@@ -113,7 +114,8 @@ class AppButtonRepository(private val context: Context) {
* can go to Home settings and pick a different one, after which we'll permit uninstallation
* of the now-not-default one.
*/
private fun uninstallDisallowedDueToHomeApp(packageName: String): Boolean {
fun uninstallDisallowedDueToHomeApp(applicationInfo: ApplicationInfo): Boolean {
val packageName = applicationInfo.packageName
val homePackageInfo = getHomePackageInfo()
return when {
packageName !in homePackageInfo.homePackages -> false
@@ -121,8 +123,17 @@ class AppButtonRepository(private val context: Context) {
// Disallow uninstall when this is the only home app.
homePackageInfo.homePackages.size == 1 -> true
// Disallow if this is the explicit default home app.
else -> packageName == homePackageInfo.currentDefaultHome?.packageName
packageName == homePackageInfo.currentDefaultHome?.packageName -> {
if (Flags.improveHomeAppBehavior()) {
// Disallow the uninstallation of the current home app if it is a system app.
return applicationInfo.isSystemApp()
} else {
// Disallow if this is the explicit default home app.
return true
}
}
else -> false
}
}

View File

@@ -476,9 +476,12 @@ public class UserDetailsSettings extends SettingsPreferenceFragment
private void enableCallsAndSms(boolean enabled) {
mPhonePref.setChecked(enabled);
UserHandle userHandle = UserHandle.of(mUserInfo.id);
mUserManager.setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, !enabled, userHandle);
mUserManager.setUserRestriction(UserManager.DISALLOW_SMS, !enabled, userHandle);
int[] userProfiles = mUserManager.getProfileIdsWithDisabled(mUserInfo.id);
for (int userId : userProfiles) {
UserHandle user = UserHandle.of(userId);
mUserManager.setUserRestriction(UserManager.DISALLOW_OUTGOING_CALLS, !enabled, user);
mUserManager.setUserRestriction(UserManager.DISALLOW_SMS, !enabled, user);
}
}
/**

View File

@@ -27,6 +27,7 @@ import android.content.res.Resources;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.telephony.CarrierConfigManager;
import android.telephony.ServiceState;
import android.telephony.SubscriptionManager;
import android.telephony.TelephonyCallback;
import android.telephony.TelephonyManager;
@@ -49,6 +50,7 @@ import androidx.preference.PreferenceScreen;
import com.android.ims.ImsConfig;
import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.telephony.Phone;
import com.android.internal.telephony.flags.Flags;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.SettingsPreferenceFragment;
@@ -97,6 +99,7 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
private boolean mEditableWfcMode = true;
private boolean mEditableWfcRoamingMode = true;
private boolean mUseWfcHomeModeForRoaming = false;
private boolean mOverrideWfcRoamingModeWhileUsingNtn = false;
private int mSubId = SubscriptionManager.INVALID_SUBSCRIPTION_ID;
private ImsMmTelManager mImsMmTelManager;
@@ -166,7 +169,8 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
final Preference pref_roam =
getPreferenceScreen().findPreference(BUTTON_WFC_ROAMING_MODE);
if (pref_roam != null) {
pref_roam.setEnabled(isWfcRoamingModeEditable);
pref_roam.setEnabled(isWfcRoamingModeEditable
&& !overrideWfcRoamingModeWhileUsingNtn());
}
}
}
@@ -361,6 +365,9 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
false);
isWifiOnlySupported = b.getBoolean(
CarrierConfigManager.KEY_CARRIER_WFC_SUPPORTS_WIFI_ONLY_BOOL, true);
mOverrideWfcRoamingModeWhileUsingNtn = b.getBoolean(
CarrierConfigManager.KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL,
true);
}
}
@@ -577,7 +584,8 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
mButtonWfcMode.setSummary(getWfcModeSummary(wfcMode));
mButtonWfcMode.setEnabled(wfcEnabled && mEditableWfcMode);
// mButtonWfcRoamingMode.setSummary is not needed; summary is just selected value.
mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode);
mButtonWfcRoamingMode.setEnabled(wfcEnabled && mEditableWfcRoamingMode
&& !overrideWfcRoamingModeWhileUsingNtn());
final PreferenceScreen preferenceScreen = getPreferenceScreen();
final boolean updateAddressEnabled = (getCarrierActivityIntent() != null);
@@ -711,4 +719,30 @@ public class WifiCallingSettingsForSub extends SettingsPreferenceFragment
}
mProvisioningManager.unregisterProvisioningChangedCallback(mProvisioningCallback);
}
/**
* Determine whether to override roaming Wi-Fi calling preference when device is connected to
* non-terrestrial network.
*
* @return {@code true} if phone is connected to non-terrestrial network and if
* {@link CarrierConfigManager#KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL} is true,
* {@code false} otherwise.
*/
private boolean overrideWfcRoamingModeWhileUsingNtn() {
if (!Flags.carrierEnabledSatelliteFlag()) {
return false;
}
TelephonyManager tm = getTelephonyManagerForSub(mSubId);
ServiceState serviceState = tm.getServiceState();
if (serviceState == null) {
return false;
}
if (!serviceState.isUsingNonTerrestrialNetwork()) {
return false;
}
return mOverrideWfcRoamingModeWhileUsingNtn;
}
}

View File

@@ -23,6 +23,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.anyList;
import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
@@ -38,15 +39,18 @@ import static org.mockito.Mockito.when;
import android.app.ActivityManager;
import android.app.admin.DevicePolicyManager;
import android.app.settings.SettingsEnums;
import android.content.ComponentName;
import android.content.Context;
import android.content.Intent;
import android.content.om.OverlayInfo;
import android.content.om.OverlayManager;
import android.content.pm.ApplicationInfo;
import android.content.pm.Flags;
import android.content.pm.PackageInfo;
import android.content.pm.PackageManager;
import android.os.RemoteException;
import android.os.UserManager;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.util.ArraySet;
import android.view.View;
@@ -107,7 +111,7 @@ public class AppButtonsPreferenceControllerTest {
@Mock
private OverlayManager mOverlayManager;
@Mock
private PackageManager mPackageManger;
private PackageManager mPackageManager;
@Mock
private DevicePolicyManager mDpm;
@Mock
@@ -132,7 +136,7 @@ public class AppButtonsPreferenceControllerTest {
mContext = RuntimeEnvironment.application;
doReturn(mDpm).when(mSettingsActivity).getSystemService(Context.DEVICE_POLICY_SERVICE);
doReturn(mUserManager).when(mSettingsActivity).getSystemService(Context.USER_SERVICE);
doReturn(mPackageManger).when(mSettingsActivity).getPackageManager();
doReturn(mPackageManager).when(mSettingsActivity).getPackageManager();
doReturn(mAm).when(mSettingsActivity).getSystemService(Context.ACTIVITY_SERVICE);
doReturn(mOverlayManager).when(mSettingsActivity).
getSystemService(OverlayManager.class);
@@ -184,7 +188,7 @@ public class AppButtonsPreferenceControllerTest {
@Test
public void retrieveAppEntry_hasAppEntry_notNull()
throws PackageManager.NameNotFoundException {
doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(anyString(), anyInt());
mController.retrieveAppEntry();
@@ -195,7 +199,7 @@ public class AppButtonsPreferenceControllerTest {
@Test
public void retrieveAppEntry_noAppEntry_null() throws PackageManager.NameNotFoundException {
doReturn(null).when(mState).getEntry(eq(PACKAGE_NAME), anyInt());
doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(anyString(), anyInt());
mController.retrieveAppEntry();
@@ -207,7 +211,7 @@ public class AppButtonsPreferenceControllerTest {
public void retrieveAppEntry_throwException_null() throws
PackageManager.NameNotFoundException {
doReturn(mAppEntry).when(mState).getEntry(anyString(), anyInt());
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManger).getPackageInfo(
doThrow(new PackageManager.NameNotFoundException()).when(mPackageManager).getPackageInfo(
anyString(), anyInt());
mController.retrieveAppEntry();
@@ -225,7 +229,7 @@ public class AppButtonsPreferenceControllerTest {
@Test
public void updateOpenButton_haveLaunchIntent_buttonShouldBeEnable() {
doReturn(new Intent()).when(mPackageManger).getLaunchIntentForPackage(anyString());
doReturn(new Intent()).when(mPackageManager).getLaunchIntentForPackage(anyString());
mController.updateOpenButton();
@@ -346,6 +350,35 @@ public class AppButtonsPreferenceControllerTest {
verify(mButtonPrefs).setButton2Enabled(false);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
public void updateUninstallButton_isNotSystemAndIsCurrentHomeAndHasOneHome_setButtonDisable() {
doReturn(false).when(mController).isSystemPackage(any(), any(), any());
doReturn(new ComponentName(PACKAGE_NAME, "cls")).when(mPackageManager).getHomeActivities(
anyList());
mController.mHomePackages.add(PACKAGE_NAME);
mController.updateUninstallButton();
verify(mButtonPrefs).setButton2Enabled(false);
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
public void updateUninstallButton_isNotSystemAndIsCurrentHomeAndHasOtherHome_setButtonEnable() {
doReturn(false).when(mController).isSystemPackage(any(), any(), any());
doReturn(new ComponentName(PACKAGE_NAME, "cls")).when(mPackageManager).getHomeActivities(
anyList());
mController.mHomePackages.add(PACKAGE_NAME);
mController.mHomePackages.add("com.android.home.fake");
mController.updateUninstallButton();
verify(mButtonPrefs).setButton2Enabled(true);
}
@Test
public void updateUninstallButton_isSystemRro_setButtonDisable() {
mAppInfo.flags |= ApplicationInfo.FLAG_SYSTEM;
@@ -477,7 +510,7 @@ public class AppButtonsPreferenceControllerTest {
throws PackageManager.NameNotFoundException {
doReturn(AppButtonsPreferenceController.AVAILABLE)
.when(mController).getAvailabilityStatus();
doReturn(mPackageInfo).when(mPackageManger).getPackageInfo(anyString(), anyInt());
doReturn(mPackageInfo).when(mPackageManager).getPackageInfo(anyString(), anyInt());
doReturn(mButtonPrefs).when(mScreen).findPreference(anyString());
mController.displayPreference(mScreen);
mController.mButtonsPref = null;

View File

@@ -0,0 +1,126 @@
/*
* 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.biometrics;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FACE;
import static android.hardware.biometrics.BiometricAuthenticator.TYPE_FINGERPRINT;
import static com.google.common.truth.Truth.assertThat;
import android.content.DialogInterface;
import android.hardware.biometrics.BiometricAuthenticator;
import android.os.Looper;
import android.widget.Button;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.R;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import org.junit.After;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.Robolectric;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.Shadows;
import org.robolectric.annotation.Config;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = ShadowAlertDialogCompat.class)
public class BiometricsSplitScreenDialogTest {
@Rule
public final MockitoRule mocks = MockitoJUnit.rule();
private FragmentActivity mActivity;
private BiometricsSplitScreenDialog mFragment;
@Before
public void setUp() {
ShadowAlertDialogCompat.reset();
mActivity = Robolectric.buildActivity(FragmentActivity.class).setup().get();
}
@After
public void tearDown() {
ShadowAlertDialogCompat.reset();
}
@Test
public void testTexts_face() {
final AlertDialog dialog = setUpFragment(TYPE_FACE, false /*destroyActivity*/);
final ShadowAlertDialogCompat shadowAlertDialog = ShadowAlertDialogCompat.shadowOf(dialog);
assertThat(shadowAlertDialog.getTitle().toString()).isEqualTo(
mActivity.getString(R.string.biometric_settings_add_face_in_split_mode_title));
assertThat(shadowAlertDialog.getMessage().toString()).isEqualTo(
mActivity.getString(R.string.biometric_settings_add_face_in_split_mode_message));
}
@Test
public void testTexts_fingerprint() {
final AlertDialog dialog = setUpFragment(TYPE_FINGERPRINT, false /*destroyActivity*/);
final ShadowAlertDialogCompat shadowAlertDialog = ShadowAlertDialogCompat.shadowOf(dialog);
assertThat(shadowAlertDialog.getTitle().toString()).isEqualTo(
mActivity.getString(
R.string.biometric_settings_add_fingerprint_in_split_mode_title));
assertThat(shadowAlertDialog.getMessage().toString()).isEqualTo(
mActivity.getString(
R.string.biometric_settings_add_fingerprint_in_split_mode_message));
}
@Test
public void testButton_destroyActivity() {
final AlertDialog dialog = setUpFragment(TYPE_FACE, true /*destroyActivity*/);
final Button button = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
assertThat(button).isNotNull();
button.performClick();
Shadows.shadowOf(Looper.getMainLooper()).idle();
assertThat(dialog.isShowing()).isFalse();
assertThat(mActivity.isFinishing()).isTrue();
}
@Test
public void testButton_notDestroyActivity() {
final AlertDialog dialog = setUpFragment(TYPE_FACE, false /*destroyActivity*/);
final Button button = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
assertThat(button).isNotNull();
button.performClick();
Shadows.shadowOf(Looper.getMainLooper()).idle();
assertThat(dialog.isShowing()).isFalse();
assertThat(mActivity.isFinishing()).isFalse();
}
private AlertDialog setUpFragment(
@BiometricAuthenticator.Modality int biometricsModality, boolean destroyActivity) {
mFragment = BiometricsSplitScreenDialog.newInstance(biometricsModality, destroyActivity);
mFragment.show(mActivity.getSupportFragmentManager(), null);
Shadows.shadowOf(Looper.getMainLooper()).idle();
final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
return dialog;
}
}

View File

@@ -16,8 +16,6 @@
package com.android.settings.biometrics.combination;
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static com.android.settings.biometrics.combination.BiometricsSettingsBase.CONFIRM_REQUEST;
import static com.android.settings.password.ChooseLockPattern.RESULT_FINISHED;
@@ -26,7 +24,6 @@ import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doAnswer;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.doThrow;
@@ -37,11 +34,8 @@ import static org.mockito.Mockito.verify;
import android.content.Context;
import android.content.Intent;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Bundle;
import android.util.AndroidRuntimeException;
import android.view.LayoutInflater;
@@ -52,15 +46,12 @@ import androidx.annotation.NonNull;
import androidx.annotation.XmlRes;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.Preference;
import androidx.preference.PreferenceManager;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.R;
import com.android.settings.biometrics.BiometricStatusPreferenceController;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.biometrics.face.FaceStatusPreferenceController;
import com.android.settings.biometrics.fingerprint.FingerprintStatusPreferenceController;
import com.android.settings.password.ChooseLockSettingsHelper;
@@ -72,7 +63,6 @@ import com.android.settingslib.core.AbstractPreferenceController;
import org.junit.After;
import org.junit.Before;
import org.junit.Ignore;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
@@ -112,8 +102,6 @@ public class CombinedBiometricProfileSettingsTest {
private FaceStatusPreferenceController mFaceStatusPreferenceController;
@Mock
private FaceManager mFaceManager;
@Mock
private FragmentTransaction mFragmentTransaction;
@Before
public void setUp() {
@@ -350,133 +338,6 @@ public class CombinedBiometricProfileSettingsTest {
assertThat(capturedPreferences.get(0).getKey()).isEqualTo(mFragment.getFacePreferenceKey());
}
@Test
public void testClickFingerprintUnlock_inMultiWindow_withoutEnrolledFp_showsDialog() {
testClickFingerprintUnlock(true /* isInMultiWindow */, false /* hasEnrolledFingerprint */);
verifyShowsDialogAfterClickingUnlock(mFragment.getFingerprintPreferenceKey());
}
@Test
public void testClickFingerprintUnlock_inMultiWindow_withEnrolledFp_noDialog() {
testClickFingerprintUnlock(true /* isInMultiWindow */, true /* hasEnrolledFingerprint */);
verifyNoDialogAfterClickingUnlock(mFragment.getFingerprintPreferenceKey());
}
@Test
public void testClickFingerprintUnlock_inFullScreen_withoutEnrolledFp_noDialog() {
testClickFingerprintUnlock(false /* isInMultiWindow */, false /* hasEnrolledFingerprint */);
verifyNoDialogAfterClickingUnlock(mFragment.getFingerprintPreferenceKey());
}
private void testClickFingerprintUnlock(boolean isInMultiWindow,
boolean hasEnrolledFingerprint) {
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
props.add(new FingerprintSensorPropertiesInternal(
0 /* sensorId */,
SensorProperties.STRENGTH_STRONG,
1 /* maxEnrollmentsPerUser */,
new ArrayList<ComponentInfoInternal>(),
TYPE_UDFPS_OPTICAL,
true /* resetLockoutRequiresHardwareAuthToken */));
doReturn(props).when(mFingerprintManager).getSensorPropertiesInternal();
doAnswer(invocation -> {
final FingerprintManager.GenerateChallengeCallback callback =
invocation.getArgument(1);
callback.onChallengeGenerated(0, 0, 1L);
return null;
}).when(mFingerprintManager).generateChallenge(anyInt(), any());
doReturn(new byte[]{1}).when(mFragment).requestGatekeeperHat(any(), anyLong(), anyInt(),
anyLong());
FragmentManager fragmentManager = mock(FragmentManager.class);
doReturn(fragmentManager).when(mActivity).getSupportFragmentManager();
doReturn(mFragmentTransaction).when(fragmentManager).beginTransaction();
doReturn(isInMultiWindow).when(mActivity).isInMultiWindowMode();
doReturn(hasEnrolledFingerprint).when(mFingerprintManager).hasEnrolledFingerprints(
anyInt());
// Start fragment
mFragment.onAttach(mContext);
mFragment.onCreate(null);
mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
mFragment.onResume();
// User clicks on "Fingerprint Unlock"
final Preference preference = new Preference(mContext);
preference.setKey(mFragment.getFingerprintPreferenceKey());
mFragment.onPreferenceTreeClick(preference);
}
@Test
@Ignore("b/295325503")
public void testClickFaceUnlock_inMultiWindow_withoutEnrolledFp_showsDialog() {
testClickFaceUnlock(true /* isInMultiWindow */, false /*hasEnrolledFace*/);
verifyShowsDialogAfterClickingUnlock(mFragment.getFacePreferenceKey());
}
@Test
public void testClickFaceUnlock_inMultiWindow_withEnrolledFp_noDialog() {
testClickFaceUnlock(true /* isInMultiWindow */, true /* hasEnrolledFace */);
verifyNoDialogAfterClickingUnlock(mFragment.getFacePreferenceKey());
}
@Test
public void testClickFaceUnlock_inFullScreen_withoutEnrolledFp_noDialog() {
testClickFaceUnlock(false /* isInMultiWindow */ , false /* hasEnrolledFace */);
verifyNoDialogAfterClickingUnlock(mFragment.getFacePreferenceKey());
}
private void testClickFaceUnlock(boolean isInMultiWindow, boolean hasEnrolledFace) {
doAnswer(invocation -> {
final FaceManager.GenerateChallengeCallback callback =
invocation.getArgument(1);
callback.onGenerateChallengeResult(0, 0, 1L);
return null;
}).when(mFaceManager).generateChallenge(anyInt(), any());
doReturn(new byte[] { 1 }).when(mFragment).requestGatekeeperHat(any(), anyLong(), anyInt(),
anyLong());
FragmentManager fragmentManager = mock(FragmentManager.class);
doReturn(fragmentManager).when(mActivity).getSupportFragmentManager();
doReturn(mFragmentTransaction).when(fragmentManager).beginTransaction();
doReturn(isInMultiWindow).when(mActivity).isInMultiWindowMode();
doReturn(hasEnrolledFace).when(mFaceManager).hasEnrolledTemplates(
anyInt());
// Start fragment
mFragment.onAttach(mContext);
mFragment.onCreate(null);
mFragment.onCreateView(LayoutInflater.from(mContext), mock(ViewGroup.class), Bundle.EMPTY);
mFragment.onResume();
// User clicks on "Face Unlock"
final Preference preference = new Preference(mContext);
preference.setKey(mFragment.getFacePreferenceKey());
mFragment.onPreferenceTreeClick(preference);
}
private void verifyNoDialogAfterClickingUnlock(String preferenceKey) {
final BiometricStatusPreferenceController controller =
preferenceKey.equals(mFragment.getFacePreferenceKey())
? mFaceStatusPreferenceController
: mFingerprintStatusPreferenceController;
verify(controller).handlePreferenceTreeClick(mPreferenceCaptor.capture());
List<Preference> capturedPreferences = mPreferenceCaptor.getAllValues();
assertThat(capturedPreferences).hasSize(1);
assertThat(capturedPreferences.get(0).getKey()).isEqualTo(preferenceKey);
verify(mFragmentTransaction, never()).add(any(),
eq(BiometricsSplitScreenDialog.class.getName()));
}
private void verifyShowsDialogAfterClickingUnlock(String preferenceKey) {
final BiometricStatusPreferenceController controller =
preferenceKey.equals(mFragment.getFacePreferenceKey())
? mFaceStatusPreferenceController
: mFingerprintStatusPreferenceController;
verify(controller, never()).handlePreferenceTreeClick(any());
verify(mFragmentTransaction).add(any(),
eq(BiometricsSplitScreenDialog.class.getName()));
}
@Test
public void testNoCrashIfDetachActivityDuringGeneratingChallengeThroughFaceManager() {
doAnswer(invocation -> {

View File

@@ -38,6 +38,7 @@ import static org.mockito.Mockito.when;
import android.app.Activity;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.res.Configuration;
import android.content.res.Resources;
@@ -46,12 +47,15 @@ import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.face.IFaceAuthenticatorsRegisteredCallback;
import android.os.Looper;
import android.os.UserHandle;
import android.view.View;
import android.widget.Button;
import android.widget.TextView;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog;
import androidx.test.core.app.ApplicationProvider;
import com.android.internal.widget.LockPatternUtils;
@@ -62,6 +66,7 @@ import com.android.settings.biometrics.BiometricUtils;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.SettingsShadowResources;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowDevicePolicyManager;
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
import com.android.settings.testutils.shadow.ShadowSensorPrivacyManager;
@@ -101,7 +106,8 @@ import java.util.List;
ShadowUtils.class,
ShadowDevicePolicyManager.class,
ShadowSensorPrivacyManager.class,
SettingsShadowResources.class
SettingsShadowResources.class,
ShadowAlertDialogCompat.class
})
public class FaceEnrollIntroductionTest {
@@ -123,8 +129,8 @@ public class FaceEnrollIntroductionTest {
enum GateKeeperAction {CALL_SUPER, RETURN_BYTE_ARRAY, THROW_CREDENTIAL_NOT_MATCH}
public static class TestFaceEnrollIntroduction extends FaceEnrollIntroduction {
private int mRecreateCount = 0;
public boolean mIsMultiWindowMode;
public int getRecreateCount() {
return mRecreateCount;
@@ -161,6 +167,11 @@ public class FaceEnrollIntroductionTest {
protected boolean launchPostureGuidance() {
return super.launchPostureGuidance();
}
@Override
public boolean isInMultiWindowMode() {
return mIsMultiWindowMode;
}
}
@Before
@@ -178,6 +189,7 @@ public class FaceEnrollIntroductionTest {
public void tearDown() {
ShadowUtils.reset();
ShadowLockPatternUtils.reset();
ShadowAlertDialogCompat.reset();
}
private void setupActivity() {
@@ -596,4 +608,37 @@ public class FaceEnrollIntroductionTest {
assertThat(result).isEqualTo(0);
}
@Test
public void multiWindow_showsDialog() {
mController = Robolectric.buildActivity(TestFaceEnrollIntroduction.class);
mActivity = (TestFaceEnrollIntroduction) mController.get();
mActivity.mIsMultiWindowMode = true;
mController.setup().get();
Shadows.shadowOf(Looper.getMainLooper()).idle();
final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
final ShadowAlertDialogCompat shadowAlertDialog = ShadowAlertDialogCompat.shadowOf(dialog);
assertThat(shadowAlertDialog.getTitle().toString()).isEqualTo(
mActivity.getString(R.string.biometric_settings_add_face_in_split_mode_title));
assertThat(shadowAlertDialog.getMessage().toString()).isEqualTo(
mActivity.getString(R.string.biometric_settings_add_face_in_split_mode_message));
final Button button = dialog.getButton(DialogInterface.BUTTON_POSITIVE);
assertThat(button).isNotNull();
button.performClick();
Shadows.shadowOf(Looper.getMainLooper()).idle();
assertThat(dialog.isShowing()).isFalse();
assertThat(mActivity.isFinishing()).isTrue();
}
@Test
public void singleWindow_noDialog() {
Robolectric.buildActivity(TestFaceEnrollIntroduction.class).setup().get();
Shadows.shadowOf(Looper.getMainLooper()).idle();
final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
}

View File

@@ -20,9 +20,7 @@ import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWE
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment;
import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.ADD_FINGERPRINT_REQUEST;
import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.CHOOSE_LOCK_GENERIC_REQUEST;
import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.KEY_FINGERPRINT_ADD;
import static com.android.settings.biometrics.fingerprint.FingerprintSettings.FingerprintSettingsFragment.KEY_REQUIRE_SCREEN_ON_TO_AUTH;
import static com.google.common.truth.Truth.assertThat;
@@ -34,9 +32,7 @@ import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.doReturn;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import android.content.Context;
@@ -57,10 +53,8 @@ import android.view.ViewGroup;
import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import androidx.preference.Preference;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.biometrics.BiometricsSplitScreenDialog;
import com.android.settings.password.ChooseLockSettingsHelper;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowFragment;
@@ -132,35 +126,6 @@ public class FingerprintSettingsFragmentTest {
ShadowUtils.reset();
}
@Test
public void testAddFingerprint_inFullScreen_noDialog() {
setUpFragment(false);
// Click "Add Fingerprint"
final Preference preference = new Preference(mContext);
preference.setKey(KEY_FINGERPRINT_ADD);
mFragment.onPreferenceTreeClick(preference);
verify(mFragment).startActivityForResult(any(), eq(ADD_FINGERPRINT_REQUEST));
verify(mFragmentTransaction, never()).add(any(),
eq(BiometricsSplitScreenDialog.class.getName()));
}
@Test
public void testAddFingerprint_inMultiWindow_showsDialog() {
setUpFragment(false);
doReturn(true).when(mActivity).isInMultiWindowMode();
// Click "Add Fingerprint"
final Preference preference = new Preference(mContext);
preference.setKey(KEY_FINGERPRINT_ADD);
mFragment.onPreferenceTreeClick(preference);
verify(mFragment, times(0)).startActivityForResult(any(), eq(ADD_FINGERPRINT_REQUEST));
verify(mFragmentTransaction).add(any(), eq(BiometricsSplitScreenDialog.class.getName()));
}
@Test
public void testChooseLockKeyForFingerprint() {
setUpFragment(true);

View File

@@ -21,6 +21,7 @@ import static com.google.common.truth.Truth.assertWithMessage;
import static org.robolectric.RuntimeEnvironment.application;
import android.app.Activity;
import android.app.KeyguardManager;
import android.content.Intent;
import android.content.pm.PackageManager;
@@ -28,14 +29,18 @@ import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.SensorProperties;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
import android.os.Looper;
import android.view.View;
import android.widget.Button;
import androidx.appcompat.app.AlertDialog;
import com.android.settings.R;
import com.android.settings.biometrics.BiometricEnrollBase;
import com.android.settings.biometrics.BiometricEnrollIntroduction;
import com.android.settings.password.SetupSkipDialog;
import com.android.settings.testutils.FakeFeatureFactory;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowFingerprintManager;
import com.android.settings.testutils.shadow.ShadowLockPatternUtils;
import com.android.settings.testutils.shadow.ShadowStorageManager;
@@ -66,12 +71,23 @@ import java.util.List;
ShadowFingerprintManager.class,
ShadowLockPatternUtils.class,
ShadowStorageManager.class,
ShadowUserManager.class
ShadowUserManager.class,
ShadowAlertDialogCompat.class
})
public class SetupFingerprintEnrollIntroductionTest {
private ActivityController<SetupFingerprintEnrollIntroduction> mController;
public static class TestSetupFingerprintEnrollIntroductionInMultiWindowMode
extends SetupFingerprintEnrollIntroduction {
public boolean mIsMultiWindowMode = true;
@Override
public boolean isInMultiWindowMode() {
return mIsMultiWindowMode;
}
}
@Before
public void setUp() {
Shadows.shadowOf(application.getPackageManager())
@@ -105,6 +121,36 @@ public class SetupFingerprintEnrollIntroductionTest {
@After
public void tearDown() {
ShadowStorageManager.reset();
ShadowAlertDialogCompat.reset();
}
@Test
public void multiWindow_showsDialog() {
Activity activity = Robolectric.buildActivity(
TestSetupFingerprintEnrollIntroductionInMultiWindowMode.class).setup().get();
Shadows.shadowOf(Looper.getMainLooper()).idle();
final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNotNull();
final ShadowAlertDialogCompat shadowAlertDialog = ShadowAlertDialogCompat.shadowOf(dialog);
assertThat(shadowAlertDialog.getTitle().toString()).isEqualTo(
activity.getString(
R.string.biometric_settings_add_fingerprint_in_split_mode_title));
assertThat(shadowAlertDialog.getMessage().toString()).isEqualTo(
activity.getString(
R.string.biometric_settings_add_fingerprint_in_split_mode_message));
// TODO(b/299573056): Make WizardManagerHelper.isAnySetupWizard(getIntent()) correct and
// test button click not finishing the activity.
}
@Test
public void singleWindow_noDialog() {
Robolectric.buildActivity(SetupFingerprintEnrollIntroduction.class).setup().get();
Shadows.shadowOf(Looper.getMainLooper()).idle();
final AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test

View File

@@ -25,8 +25,6 @@ import static org.mockito.Mockito.spy;
import android.content.Context;
import android.content.pm.PackageManager;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.SearchIndexableResource;
@@ -34,7 +32,6 @@ import android.provider.SearchIndexableResource;
import com.android.settings.R;
import com.android.settings.core.BasePreferenceController;
import com.android.settings.core.PreferenceControllerListHelper;
import com.android.settings.flags.Flags;
import com.android.settings.slices.SlicePreferenceController;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowConnectivityManager;
@@ -53,8 +50,12 @@ import org.robolectric.annotation.Config;
import java.util.List;
@RunWith(RobolectricTestRunner.class)
@Config(shadows = {ShadowUserManager.class,
ShadowConnectivityManager.class, ShadowBluetoothAdapter.class})
@Config(
shadows = {
ShadowUserManager.class,
ShadowConnectivityManager.class,
ShadowBluetoothAdapter.class
})
public class ConnectedDeviceDashboardFragmentTest {
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@@ -98,30 +99,11 @@ public class ConnectedDeviceDashboardFragmentTest {
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void nonIndexableKeys_existInXmlLayout() {
final List<String> niks =
ConnectedDeviceDashboardFragment.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(
mContext);
assertThat(niks)
.containsExactly(
KEY_CONNECTED_DEVICES,
KEY_AVAILABLE_DEVICES,
KEY_NEARBY_DEVICES,
KEY_DISCOVERABLE_FOOTER,
KEY_SAVED_DEVICE_SEE_ALL,
KEY_FAST_PAIR_DEVICE_SEE_ALL,
KEY_AUDIO_SHARING_SETTINGS);
}
@Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void nonIndexableKeys_existInXmlLayout_flagOff() {
final List<String> niks =
ConnectedDeviceDashboardFragment.SEARCH_INDEX_DATA_PROVIDER.getNonIndexableKeys(
mContext);
assertThat(niks)
.containsExactly(
KEY_CONNECTED_DEVICES,
@@ -160,10 +142,12 @@ public class ConnectedDeviceDashboardFragmentTest {
PreferenceControllerListHelper.getPreferenceControllersFromXml(
mContext, R.xml.connected_devices);
assertThat(controllers
.stream()
.filter(controller -> controller instanceof SlicePreferenceController)
.count())
assertThat(
controllers.stream()
.filter(
controller ->
controller instanceof SlicePreferenceController)
.count())
.isEqualTo(1);
}
}

View File

@@ -158,7 +158,9 @@ public class AudioSharingDialogFragmentTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_singleConnectedDevice_dialogDismiss() {
mFragment.show(mParent, new ArrayList<>(), (item) -> {});
ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
list.add(TEST_DEVICE_ITEM1);
mFragment.show(mParent, list, (item) -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
ShadowAlertDialogCompat shadowDialog = ShadowAlertDialogCompat.shadowOf(dialog);
@@ -170,10 +172,12 @@ public class AudioSharingDialogFragmentTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_singleConnectedDevice_shareClicked() {
ArrayList<AudioSharingDeviceItem> list = new ArrayList<>();
list.add(TEST_DEVICE_ITEM1);
AtomicBoolean isShareBtnClicked = new AtomicBoolean(false);
mFragment.show(
mParent,
new ArrayList<>(),
list,
(item) -> {
isShareBtnClicked.set(true);
});

View File

@@ -18,7 +18,6 @@ package com.android.settings.connecteddevice.audiosharing;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.Mockito.verify;
import static org.robolectric.Shadows.shadowOf;
import android.bluetooth.BluetoothAdapter;
@@ -71,6 +70,7 @@ public class AudioSharingSwitchBarControllerTest {
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void bluetoothOff_switchDisabled() {
assertThat(mSwitchBar.isEnabled()).isTrue();
mContext.registerReceiver(
mController.mReceiver,
mController.mIntentFilter,
@@ -79,7 +79,7 @@ public class AudioSharingSwitchBarControllerTest {
intent.putExtra(BluetoothAdapter.EXTRA_STATE, BluetoothAdapter.STATE_OFF);
mContext.sendBroadcast(intent);
shadowOf(Looper.getMainLooper()).idle();
verify(mSwitch).setEnabled(false);
assertThat(mSwitchBar.isEnabled()).isFalse();
assertThat(mOnSwitchBarChanged).isTrue();
}
}

View File

@@ -0,0 +1,110 @@
/*
* 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.connecteddevice.audiosharing;
import static com.google.common.truth.Truth.assertThat;
import static org.robolectric.shadows.ShadowLooper.shadowMainLooper;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothStatusCodes;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import androidx.appcompat.app.AlertDialog;
import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentActivity;
import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowAlertDialogCompat;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import org.robolectric.shadows.androidx.fragment.FragmentController;
import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowAlertDialogCompat.class,
ShadowBluetoothAdapter.class,
})
public class CallsAndAlarmsDialogFragmentTest {
@Rule public final MockitoRule mocks = MockitoJUnit.rule();
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
private static final String TEST_DEVICE_NAME1 = "test1";
private static final String TEST_DEVICE_NAME2 = "test2";
private static final AudioSharingDeviceItem TEST_DEVICE_ITEM1 =
new AudioSharingDeviceItem(TEST_DEVICE_NAME1, /* groupId= */ 1, /* isActive= */ true);
private static final AudioSharingDeviceItem TEST_DEVICE_ITEM2 =
new AudioSharingDeviceItem(TEST_DEVICE_NAME2, /* groupId= */ 1, /* isActive= */ true);
private Fragment mParent;
private CallsAndAlarmsDialogFragment mFragment;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
@Before
public void setUp() {
ShadowAlertDialogCompat.reset();
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mFragment = new CallsAndAlarmsDialogFragment();
mParent = new Fragment();
FragmentController.setupFragment(
mParent, FragmentActivity.class, /* containerViewId= */ 0, /* bundle= */ null);
}
@Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_flagOff_dialogNotExist() {
mFragment.show(mParent, new ArrayList<>(), (item) -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog).isNull();
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void onCreateDialog_showCorrectItems() {
ArrayList<AudioSharingDeviceItem> deviceItemList = new ArrayList<>();
deviceItemList.add(TEST_DEVICE_ITEM1);
deviceItemList.add(TEST_DEVICE_ITEM2);
mFragment.show(mParent, deviceItemList, (item) -> {});
shadowMainLooper().idle();
AlertDialog dialog = ShadowAlertDialogCompat.getLatestAlertDialog();
assertThat(dialog.getListView().getCount()).isEqualTo(2);
}
}

View File

@@ -0,0 +1,245 @@
/*
* 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.connecteddevice.audiosharing;
import static com.android.settings.core.BasePreferenceController.AVAILABLE;
import static com.android.settings.core.BasePreferenceController.UNSUPPORTED_ON_DEVICE;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import static org.robolectric.Shadows.shadowOf;
import android.bluetooth.BluetoothAdapter;
import android.bluetooth.BluetoothDevice;
import android.bluetooth.BluetoothLeBroadcastAssistant;
import android.bluetooth.BluetoothLeBroadcastReceiveState;
import android.bluetooth.BluetoothProfile;
import android.bluetooth.BluetoothStatusCodes;
import android.content.Context;
import android.os.Looper;
import android.platform.test.annotations.RequiresFlagsDisabled;
import android.platform.test.annotations.RequiresFlagsEnabled;
import android.platform.test.flag.junit.CheckFlagsRule;
import android.platform.test.flag.junit.DeviceFlagsValueProvider;
import android.provider.Settings;
import androidx.lifecycle.LifecycleOwner;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import androidx.test.core.app.ApplicationProvider;
import com.android.settings.bluetooth.Utils;
import com.android.settings.flags.Flags;
import com.android.settings.testutils.shadow.ShadowBluetoothAdapter;
import com.android.settings.testutils.shadow.ShadowBluetoothUtils;
import com.android.settingslib.bluetooth.BluetoothEventManager;
import com.android.settingslib.bluetooth.CachedBluetoothDevice;
import com.android.settingslib.bluetooth.CachedBluetoothDeviceManager;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcast;
import com.android.settingslib.bluetooth.LocalBluetoothLeBroadcastAssistant;
import com.android.settingslib.bluetooth.LocalBluetoothManager;
import com.android.settingslib.bluetooth.LocalBluetoothProfileManager;
import com.android.settingslib.core.lifecycle.Lifecycle;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableSet;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.Mock;
import org.mockito.Spy;
import org.mockito.junit.MockitoJUnit;
import org.mockito.junit.MockitoRule;
import org.robolectric.RobolectricTestRunner;
import org.robolectric.annotation.Config;
import org.robolectric.shadow.api.Shadow;
import java.util.ArrayList;
@RunWith(RobolectricTestRunner.class)
@Config(
shadows = {
ShadowBluetoothAdapter.class,
ShadowBluetoothUtils.class,
})
public class CallsAndAlarmsPreferenceControllerTest {
private static final String PREF_KEY = "calls_and_alarms";
private static final String SUMMARY_EMPTY = "No active device in sharing";
private static final String TEST_DEVICE_NAME1 = "test1";
private static final String TEST_DEVICE_NAME2 = "test2";
private static final String TEST_SETTINGS_KEY =
"bluetooth_le_broadcast_fallback_active_group_id";
@Rule public final MockitoRule mMockitoRule = MockitoJUnit.rule();
@Rule
public final CheckFlagsRule mCheckFlagsRule = DeviceFlagsValueProvider.createCheckFlagsRule();
@Spy Context mContext = ApplicationProvider.getApplicationContext();
@Mock private PreferenceScreen mScreen;
@Mock private LocalBluetoothManager mLocalBtManager;
@Mock private BluetoothEventManager mBtEventManager;
@Mock private LocalBluetoothProfileManager mLocalBtProfileManager;
@Mock private CachedBluetoothDeviceManager mCacheManager;
@Mock private LocalBluetoothLeBroadcast mBroadcast;
@Mock private LocalBluetoothLeBroadcastAssistant mAssistant;
@Mock private BluetoothDevice mDevice1;
@Mock private BluetoothDevice mDevice2;
@Mock private BluetoothDevice mDevice3;
@Mock private CachedBluetoothDevice mCachedDevice1;
@Mock private CachedBluetoothDevice mCachedDevice2;
@Mock private CachedBluetoothDevice mCachedDevice3;
@Mock private BluetoothLeBroadcastReceiveState mState;
private CallsAndAlarmsPreferenceController mController;
private ShadowBluetoothAdapter mShadowBluetoothAdapter;
private LocalBluetoothManager mLocalBluetoothManager;
private Lifecycle mLifecycle;
private LifecycleOwner mLifecycleOwner;
private Preference mPreference;
@Before
public void setUp() {
mShadowBluetoothAdapter = Shadow.extract(BluetoothAdapter.getDefaultAdapter());
mShadowBluetoothAdapter.setEnabled(true);
mShadowBluetoothAdapter.setIsLeAudioBroadcastSourceSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mShadowBluetoothAdapter.setIsLeAudioBroadcastAssistantSupported(
BluetoothStatusCodes.FEATURE_SUPPORTED);
mLifecycleOwner = () -> mLifecycle;
mLifecycle = new Lifecycle(mLifecycleOwner);
ShadowBluetoothUtils.sLocalBluetoothManager = mLocalBtManager;
mLocalBluetoothManager = Utils.getLocalBtManager(mContext);
when(mLocalBluetoothManager.getEventManager()).thenReturn(mBtEventManager);
when(mLocalBluetoothManager.getProfileManager()).thenReturn(mLocalBtProfileManager);
when(mLocalBtProfileManager.getLeAudioBroadcastProfile()).thenReturn(mBroadcast);
when(mLocalBtProfileManager.getLeAudioBroadcastAssistantProfile()).thenReturn(mAssistant);
mController = new CallsAndAlarmsPreferenceController(mContext);
mPreference = new Preference(mContext);
when(mScreen.findPreference(PREF_KEY)).thenReturn(mPreference);
}
@Test
public void onStart_registerCallback() {
mController.onStart(mLifecycleOwner);
verify(mBtEventManager).registerCallback(mController);
verify(mAssistant)
.registerServiceCallBack(any(), any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
public void onStop_unregisterCallback() {
mController.onStop(mLifecycleOwner);
verify(mBtEventManager).unregisterCallback(mController);
verify(mAssistant)
.unregisterServiceCallBack(any(BluetoothLeBroadcastAssistant.Callback.class));
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void getAvailabilityStatus_flagOn() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
}
@Test
@RequiresFlagsDisabled(Flags.FLAG_ENABLE_LE_AUDIO_SHARING)
public void getAvailabilityStatus_flagOff() {
assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
}
@Test
public void updateVisibility_broadcastOffBluetoothOff() {
when(mBroadcast.isEnabled(any())).thenReturn(false);
mShadowBluetoothAdapter.setEnabled(false);
mController.displayPreference(mScreen);
mController.updateVisibility();
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void updateVisibility_broadcastOnBluetoothOff() {
when(mBroadcast.isEnabled(any())).thenReturn(true);
mShadowBluetoothAdapter.setEnabled(false);
mController.displayPreference(mScreen);
mController.updateVisibility();
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void updateVisibility_broadcastOffBluetoothOn() {
when(mBroadcast.isEnabled(any())).thenReturn(false);
mController.displayPreference(mScreen);
mController.updateVisibility();
assertThat(mPreference.isVisible()).isFalse();
}
@Test
public void updateVisibility_broadcastOnBluetoothOn() {
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mAssistant.getConnectedDevices()).thenReturn(new ArrayList<BluetoothDevice>());
mController.displayPreference(mScreen);
mController.updateVisibility();
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getSummary().toString()).isEqualTo(SUMMARY_EMPTY);
}
@Test
public void onProfileConnectionStateChanged_updatePreference() {
when(mBroadcast.isEnabled(any())).thenReturn(true);
when(mAssistant.getConnectedDevices()).thenReturn(new ArrayList<BluetoothDevice>());
mController.displayPreference(mScreen);
mController.onProfileConnectionStateChanged(
mCachedDevice1, BluetoothAdapter.STATE_DISCONNECTED, BluetoothProfile.LE_AUDIO);
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getSummary().toString()).isEqualTo(SUMMARY_EMPTY);
}
@Test
public void updatePreference_showCorrectSummary() {
final int groupId1 = 1;
final int groupId2 = 2;
Settings.Secure.putInt(mContext.getContentResolver(), TEST_SETTINGS_KEY, groupId1);
when(mCachedDevice1.getGroupId()).thenReturn(groupId1);
when(mCachedDevice1.getDevice()).thenReturn(mDevice1);
when(mCachedDevice2.getGroupId()).thenReturn(groupId1);
when(mCachedDevice2.getDevice()).thenReturn(mDevice2);
when(mCachedDevice1.getMemberDevice()).thenReturn(ImmutableSet.of(mCachedDevice2));
when(mCachedDevice1.getName()).thenReturn(TEST_DEVICE_NAME1);
when(mCachedDevice3.getGroupId()).thenReturn(groupId2);
when(mCachedDevice3.getDevice()).thenReturn(mDevice3);
when(mCachedDevice3.getName()).thenReturn(TEST_DEVICE_NAME2);
when(mLocalBluetoothManager.getCachedDeviceManager()).thenReturn(mCacheManager);
when(mCacheManager.findDevice(mDevice1)).thenReturn(mCachedDevice1);
when(mCacheManager.findDevice(mDevice2)).thenReturn(mCachedDevice2);
when(mCacheManager.findDevice(mDevice3)).thenReturn(mCachedDevice3);
when(mBroadcast.isEnabled(any())).thenReturn(true);
ImmutableList<BluetoothDevice> deviceList = ImmutableList.of(mDevice1, mDevice2, mDevice3);
when(mAssistant.getConnectedDevices()).thenReturn(deviceList);
when(mAssistant.getAllSources(any())).thenReturn(ImmutableList.of(mState));
mController.displayPreference(mScreen);
mController.updateVisibility();
shadowOf(Looper.getMainLooper()).idle();
assertThat(mPreference.isVisible()).isTrue();
assertThat(mPreference.getSummary().toString()).isEqualTo(TEST_DEVICE_NAME1);
}
}

View File

@@ -677,7 +677,7 @@ public class DashboardFeatureProviderImplTest {
preference.performClick();
Intent nextStartedActivity =
Shadows.shadowOf((Application) mApplication).peekNextStartedActivity();
Shadows.shadowOf(mApplication).peekNextStartedActivity();
assertThat(nextStartedActivity).isNotNull();
assertThat(nextStartedActivity.getAction()).isEqualTo("test");
}
@@ -704,7 +704,7 @@ public class DashboardFeatureProviderImplTest {
mActivity.getSupportFragmentManager().findFragmentByTag("select_profile");
assertThat(dialogFragment).isNotNull();
Intent nextStartedActivity =
Shadows.shadowOf((Application) mApplication).peekNextStartedActivity();
Shadows.shadowOf(mApplication).peekNextStartedActivity();
assertThat(nextStartedActivity).isNull();
}

View File

@@ -40,7 +40,10 @@ import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.PersistableBundle;
import android.platform.test.flag.junit.SetFlagsRule;
import android.telephony.CarrierConfigManager;
import android.telephony.NetworkRegistrationInfo;
import android.telephony.ServiceState;
import android.telephony.TelephonyManager;
import android.telephony.ims.ImsMmTelManager;
import android.view.View;
@@ -48,6 +51,7 @@ import android.view.View;
import androidx.preference.Preference;
import androidx.preference.PreferenceScreen;
import com.android.internal.telephony.flags.Flags;
import com.android.settings.R;
import com.android.settings.SettingsActivity;
import com.android.settings.network.ims.MockWifiCallingQueryImsState;
@@ -57,6 +61,7 @@ import com.android.settings.widget.SettingsMainSwitchBar;
import com.android.settings.widget.SettingsMainSwitchPreference;
import org.junit.Before;
import org.junit.Rule;
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
@@ -70,6 +75,7 @@ import org.robolectric.util.ReflectionHelpers;
@Config(shadows = ShadowFragment.class)
@RunWith(RobolectricTestRunner.class)
public class WifiCallingSettingsForSubTest {
@Rule public final SetFlagsRule mSetFlagsRule = new SetFlagsRule();
private static final int SUB_ID = 2;
private static final String SWITCH_BAR = "wifi_calling_switch_bar";
@@ -158,6 +164,7 @@ public class WifiCallingSettingsForSubTest {
mFragment.onAttach(mContext);
mFragment.onCreate(null);
mFragment.onActivityCreated(null);
mSetFlagsRule.disableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
}
private void setDefaultCarrierConfigValues() {
@@ -240,6 +247,31 @@ public class WifiCallingSettingsForSubTest {
verify(mButtonWfcRoamingMode, times(1)).setVisible(false);
}
@Test
public void onResume_overrideWfcRoamingModeWhileUsingNTN_shouldDisableWfcRoaming() {
mSetFlagsRule.enableFlags(Flags.FLAG_CARRIER_ENABLED_SATELLITE_FLAG);
mBundle.putBoolean(
CarrierConfigManager.KEY_USE_WFC_HOME_NETWORK_MODE_IN_ROAMING_NETWORK_BOOL, false);
mBundle.putBoolean(CarrierConfigManager.KEY_EDITABLE_WFC_ROAMING_MODE_BOOL, true);
mBundle.putBoolean(
CarrierConfigManager.KEY_OVERRIDE_WFC_ROAMING_MODE_WHILE_USING_NTN_BOOL, true);
// Phone connected to non-terrestrial network
NetworkRegistrationInfo nri = new NetworkRegistrationInfo.Builder()
.setIsNonTerrestrialNetwork(true)
.build();
ServiceState ss = new ServiceState();
ss.addNetworkRegistrationInfo(nri);
doReturn(ss).when(mTelephonyManager).getServiceState();
// Call onResume to update the WFC roaming preference.
mFragment.onResume();
// Check that WFC roaming preference is visible but disabled
verify(mButtonWfcRoamingMode, times(1)).setEnabled(false);
verify(mButtonWfcRoamingMode, times(1)).setVisible(true);
}
@Test
public void onResume_useWfcHomeModeConfigTrueAndNotEditable_shouldHideWfcRoaming() {
mBundle.putBoolean(

View File

@@ -20,8 +20,11 @@ import android.app.ActivityManager
import android.content.ComponentName
import android.content.Context
import android.content.pm.ActivityInfo
import android.content.pm.ApplicationInfo
import android.content.pm.Flags
import android.content.pm.PackageManager
import android.content.pm.ResolveInfo
import android.platform.test.annotations.RequiresFlagsEnabled
import androidx.core.os.bundleOf
import androidx.test.core.app.ApplicationProvider
import androidx.test.ext.junit.runners.AndroidJUnit4
@@ -126,9 +129,62 @@ class AppButtonRepositoryTest {
assertThat(homePackageInfo.homePackages).containsExactly(PACKAGE_NAME)
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
fun uninstallDisallowedDueToHomeApp_isNotSystemAndIsCurrentHomeAndHasOnlyOneHomeApp() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
mockGetHomeActivities(
homeActivities = listOf(RESOLVE_INFO),
currentDefaultHome = COMPONENT_NAME,
)
val value = appButtonRepository.uninstallDisallowedDueToHomeApp(app)
assertThat(value).isTrue()
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
fun uninstallDisallowedDueToHomeApp_isNotSystemAndIsCurrentHomeAndHasOtherHomeApps() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
}
mockGetHomeActivities(
homeActivities = listOf(RESOLVE_INFO, RESOLVE_INFO_FAKE),
currentDefaultHome = COMPONENT_NAME,
)
val value = appButtonRepository.uninstallDisallowedDueToHomeApp(app)
assertThat(value).isFalse()
}
@Test
@RequiresFlagsEnabled(Flags.FLAG_IMPROVE_HOME_APP_BEHAVIOR)
fun uninstallDisallowedDueToHomeApp_isSystemAndIsCurrentHomeAndHasOtherHomeApps() {
val app = ApplicationInfo().apply {
packageName = PACKAGE_NAME
flags = ApplicationInfo.FLAG_SYSTEM
}
mockGetHomeActivities(
homeActivities = listOf(RESOLVE_INFO, RESOLVE_INFO_FAKE),
currentDefaultHome = COMPONENT_NAME,
)
val value = appButtonRepository.uninstallDisallowedDueToHomeApp(app)
assertThat(value).isTrue()
}
private companion object {
const val PACKAGE_NAME = "packageName"
const val PACKAGE_NAME_ALTERNATE = "packageName.alternate"
const val PACKAGE_NAME_FAKE = "packageName.fake"
const val ACTIVITY_NAME = "activityName"
val COMPONENT_NAME = ComponentName(PACKAGE_NAME, ACTIVITY_NAME)
val RESOLVE_INFO = ResolveInfo().apply {
@@ -136,6 +192,11 @@ class AppButtonRepositoryTest {
packageName = PACKAGE_NAME
}
}
val RESOLVE_INFO_FAKE = ResolveInfo().apply {
activityInfo = ActivityInfo().apply {
packageName = PACKAGE_NAME_FAKE
}
}
val RESOLVE_INFO_WITH_ALTERNATE = ResolveInfo().apply {
activityInfo = ActivityInfo().apply {
packageName = PACKAGE_NAME

View File

@@ -19,9 +19,7 @@ package com.android.settings.biometrics.face;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.content.Context;
import android.widget.Button;
@@ -61,20 +59,10 @@ public class FaceSettingsEnrollButtonPreferenceControllerTest {
}
@Test
public void testOnClick_inFullScreen() {
when(mListener.onShowSplitScreenDialog()).thenReturn(false);
public void testOnClick() {
mController.onClick(mButton);
assertThat(mController.isClicked()).isTrue();
verify(mListener).onStartEnrolling(any());
}
@Test
public void testOnClick_inMultiWindow() {
when(mListener.onShowSplitScreenDialog()).thenReturn(true);
mController.onClick(mButton);
assertThat(mController.isClicked()).isFalse();
verify(mListener, never()).onStartEnrolling(any());
}
}