Snap for 12864127 from 2a828c6bde to 25Q2-release
Change-Id: I0279432ec83d85cba060e6be32836a5dce9e64e5
This commit is contained in:
25
res/drawable/ic_check_list_24dp.xml
Normal file
25
res/drawable/ic_check_list_24dp.xml
Normal file
@@ -0,0 +1,25 @@
|
||||
<!--
|
||||
~ Copyright (C) 2016 The Android Open Source Project
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960"
|
||||
android:tint="?android:attr/colorControlNormal">
|
||||
<path
|
||||
android:pathData="M222,760L80,618L136,562L221,647L391,477L447,534L222,760ZM222,440L80,298L136,242L221,327L391,157L447,214L222,440ZM520,680L520,600L880,600L880,680L520,680ZM520,360L520,280L880,280L880,360L520,360Z"
|
||||
android:fillColor="#000000"/>
|
||||
</vector>
|
||||
47
res/layout/fingerprint_check_enrolled_dialog.xml
Normal file
47
res/layout/fingerprint_check_enrolled_dialog.xml
Normal file
@@ -0,0 +1,47 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ 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.
|
||||
-->
|
||||
<RelativeLayout
|
||||
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.android.settings.biometrics.fingerprint.UdfpsCheckEnrolledView
|
||||
android:id="@+id/udfps_check_enrolled_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_centerHorizontal="true">
|
||||
|
||||
<!-- Fingerprint -->
|
||||
<ImageView
|
||||
android:id="@+id/udfps_fingerprint_sensor_view"
|
||||
android:contentDescription="@string/accessibility_fingerprint_label"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/udfps_fingerprint_sensor_message"
|
||||
android:layout_marginTop="80dp"
|
||||
android:layout_below="@id/udfps_fingerprint_sensor_view"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerHorizontal="true"
|
||||
android:textSize="18sp"
|
||||
android:textColor="@android:color/white"
|
||||
android:text="@string/fingerprint_check_enroll_touch_sensor"/>
|
||||
|
||||
</com.android.settings.biometrics.fingerprint.UdfpsCheckEnrolledView>
|
||||
</RelativeLayout>
|
||||
@@ -938,6 +938,8 @@
|
||||
<string name="security_settings_fingerprint_settings_preferences_category">When using Fingerprint Unlock</string>
|
||||
<!-- Title shown for work menu item that launches fingerprint settings or enrollment [CHAR LIMIT=22] -->
|
||||
<string name="security_settings_work_fingerprint_preference_title">Fingerprint for work</string>
|
||||
<!-- Preference to check enrolled fingerprints -->
|
||||
<string name="fingerprint_check_enrolled_title">Check enrolled fingerprints</string>
|
||||
<!-- Preference to add another fingerprint -->
|
||||
<string name="fingerprint_add_title">Add fingerprint</string>
|
||||
<!-- Message showing the current number of fingerprints set up. Shown for a menu item that launches fingerprint settings or enrollment. -->
|
||||
@@ -991,7 +993,10 @@
|
||||
<string name="security_settings_fingerprint_v2_enroll_introduction_footer_message_consent_6">For best results, use a screen protector that\u2019s Made for Google certified. With other screen protectors, your child\u2019s fingerprint may not work.</string>
|
||||
<!-- Introduction detail message shown in fingerprint enrollment introduction to learn more about fingerprint [CHAR LIMIT=NONE]-->
|
||||
<string name="security_settings_fingerprint_v2_enroll_introduction_message_learn_more"></string>
|
||||
|
||||
<!-- Description shown on the check enrolled fingerprint dialog -->
|
||||
<string name="fingerprint_check_enroll_touch_sensor">Touch the fingerprint sensor</string>
|
||||
<!-- Description shown on the check enrolled fingerprint dialog -->
|
||||
<string name="fingerprint_check_enroll_not_recognized">Fingerprint not recognized</string>
|
||||
<!-- Watch unlock enrollment and settings --><skip />
|
||||
<!-- Title shown for menu item that launches watch unlock settings. [CHAR LIMIT=40] -->
|
||||
<string name ="security_settings_activeunlock_preference_title">Watch Unlock</string>
|
||||
@@ -2035,6 +2040,11 @@
|
||||
<string name="bluetooth_companion_app_body"><xliff:g id="app_name" example="App Name">%1$s</xliff:g> app will no longer connect to your <xliff:g id="device_name" example="Device Name">%2$s</xliff:g></string>
|
||||
<!-- Summary of Bluetooth LE Audio toggle in Device Details. [CHAR LIMIT=40] -->
|
||||
<string name="device_details_leaudio_toggle_summary">Experimental. Improves audio quality.</string>
|
||||
<!-- Content Description for battery icon in device details screen [CHAR LIMIT=NONE]-->
|
||||
<string name="device_details_battery">Battery</string>
|
||||
<!-- Content Description for battery charging icon in device details screen [CHAR LIMIT=NONE]-->
|
||||
<string name="device_details_battery_charging">Battery, charging</string>
|
||||
|
||||
|
||||
<!-- Bluetooth device details. In the confirmation dialog for unpairing a paired device, this is the label on the button that will complete the unpairing action. -->
|
||||
<string name="bluetooth_unpair_dialog_forget_confirm_button">Forget device</string>
|
||||
|
||||
@@ -44,7 +44,7 @@ import com.android.settings.spa.SettingsSpaEnvironment;
|
||||
import com.android.settingslib.applications.AppIconCacheManager;
|
||||
import com.android.settingslib.datastore.BackupRestoreStorageManager;
|
||||
import com.android.settingslib.metadata.FixedArrayMap;
|
||||
import com.android.settingslib.metadata.PreferenceScreenMetadataCreator;
|
||||
import com.android.settingslib.metadata.PreferenceScreenMetadataFactory;
|
||||
import com.android.settingslib.metadata.PreferenceScreenRegistry;
|
||||
import com.android.settingslib.metadata.ProvidePreferenceScreenOptions;
|
||||
import com.android.settingslib.preference.PreferenceBindingFactory;
|
||||
@@ -75,8 +75,8 @@ public class SettingsApplication extends Application {
|
||||
super.onCreate();
|
||||
|
||||
if (Flags.catalyst()) {
|
||||
PreferenceScreenRegistry.INSTANCE.setPreferenceScreenMetadataCreators(
|
||||
getPreferenceScreenCreators());
|
||||
PreferenceScreenRegistry.INSTANCE.setPreferenceScreenMetadataFactories(
|
||||
preferenceScreenFactories());
|
||||
PreferenceBindingFactory.setDefaultFactory(new SettingsPreferenceBindingFactory());
|
||||
}
|
||||
|
||||
@@ -106,8 +106,8 @@ public class SettingsApplication extends Application {
|
||||
registerActivityLifecycleCallbacks(new DeveloperOptionsActivityLifecycle());
|
||||
}
|
||||
|
||||
/** Returns the creators of preference screen metadata. */
|
||||
protected FixedArrayMap<String, PreferenceScreenMetadataCreator> getPreferenceScreenCreators() {
|
||||
/** Returns the factories of preference screen metadata. */
|
||||
protected FixedArrayMap<String, PreferenceScreenMetadataFactory> preferenceScreenFactories() {
|
||||
// PreferenceScreenCollector is generated by annotation processor from classes annotated
|
||||
// with @ProvidePreferenceScreen
|
||||
return PreferenceScreenCollector.get();
|
||||
|
||||
@@ -572,7 +572,7 @@ public class AppInfoDashboardFragment extends DashboardFragment
|
||||
showIt = false;
|
||||
} else if (mPackageInfo == null || mDpm.packageHasActiveAdmins(mPackageInfo.packageName)) {
|
||||
showIt = false;
|
||||
} else if (UserHandle.myUserId() != 0) {
|
||||
} else if (!mUserManager.isAdminUser()) {
|
||||
showIt = false;
|
||||
} else if (mUserManager.getUsers().size() < 2) {
|
||||
showIt = false;
|
||||
|
||||
@@ -21,6 +21,7 @@ import static android.app.admin.DevicePolicyResources.Strings.Settings.FINGERPRI
|
||||
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.Flags.screenOffUnlockUdfps;
|
||||
import static android.view.WindowManager.LayoutParams.LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
|
||||
|
||||
import static com.android.settings.Utils.SETTINGS_PACKAGE_NAME;
|
||||
import static com.android.settings.Utils.isPrivateProfile;
|
||||
@@ -40,6 +41,7 @@ import android.hardware.fingerprint.Fingerprint;
|
||||
import android.hardware.fingerprint.FingerprintManager;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.os.Bundle;
|
||||
import android.os.CancellationSignal;
|
||||
import android.os.Handler;
|
||||
import android.os.UserHandle;
|
||||
import android.os.UserManager;
|
||||
@@ -49,8 +51,15 @@ import android.text.InputFilter;
|
||||
import android.text.Spanned;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.Window;
|
||||
import android.view.WindowInsets;
|
||||
import android.view.WindowInsetsController;
|
||||
import android.view.WindowManager;
|
||||
import android.widget.ImeAwareEditText;
|
||||
import android.widget.TextView;
|
||||
import android.widget.Toast;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
@@ -76,6 +85,7 @@ import com.android.settings.biometrics.IdentityCheckBiometricErrorDialog;
|
||||
import com.android.settings.core.SettingsBaseActivity;
|
||||
import com.android.settings.core.instrumentation.InstrumentedDialogFragment;
|
||||
import com.android.settings.dashboard.DashboardFragment;
|
||||
import com.android.settings.flags.Flags;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.password.ChooseLockGeneric;
|
||||
import com.android.settings.password.ChooseLockSettingsHelper;
|
||||
@@ -235,6 +245,9 @@ public class FingerprintSettings extends SubSettings {
|
||||
|
||||
private static final String TAG = "FingerprintSettings";
|
||||
private static final String KEY_FINGERPRINT_ITEM_PREFIX = "key_fingerprint_item";
|
||||
|
||||
private static final String KEY_FINGERPRINT_CHECK_ENROLLED =
|
||||
"key_fingerprint_check_enrolled";
|
||||
@VisibleForTesting
|
||||
static final String KEY_FINGERPRINT_ADD = "key_fingerprint_add";
|
||||
private static final String KEY_FINGERPRINT_ENABLE_KEYGUARD_TOGGLE =
|
||||
@@ -691,6 +704,17 @@ public class FingerprintSettings extends SubSettings {
|
||||
mFingerprintsEnrolledCategory.addPreference(pref);
|
||||
pref.setOnPreferenceChangeListener(this);
|
||||
}
|
||||
if (Flags.biometricsOnboardingEducation() && isUdfps() && fingerprintCount > 0) {
|
||||
// Setup check enrolled fingerprints preference
|
||||
Preference pref = new Preference(root.getContext());
|
||||
pref.setKey(KEY_FINGERPRINT_CHECK_ENROLLED);
|
||||
pref.setTitle(root.getContext().getString(
|
||||
R.string.fingerprint_check_enrolled_title));
|
||||
pref.setIcon(R.drawable.ic_check_list_24dp);
|
||||
pref.setVisible(true);
|
||||
mFingerprintsEnrolledCategory.addPreference(pref);
|
||||
pref.setOnPreferenceChangeListener(this);
|
||||
}
|
||||
mAddFingerprintPreference = findPreference(KEY_FINGERPRINT_ADD);
|
||||
setupAddFingerprintPreference();
|
||||
return keyToReturn;
|
||||
@@ -916,6 +940,8 @@ public class FingerprintSettings extends SubSettings {
|
||||
FingerprintPreference fpref = (FingerprintPreference) pref;
|
||||
final Fingerprint fp = fpref.getFingerprint();
|
||||
showRenameDialog(fp);
|
||||
} else if (KEY_FINGERPRINT_CHECK_ENROLLED.equals(key)) {
|
||||
showCheckEnrolledDialog();
|
||||
}
|
||||
return super.onPreferenceTreeClick(pref);
|
||||
}
|
||||
@@ -968,6 +994,16 @@ public class FingerprintSettings extends SubSettings {
|
||||
mAuthenticateSidecar.stopAuthentication();
|
||||
}
|
||||
|
||||
private void showCheckEnrolledDialog() {
|
||||
final CheckEnrolledDialog checkEnrolledDialog = new CheckEnrolledDialog();
|
||||
final Bundle args = new Bundle();
|
||||
args.putInt(CheckEnrolledDialog.KEY_USER_ID, mUserId);
|
||||
args.putParcelable(CheckEnrolledDialog.KEY_SENSOR_PROPERTIES, mSensorProperties.get(0));
|
||||
checkEnrolledDialog.setArguments(args);
|
||||
checkEnrolledDialog.setTargetFragment(this, 0);
|
||||
checkEnrolledDialog.show(getFragmentManager(), CheckEnrolledDialog.class.getName());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onPreferenceChange(Preference preference, Object value) {
|
||||
boolean result = true;
|
||||
@@ -1344,6 +1380,121 @@ public class FingerprintSettings extends SubSettings {
|
||||
return new InputFilter[]{filter};
|
||||
}
|
||||
|
||||
public static class CheckEnrolledDialog extends InstrumentedDialogFragment {
|
||||
|
||||
private static final String KEY_USER_ID = "user_id";
|
||||
private static final String KEY_SENSOR_PROPERTIES = "sensor_properties";
|
||||
private int mUserId;
|
||||
private @Nullable CancellationSignal mCancellationSignal;
|
||||
private @Nullable FingerprintSensorPropertiesInternal mSensorPropertiesInternal;
|
||||
|
||||
@Override
|
||||
public @NonNull View onCreateView(
|
||||
@NonNull LayoutInflater inflater,
|
||||
@Nullable ViewGroup container,
|
||||
@Nullable Bundle savedInstanceState) {
|
||||
return inflater.inflate(
|
||||
R.layout.fingerprint_check_enrolled_dialog, container, false);
|
||||
}
|
||||
|
||||
@Override
|
||||
public @NonNull Dialog onCreateDialog(@Nullable Bundle savedInstanceState) {
|
||||
final Dialog dialog = super.onCreateDialog(savedInstanceState);
|
||||
if (dialog != null) {
|
||||
mUserId = getArguments().getInt(KEY_USER_ID);
|
||||
mSensorPropertiesInternal =
|
||||
getArguments().getParcelable(KEY_SENSOR_PROPERTIES);
|
||||
|
||||
// Remove the default dialog title bar
|
||||
dialog.getWindow().requestFeature(Window.FEATURE_NO_TITLE);
|
||||
|
||||
dialog.setOnShowListener(dialogInterface -> {
|
||||
final UdfpsCheckEnrolledView v =
|
||||
dialog.findViewById(R.id.udfps_check_enrolled_view);
|
||||
v.setSensorProperties(mSensorPropertiesInternal);
|
||||
});
|
||||
}
|
||||
|
||||
return dialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onStart() {
|
||||
super.onStart();
|
||||
if (getDialog() == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
final Dialog dialog = getDialog();
|
||||
Window window = dialog.getWindow();
|
||||
WindowManager.LayoutParams params = window.getAttributes();
|
||||
|
||||
// Make the dialog fullscreen
|
||||
params.width = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
params.height = WindowManager.LayoutParams.MATCH_PARENT;
|
||||
params.layoutInDisplayCutoutMode = LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS;
|
||||
params.setFitInsetsTypes(0);
|
||||
window.setAttributes(params);
|
||||
window.getDecorView().getWindowInsetsController().hide(
|
||||
WindowInsets.Type.statusBars());
|
||||
window.getDecorView().getWindowInsetsController().setSystemBarsBehavior(
|
||||
WindowInsetsController.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE);
|
||||
window.setBackgroundDrawableResource(android.R.color.black);
|
||||
|
||||
final TextView message =
|
||||
dialog.findViewById(R.id.udfps_fingerprint_sensor_message);
|
||||
final Vibrator vibrator = getContext().getSystemService(Vibrator.class);
|
||||
final FingerprintManager fpm = Utils.getFingerprintManagerOrNull(getContext());
|
||||
mCancellationSignal = new CancellationSignal();
|
||||
fpm.authenticate(
|
||||
null /* crypto */,
|
||||
mCancellationSignal,
|
||||
new FingerprintManager.AuthenticationCallback() {
|
||||
@Override
|
||||
public void onAuthenticationError(
|
||||
int errorCode, @NonNull CharSequence errString) {
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationSucceeded(
|
||||
@NonNull FingerprintManager.AuthenticationResult result) {
|
||||
int fingerId = result.getFingerprint().getBiometricId();
|
||||
FingerprintSettingsFragment parent =
|
||||
(FingerprintSettingsFragment) getTargetFragment();
|
||||
parent.highlightFingerprintItem(fingerId);
|
||||
dialog.dismiss();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onAuthenticationFailed() {
|
||||
vibrator.vibrate(
|
||||
VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK));
|
||||
message.setText(R.string.fingerprint_check_enroll_not_recognized);
|
||||
message.postDelayed(() -> {
|
||||
message.setText(R.string.fingerprint_check_enroll_touch_sensor);
|
||||
}, 2000);
|
||||
}
|
||||
},
|
||||
null /* handler */,
|
||||
mUserId);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(@NonNull DialogInterface dialog) {
|
||||
super.onDismiss(dialog);
|
||||
if (mCancellationSignal != null) {
|
||||
mCancellationSignal.cancel();
|
||||
mCancellationSignal = null;
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getMetricsCategory() {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public static class RenameDialog extends InstrumentedDialogFragment {
|
||||
|
||||
private Fingerprint mFp;
|
||||
|
||||
@@ -0,0 +1,186 @@
|
||||
/*
|
||||
* 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.fingerprint;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.Log;
|
||||
import android.util.RotationUtils;
|
||||
import android.view.DisplayInfo;
|
||||
import android.view.Surface;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.RelativeLayout;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.systemui.biometrics.UdfpsUtils;
|
||||
import com.android.systemui.biometrics.shared.model.UdfpsOverlayParams;
|
||||
|
||||
/**
|
||||
* View corresponding with fingerprint_check_enrolled_dialog.xml
|
||||
*/
|
||||
public class UdfpsCheckEnrolledView extends RelativeLayout {
|
||||
private static final String TAG = "UdfpsCheckEnrolledView";
|
||||
@NonNull
|
||||
private final UdfpsFingerprintDrawable mFingerprintDrawable;
|
||||
private ImageView mFingerprintView;
|
||||
private UdfpsUtils mUdfpsUtils;
|
||||
|
||||
private @Nullable Rect mSensorRect;
|
||||
private @Nullable UdfpsOverlayParams mOverlayParams;
|
||||
private @Nullable FingerprintSensorPropertiesInternal mSensorProperties;
|
||||
|
||||
|
||||
public UdfpsCheckEnrolledView(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
mFingerprintDrawable = new UdfpsFingerprintDrawable(mContext, attrs);
|
||||
mUdfpsUtils = new UdfpsUtils();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onFinishInflate() {
|
||||
super.onFinishInflate();
|
||||
mFingerprintView = findViewById(R.id.udfps_fingerprint_sensor_view);
|
||||
mFingerprintView.setImageDrawable(mFingerprintDrawable);
|
||||
}
|
||||
|
||||
/**
|
||||
* setup SensorProperties
|
||||
*/
|
||||
public void setSensorProperties(@Nullable FingerprintSensorPropertiesInternal properties) {
|
||||
mSensorProperties = properties;
|
||||
updateOverlayParams();
|
||||
}
|
||||
|
||||
private void onSensorRectUpdated() {
|
||||
updateDimensions();
|
||||
|
||||
if (mSensorRect == null || mOverlayParams == null) {
|
||||
Log.e(TAG, "Fail to onSensorRectUpdated, mSensorRect/mOverlayParams null");
|
||||
return;
|
||||
}
|
||||
|
||||
// Updates sensor rect in relation to the overlay view
|
||||
mSensorRect.set(0, 0,
|
||||
mOverlayParams.getSensorBounds().width(),
|
||||
mOverlayParams.getSensorBounds().height());
|
||||
mFingerprintDrawable.onSensorRectUpdated(new RectF(mSensorRect));
|
||||
}
|
||||
|
||||
private void updateDimensions() {
|
||||
if (mOverlayParams == null) {
|
||||
Log.e(TAG, "Fail to updateDimensions for " + this + ", mOverlayParams null");
|
||||
return;
|
||||
}
|
||||
// Original sensorBounds assume portrait mode.
|
||||
final Rect rotatedBounds = new Rect(mOverlayParams.getSensorBounds());
|
||||
int rotation = mOverlayParams.getRotation();
|
||||
if (rotation == Surface.ROTATION_90 || rotation == Surface.ROTATION_270) {
|
||||
RotationUtils.rotateBounds(
|
||||
rotatedBounds,
|
||||
mOverlayParams.getNaturalDisplayWidth(),
|
||||
mOverlayParams.getNaturalDisplayHeight(),
|
||||
rotation
|
||||
);
|
||||
}
|
||||
|
||||
RelativeLayout parent = ((RelativeLayout) getParent());
|
||||
if (parent == null) {
|
||||
Log.e(TAG, "Fail to updateDimensions for " + this + ", parent null");
|
||||
return;
|
||||
}
|
||||
final int[] coords = parent.getLocationOnScreen();
|
||||
final int parentLeft = coords[0];
|
||||
final int parentTop = coords[1];
|
||||
final int parentRight = parentLeft + parent.getWidth();
|
||||
|
||||
// Update container view LayoutParams
|
||||
RelativeLayout.LayoutParams checkEnrolledViewLp =
|
||||
new RelativeLayout.LayoutParams(getWidth(), getHeight());
|
||||
checkEnrolledViewLp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
|
||||
if (rotation == Surface.ROTATION_90) {
|
||||
checkEnrolledViewLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
checkEnrolledViewLp.width =
|
||||
rotatedBounds.width() + 2 * (parentRight - rotatedBounds.right);
|
||||
} else {
|
||||
checkEnrolledViewLp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
|
||||
checkEnrolledViewLp.width = rotatedBounds.width() + 2 * rotatedBounds.left;
|
||||
}
|
||||
setLayoutParams(checkEnrolledViewLp);
|
||||
|
||||
// Update fingerprint view LayoutParams
|
||||
RelativeLayout.LayoutParams fingerprintViewLp = new RelativeLayout.LayoutParams(
|
||||
rotatedBounds.width(), rotatedBounds.height());
|
||||
fingerprintViewLp.addRule(RelativeLayout.ALIGN_PARENT_TOP);
|
||||
fingerprintViewLp.topMargin = rotatedBounds.top - parentTop;
|
||||
if (rotation == Surface.ROTATION_90) {
|
||||
fingerprintViewLp.addRule(RelativeLayout.ALIGN_PARENT_RIGHT);
|
||||
fingerprintViewLp.rightMargin = parentRight - rotatedBounds.right;
|
||||
} else {
|
||||
fingerprintViewLp.addRule(RelativeLayout.ALIGN_PARENT_LEFT);
|
||||
fingerprintViewLp.leftMargin = rotatedBounds.left - parentLeft;
|
||||
}
|
||||
mFingerprintView.setLayoutParams(fingerprintViewLp);
|
||||
}
|
||||
|
||||
private void updateOverlayParams() {
|
||||
|
||||
if (mSensorProperties == null) {
|
||||
android.util.Log.e(TAG, "There is no sensor info!");
|
||||
return;
|
||||
}
|
||||
|
||||
DisplayInfo displayInfo = new DisplayInfo();
|
||||
if (getDisplay() == null) {
|
||||
android.util.Log.e(TAG, "Can not get display");
|
||||
return;
|
||||
}
|
||||
getDisplay().getDisplayInfo(displayInfo);
|
||||
Rect udfpsBounds = mSensorProperties.getLocation().getRect();
|
||||
float scaleFactor = mUdfpsUtils.getScaleFactor(displayInfo);
|
||||
udfpsBounds.scale(scaleFactor);
|
||||
|
||||
final Rect overlayBounds = new Rect(
|
||||
0, /* left */
|
||||
displayInfo.getNaturalHeight() / 2, /* top */
|
||||
displayInfo.getNaturalWidth(), /* right */
|
||||
displayInfo.getNaturalHeight() /* botom */);
|
||||
|
||||
mOverlayParams = new UdfpsOverlayParams(
|
||||
udfpsBounds,
|
||||
overlayBounds,
|
||||
displayInfo.getNaturalWidth(),
|
||||
displayInfo.getNaturalHeight(),
|
||||
scaleFactor,
|
||||
displayInfo.rotation,
|
||||
mSensorProperties.sensorType);
|
||||
|
||||
post(() -> {
|
||||
if (mOverlayParams == null) {
|
||||
Log.e(TAG, "Fail to updateOverlayParams, mOverlayParams null");
|
||||
return;
|
||||
}
|
||||
mSensorRect = new Rect(mOverlayParams.getSensorBounds());
|
||||
onSensorRectUpdated();
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,138 @@
|
||||
/*
|
||||
* 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.fingerprint;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.Canvas;
|
||||
import android.graphics.ColorFilter;
|
||||
import android.graphics.Paint;
|
||||
import android.graphics.PixelFormat;
|
||||
import android.graphics.Rect;
|
||||
import android.graphics.RectF;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.graphics.drawable.ShapeDrawable;
|
||||
import android.graphics.drawable.shapes.PathShape;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.PathParser;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.Nullable;
|
||||
|
||||
import com.android.settings.R;
|
||||
|
||||
/**
|
||||
* UDFPS fingerprint drawable
|
||||
*/
|
||||
public class UdfpsFingerprintDrawable extends Drawable {
|
||||
private static final String TAG = "UdfpsFingerprintDrawable";
|
||||
|
||||
private static final float DEFAULT_STROKE_WIDTH = 3f;
|
||||
|
||||
@NonNull
|
||||
private final Paint mSensorOutlinePaint;
|
||||
@NonNull
|
||||
private final ShapeDrawable mFingerprintDrawable;
|
||||
private int mAlpha;
|
||||
|
||||
@Nullable
|
||||
private RectF mSensorRect;
|
||||
private int mEnrollIcon;
|
||||
private int mOutlineColor;
|
||||
|
||||
UdfpsFingerprintDrawable(@NonNull Context context, @Nullable AttributeSet attrs) {
|
||||
mFingerprintDrawable = defaultFactory(context);
|
||||
|
||||
loadResources(context, attrs);
|
||||
mSensorOutlinePaint = new Paint(0 /* flags */);
|
||||
mSensorOutlinePaint.setAntiAlias(true);
|
||||
mSensorOutlinePaint.setColor(mOutlineColor);
|
||||
mSensorOutlinePaint.setStyle(Paint.Style.FILL);
|
||||
|
||||
mFingerprintDrawable.setTint(mEnrollIcon);
|
||||
|
||||
setAlpha(255);
|
||||
}
|
||||
|
||||
/** The [sensorRect] coordinates for the sensor area. */
|
||||
void onSensorRectUpdated(@NonNull RectF sensorRect) {
|
||||
int margin = ((int) sensorRect.height()) / 8;
|
||||
Rect bounds = new Rect((int) (sensorRect.left) + margin, (int) (sensorRect.top) + margin,
|
||||
(int) (sensorRect.right) - margin, (int) (sensorRect.bottom) - margin);
|
||||
updateFingerprintIconBounds(bounds);
|
||||
mSensorRect = sensorRect;
|
||||
}
|
||||
|
||||
void updateFingerprintIconBounds(@NonNull Rect bounds) {
|
||||
mFingerprintDrawable.setBounds(bounds);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void draw(@NonNull Canvas canvas) {
|
||||
if (mSensorRect != null) {
|
||||
canvas.drawOval(mSensorRect, mSensorOutlinePaint);
|
||||
}
|
||||
mFingerprintDrawable.draw(canvas);
|
||||
mFingerprintDrawable.setAlpha(getAlpha());
|
||||
mSensorOutlinePaint.setAlpha(getAlpha());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setAlpha(int alpha) {
|
||||
mAlpha = alpha;
|
||||
mFingerprintDrawable.setAlpha(alpha);
|
||||
mSensorOutlinePaint.setAlpha(alpha);
|
||||
invalidateSelf();
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getAlpha() {
|
||||
return mAlpha;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setColorFilter(@Nullable ColorFilter colorFilter) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public int getOpacity() {
|
||||
return PixelFormat.OPAQUE;
|
||||
}
|
||||
|
||||
private ShapeDrawable defaultFactory(Context context) {
|
||||
String fpPath = context.getResources().getString(R.string.config_udfpsIcon);
|
||||
ShapeDrawable drawable = new ShapeDrawable(
|
||||
new PathShape(PathParser.createPathFromPathData(fpPath), 72f, 72f)
|
||||
);
|
||||
drawable.mutate();
|
||||
drawable.getPaint().setStyle(Paint.Style.STROKE);
|
||||
drawable.getPaint().setStrokeCap(Paint.Cap.ROUND);
|
||||
drawable.getPaint().setStrokeWidth(DEFAULT_STROKE_WIDTH);
|
||||
return drawable;
|
||||
}
|
||||
|
||||
private void loadResources(Context context, @Nullable AttributeSet attrs) {
|
||||
final TypedArray ta = context.obtainStyledAttributes(attrs,
|
||||
R.styleable.BiometricsEnrollView, R.attr.biometricsEnrollStyle,
|
||||
R.style.BiometricsEnrollStyle);
|
||||
mEnrollIcon = ta.getColor(R.styleable.BiometricsEnrollView_biometricsEnrollIcon, 0);
|
||||
mOutlineColor = ta.getColor(
|
||||
R.styleable.BiometricsEnrollView_biometricsMovingTargetFill, 0);
|
||||
ta.recycle();
|
||||
}
|
||||
}
|
||||
@@ -626,6 +626,11 @@ public class AdvancedBluetoothDetailsHeaderController extends BasePreferenceCont
|
||||
imageView.setLayoutParams(layoutParams);
|
||||
} else {
|
||||
imageView.setImageDrawable(createBtBatteryIcon(mContext, level, charging));
|
||||
imageView.setContentDescription(
|
||||
mContext.getString(
|
||||
charging
|
||||
? R.string.device_details_battery_charging
|
||||
: R.string.device_details_battery));
|
||||
LinearLayout.LayoutParams layoutParams = new LinearLayout.LayoutParams(
|
||||
ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
|
||||
imageView.setLayoutParams(layoutParams);
|
||||
|
||||
@@ -269,9 +269,7 @@ public class SetupChooseLockGeneric extends ChooseLockGeneric {
|
||||
public void onViewCreated(@NotNull View view, Bundle savedInstanceState) {
|
||||
super.onViewCreated(view, savedInstanceState);
|
||||
GlifPreferenceLayout layout = (GlifPreferenceLayout) view;
|
||||
int titleResource = R.string.lock_settings_picker_new_lock_title;
|
||||
|
||||
layout.setHeaderText(titleResource);
|
||||
setDivider(new ColorDrawable(Color.TRANSPARENT));
|
||||
setDividerHeight(0);
|
||||
getHeaderView().setVisible(false);
|
||||
|
||||
@@ -167,7 +167,7 @@ private fun ApplicationInfo.isShowUninstallUpdates(context: Context): Boolean =
|
||||
private fun ApplicationInfo.isShowUninstallForAllUsers(
|
||||
userManager: UserManager,
|
||||
packageManagers: IPackageManagers,
|
||||
): Boolean = userId == 0 && !isSystemApp && !isInstantApp &&
|
||||
): Boolean = userManager.isUserAdmin(userId) && !isSystemApp && !isInstantApp &&
|
||||
isOtherUserHasInstallPackage(userManager, packageManagers)
|
||||
|
||||
private fun ApplicationInfo.isOtherUserHasInstallPackage(
|
||||
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* 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.network
|
||||
|
||||
import androidx.annotation.VisibleForTesting
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.telephony.MobileDataRepository
|
||||
import com.android.settings.network.telephony.subscriptionManager
|
||||
import com.android.settingslib.spa.framework.compose.rememberContext
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreference
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun MobileDataSwitchPreference(subId: Int) {
|
||||
MobileDataSwitchPreference(
|
||||
subId = subId,
|
||||
mobileDataRepository = rememberContext(::MobileDataRepository),
|
||||
setMobileData = setMobileDataImpl(subId),
|
||||
)
|
||||
}
|
||||
|
||||
@VisibleForTesting
|
||||
@Composable
|
||||
fun MobileDataSwitchPreference(
|
||||
subId: Int,
|
||||
mobileDataRepository: MobileDataRepository,
|
||||
setMobileData: (newChecked: Boolean) -> Unit,
|
||||
) {
|
||||
val mobileDataSummary = stringResource(id = R.string.mobile_data_settings_summary)
|
||||
val isMobileDataEnabled by
|
||||
remember(subId) { mobileDataRepository.isMobileDataEnabledFlow(subId) }
|
||||
.collectAsStateWithLifecycle(initialValue = null)
|
||||
|
||||
SwitchPreference(
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = stringResource(id = R.string.mobile_data_settings_title)
|
||||
override val summary = { mobileDataSummary }
|
||||
override val checked = { isMobileDataEnabled }
|
||||
override val onCheckedChange = setMobileData
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun setMobileDataImpl(subId: Int): (newChecked: Boolean) -> Unit {
|
||||
val context = LocalContext.current
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
val wifiPickerTrackerHelper = rememberWifiPickerTrackerHelper()
|
||||
return { newEnabled ->
|
||||
coroutineScope.launch(Dispatchers.Default) {
|
||||
setMobileData(
|
||||
context = context,
|
||||
subscriptionManager = context.subscriptionManager,
|
||||
wifiPickerTrackerHelper = wifiPickerTrackerHelper,
|
||||
subId = subId,
|
||||
enabled = newEnabled,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,48 +0,0 @@
|
||||
/*
|
||||
* 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.network
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.android.settings.R
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreference
|
||||
import com.android.settingslib.spa.widget.preference.SwitchPreferenceModel
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@Composable
|
||||
fun MobileDataSwitchingPreference(
|
||||
isMobileDataEnabled: () -> Boolean?,
|
||||
setMobileDataEnabled: (newEnabled: Boolean) -> Unit,
|
||||
) {
|
||||
val mobileDataSummary = stringResource(id = R.string.mobile_data_settings_summary)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
SwitchPreference(
|
||||
object : SwitchPreferenceModel {
|
||||
override val title = stringResource(id = R.string.mobile_data_settings_title)
|
||||
override val summary = { mobileDataSummary }
|
||||
override val checked = { isMobileDataEnabled() }
|
||||
override val onCheckedChange: (Boolean) -> Unit = { newEnabled ->
|
||||
coroutineScope.launch(Dispatchers.Default) {
|
||||
setMobileDataEnabled(newEnabled)
|
||||
}
|
||||
}
|
||||
override val changeable:() -> Boolean = {true}
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -29,6 +29,7 @@ import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.automirrored.outlined.Message
|
||||
import androidx.compose.material.icons.outlined.DataUsage
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.MutableIntState
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableIntStateOf
|
||||
@@ -40,7 +41,6 @@ import androidx.compose.ui.graphics.vector.ImageVector
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.res.vectorResource
|
||||
import androidx.lifecycle.LifecycleOwner
|
||||
import androidx.lifecycle.LifecycleRegistry
|
||||
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||
import androidx.lifecycle.compose.collectAsStateWithLifecycle
|
||||
@@ -60,7 +60,6 @@ import com.android.settingslib.spa.framework.common.SettingsPageProvider
|
||||
import com.android.settingslib.spa.framework.common.createSettingsPage
|
||||
import com.android.settingslib.spa.framework.compose.navigator
|
||||
import com.android.settingslib.spa.framework.compose.rememberContext
|
||||
import com.android.settingslib.spa.framework.util.collectLatestWithLifecycle
|
||||
import com.android.settingslib.spa.widget.preference.Preference
|
||||
import com.android.settingslib.spa.widget.preference.PreferenceModel
|
||||
import com.android.settingslib.spa.widget.scaffold.RegularScaffold
|
||||
@@ -110,51 +109,48 @@ open class NetworkCellularGroupProvider : SettingsPageProvider, SearchablePage {
|
||||
var textsSelectedId = rememberSaveable {
|
||||
mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
|
||||
}
|
||||
var mobileDataSelectedId = rememberSaveable {
|
||||
mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
|
||||
}
|
||||
val mobileDataSelectedId = rememberSaveable { mutableStateOf<Int?>(null) }
|
||||
var nonDdsRemember = rememberSaveable {
|
||||
mutableIntStateOf(SubscriptionManager.INVALID_SUBSCRIPTION_ID)
|
||||
}
|
||||
var showMobileDataSection = rememberSaveable {
|
||||
mutableStateOf(false)
|
||||
}
|
||||
val subscriptionViewModel = viewModel<SubscriptionInfoListViewModel>()
|
||||
|
||||
CollectAirplaneModeAndFinishIfOn()
|
||||
|
||||
remember {
|
||||
allOfFlows(context, subscriptionViewModel.selectableSubscriptionInfoListFlow)
|
||||
}.collectLatestWithLifecycle(LocalLifecycleOwner.current) {
|
||||
callsSelectedId.intValue = defaultVoiceSubId
|
||||
textsSelectedId.intValue = defaultSmsSubId
|
||||
mobileDataSelectedId.intValue = defaultDataSubId
|
||||
nonDdsRemember.intValue = nonDds
|
||||
LaunchedEffect(Unit) {
|
||||
allOfFlows(context, subscriptionViewModel.selectableSubscriptionInfoListFlow).collect {
|
||||
callsSelectedId.intValue = defaultVoiceSubId
|
||||
textsSelectedId.intValue = defaultSmsSubId
|
||||
mobileDataSelectedId.value = defaultDataSubId
|
||||
nonDdsRemember.intValue = nonDds
|
||||
}
|
||||
}
|
||||
|
||||
val selectableSubscriptionInfoList by subscriptionViewModel
|
||||
.selectableSubscriptionInfoListFlow
|
||||
.collectAsStateWithLifecycle(initialValue = emptyList())
|
||||
showMobileDataSection.value = selectableSubscriptionInfoList
|
||||
.filter { subInfo -> subInfo.simSlotIndex > -1 }
|
||||
.size > 0
|
||||
val stringSims = stringResource(R.string.provider_network_settings_title)
|
||||
RegularScaffold(title = stringSims) {
|
||||
|
||||
RegularScaffold(title = stringResource(R.string.provider_network_settings_title)) {
|
||||
SimsSection(selectableSubscriptionInfoList)
|
||||
if(showMobileDataSection.value) {
|
||||
MobileDataSectionImpl(
|
||||
mobileDataSelectedId,
|
||||
nonDdsRemember,
|
||||
val mobileDataSelectedIdValue = mobileDataSelectedId.value
|
||||
// Avoid draw mobile data UI before data ready to reduce flaky
|
||||
if (mobileDataSelectedIdValue != null) {
|
||||
val showMobileDataSection =
|
||||
selectableSubscriptionInfoList.any { subInfo -> subInfo.simSlotIndex > -1 }
|
||||
if (showMobileDataSection) {
|
||||
MobileDataSectionImpl(mobileDataSelectedIdValue, nonDdsRemember.intValue)
|
||||
}
|
||||
|
||||
PrimarySimSectionImpl(
|
||||
subscriptionViewModel.selectableSubscriptionInfoListFlow,
|
||||
callsSelectedId,
|
||||
textsSelectedId,
|
||||
remember(mobileDataSelectedIdValue) {
|
||||
mutableIntStateOf(mobileDataSelectedIdValue)
|
||||
},
|
||||
)
|
||||
}
|
||||
|
||||
PrimarySimSectionImpl(
|
||||
subscriptionViewModel.selectableSubscriptionInfoListFlow,
|
||||
callsSelectedId,
|
||||
textsSelectedId,
|
||||
mobileDataSelectedId,
|
||||
)
|
||||
|
||||
OtherSection()
|
||||
}
|
||||
}
|
||||
@@ -217,46 +213,23 @@ open class NetworkCellularGroupProvider : SettingsPageProvider, SearchablePage {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MobileDataSectionImpl(
|
||||
mobileDataSelectedId: MutableIntState,
|
||||
nonDds: MutableIntState,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val localLifecycleOwner = LocalLifecycleOwner.current
|
||||
fun MobileDataSectionImpl(mobileDataSelectedId: Int, nonDds: Int) {
|
||||
val mobileDataRepository = rememberContext(::MobileDataRepository)
|
||||
|
||||
Category(title = stringResource(id = R.string.mobile_data_settings_title)) {
|
||||
val isAutoDataEnabled by remember(nonDds.intValue) {
|
||||
MobileDataSwitchPreference(subId = mobileDataSelectedId)
|
||||
|
||||
val isAutoDataEnabled by remember(nonDds) {
|
||||
mobileDataRepository.isMobileDataPolicyEnabledFlow(
|
||||
subId = nonDds.intValue,
|
||||
subId = nonDds,
|
||||
policy = TelephonyManager.MOBILE_DATA_POLICY_AUTO_DATA_SWITCH
|
||||
)
|
||||
}.collectAsStateWithLifecycle(initialValue = null)
|
||||
|
||||
val mobileDataStateChanged by remember(mobileDataSelectedId.intValue) {
|
||||
mobileDataRepository.isMobileDataEnabledFlow(mobileDataSelectedId.intValue)
|
||||
}.collectAsStateWithLifecycle(initialValue = false)
|
||||
val coroutineScope = rememberCoroutineScope()
|
||||
|
||||
MobileDataSwitchingPreference(
|
||||
isMobileDataEnabled = { mobileDataStateChanged },
|
||||
setMobileDataEnabled = { newEnabled ->
|
||||
coroutineScope.launch {
|
||||
setMobileData(
|
||||
context,
|
||||
context.getSystemService(SubscriptionManager::class.java),
|
||||
getWifiPickerTrackerHelper(context, localLifecycleOwner),
|
||||
mobileDataSelectedId.intValue,
|
||||
newEnabled
|
||||
)
|
||||
}
|
||||
},
|
||||
)
|
||||
if (nonDds.intValue != SubscriptionManager.INVALID_SUBSCRIPTION_ID) {
|
||||
if (SubscriptionManager.isValidSubscriptionId(nonDds)) {
|
||||
AutomaticDataSwitchingPreference(
|
||||
isAutoDataEnabled = { isAutoDataEnabled },
|
||||
setAutoDataEnabled = { newEnabled ->
|
||||
mobileDataRepository.setAutoDataSwitch(nonDds.intValue, newEnabled)
|
||||
mobileDataRepository.setAutoDataSwitch(nonDds, newEnabled)
|
||||
},
|
||||
)
|
||||
}
|
||||
@@ -328,9 +301,6 @@ fun PrimarySimSectionImpl(
|
||||
mobileDataSelectedId: MutableIntState,
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val localLifecycleOwner = LocalLifecycleOwner.current
|
||||
val wifiPickerTrackerHelper = getWifiPickerTrackerHelper(context, localLifecycleOwner)
|
||||
|
||||
val primarySimInfo = remember(subscriptionInfoListFlow) {
|
||||
subscriptionInfoListFlow
|
||||
.map { subscriptionInfoList ->
|
||||
@@ -346,7 +316,7 @@ fun PrimarySimSectionImpl(
|
||||
callsSelectedId,
|
||||
textsSelectedId,
|
||||
mobileDataSelectedId,
|
||||
wifiPickerTrackerHelper
|
||||
rememberWifiPickerTrackerHelper()
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -354,22 +324,21 @@ fun PrimarySimSectionImpl(
|
||||
@Composable
|
||||
fun CollectAirplaneModeAndFinishIfOn() {
|
||||
val context = LocalContext.current
|
||||
context.settingsGlobalBooleanFlow(Settings.Global.AIRPLANE_MODE_ON)
|
||||
.collectLatestWithLifecycle(LocalLifecycleOwner.current) { isAirplaneModeOn ->
|
||||
LaunchedEffect(Unit) {
|
||||
context.settingsGlobalBooleanFlow(Settings.Global.AIRPLANE_MODE_ON).collect {
|
||||
isAirplaneModeOn ->
|
||||
if (isAirplaneModeOn) {
|
||||
context.getActivity()?.finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun getWifiPickerTrackerHelper(
|
||||
context: Context,
|
||||
lifecycleOwner: LifecycleOwner
|
||||
): WifiPickerTrackerHelper {
|
||||
return WifiPickerTrackerHelper(
|
||||
LifecycleRegistry(lifecycleOwner), context,
|
||||
null /* WifiPickerTrackerCallback */
|
||||
)
|
||||
@Composable
|
||||
fun rememberWifiPickerTrackerHelper(): WifiPickerTrackerHelper {
|
||||
val context = LocalContext.current
|
||||
val lifecycleOwner = LocalLifecycleOwner.current
|
||||
return remember { WifiPickerTrackerHelper(LifecycleRegistry(lifecycleOwner), context, null) }
|
||||
}
|
||||
|
||||
private fun Context.defaultVoiceSubscriptionFlow(): Flow<Int> =
|
||||
|
||||
@@ -20,7 +20,9 @@ import android.app.settings.SettingsEnums;
|
||||
import android.content.Context;
|
||||
import android.net.wifi.SoftApConfiguration;
|
||||
import android.text.TextUtils;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.Preference;
|
||||
@@ -36,7 +38,8 @@ import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
* Controller for logic pertaining to the password of Wi-Fi tethering.
|
||||
*/
|
||||
public class WifiTetherPasswordPreferenceController extends WifiTetherBasePreferenceController
|
||||
implements ValidatedEditTextPreference.Validator {
|
||||
implements ValidatedEditTextPreference.Validator,
|
||||
EditTextPreference.OnBindEditTextListener {
|
||||
|
||||
private static final String PREF_KEY = "wifi_tether_network_password";
|
||||
|
||||
@@ -80,6 +83,7 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer
|
||||
((ValidatedEditTextPreference) mPreference).setValidator(this);
|
||||
((ValidatedEditTextPreference) mPreference).setIsPassword(true);
|
||||
((ValidatedEditTextPreference) mPreference).setIsSummaryPassword(true);
|
||||
((EditTextPreference) mPreference).setOnBindEditTextListener(this);
|
||||
updatePasswordDisplay((EditTextPreference) mPreference);
|
||||
}
|
||||
|
||||
@@ -143,4 +147,9 @@ public class WifiTetherPasswordPreferenceController extends WifiTetherBasePrefer
|
||||
pref.setVisible(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindEditText(@NonNull EditText editText) {
|
||||
editText.setHint(R.string.wifi_hotspot_password_title);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -22,18 +22,22 @@ import android.content.Intent;
|
||||
import android.net.wifi.SoftApConfiguration;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.annotation.NonNull;
|
||||
import androidx.annotation.VisibleForTesting;
|
||||
import androidx.preference.EditTextPreference;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.android.settings.R;
|
||||
import com.android.settings.overlay.FeatureFactory;
|
||||
import com.android.settings.widget.ValidatedEditTextPreference;
|
||||
import com.android.settings.wifi.dpp.WifiDppUtils;
|
||||
import com.android.settingslib.core.instrumentation.MetricsFeatureProvider;
|
||||
|
||||
public class WifiTetherSSIDPreferenceController extends WifiTetherBasePreferenceController
|
||||
implements ValidatedEditTextPreference.Validator {
|
||||
implements ValidatedEditTextPreference.Validator,
|
||||
EditTextPreference.OnBindEditTextListener {
|
||||
|
||||
private static final String TAG = "WifiTetherSsidPref";
|
||||
private static final String PREF_KEY = "wifi_tether_network_name";
|
||||
@@ -93,6 +97,7 @@ public class WifiTetherSSIDPreferenceController extends WifiTetherBasePreference
|
||||
((WifiTetherSsidPreference) mPreference).setButtonVisible(false);
|
||||
}
|
||||
|
||||
((EditTextPreference) mPreference).setOnBindEditTextListener(this);
|
||||
updateSsidDisplay((EditTextPreference) mPreference);
|
||||
}
|
||||
|
||||
@@ -138,4 +143,9 @@ public class WifiTetherSSIDPreferenceController extends WifiTetherBasePreference
|
||||
boolean isQrCodeButtonAvailable() {
|
||||
return ((WifiTetherSsidPreference) mPreference).isQrCodeButtonAvailable();
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onBindEditText(@NonNull EditText editText) {
|
||||
editText.setHint(R.string.wifi_hotspot_name_title);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
package com.android.settings.biometrics.fingerprint;
|
||||
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_REAR;
|
||||
import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_UDFPS_OPTICAL;
|
||||
|
||||
import static com.android.settings.biometrics.BiometricEnrollBase.BIOMETRIC_AUTH_REQUEST;
|
||||
@@ -354,6 +355,48 @@ public class FingerprintSettingsFragmentTest {
|
||||
assertThat(addPref.isEnabled()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
|
||||
public void testCheckEnrolledShown_whenAtLeastOneFingerprintEnrolled_Udfps() {
|
||||
final Fingerprint fingerprint = new Fingerprint("Test", 0, 0);
|
||||
doReturn(List.of(fingerprint)).when(mFingerprintManager).getEnrolledFingerprints(anyInt());
|
||||
setUpFragment(false, PRIMARY_USER_ID, TYPE_UDFPS_OPTICAL, 5);
|
||||
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
final Preference checkEnrolledPerf =
|
||||
mFragment.findPreference("key_fingerprint_check_enrolled");
|
||||
assertThat(checkEnrolledPerf).isNotNull();
|
||||
assertThat(checkEnrolledPerf.isVisible()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
|
||||
public void testCheckEnrolledHide_whenNoFingerprintEnrolled_Udfps() {
|
||||
doReturn(List.of()).when(mFingerprintManager).getEnrolledFingerprints(anyInt());
|
||||
setUpFragment(false, PRIMARY_USER_ID, TYPE_UDFPS_OPTICAL, 5);
|
||||
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
final Preference checkEnrolledPerf =
|
||||
mFragment.findPreference("key_fingerprint_check_enrolled");
|
||||
assertThat(checkEnrolledPerf).isNull();
|
||||
}
|
||||
|
||||
@Test
|
||||
@EnableFlags(com.android.settings.flags.Flags.FLAG_BIOMETRICS_ONBOARDING_EDUCATION)
|
||||
public void testCheckEnrolledHide_nonUdfps() {
|
||||
final Fingerprint fingerprint = new Fingerprint("Test", 0, 0);
|
||||
doReturn(List.of(fingerprint)).when(mFingerprintManager).getEnrolledFingerprints(anyInt());
|
||||
setUpFragment(false, PRIMARY_USER_ID, TYPE_REAR, 5);
|
||||
|
||||
shadowOf(Looper.getMainLooper()).idle();
|
||||
|
||||
final Preference checkEnrolledPerf =
|
||||
mFragment.findPreference("key_fingerprint_check_enrolled");
|
||||
assertThat(checkEnrolledPerf).isNull();
|
||||
}
|
||||
|
||||
private void setSensor(@FingerprintSensorProperties.SensorType int sensorType,
|
||||
int maxFingerprints) {
|
||||
final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
|
||||
|
||||
@@ -329,11 +329,16 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
|
||||
|
||||
mController.refresh();
|
||||
|
||||
assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_left),
|
||||
R.drawable.ic_battery_alert_24dp);
|
||||
assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_right), /* resId= */-1);
|
||||
assertBatteryIcon(mLayoutPreference.findViewById(R.id.layout_middle),
|
||||
R.drawable.ic_battery_alert_24dp);
|
||||
assertBatteryIcon(
|
||||
mLayoutPreference.findViewById(R.id.layout_left),
|
||||
R.drawable.ic_battery_alert_24dp,
|
||||
false);
|
||||
assertBatteryIcon(
|
||||
mLayoutPreference.findViewById(R.id.layout_right), /* resId= */ -1, true);
|
||||
assertBatteryIcon(
|
||||
mLayoutPreference.findViewById(R.id.layout_middle),
|
||||
R.drawable.ic_battery_alert_24dp,
|
||||
false);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -546,10 +551,15 @@ public class AdvancedBluetoothDetailsHeaderControllerTest {
|
||||
}
|
||||
}
|
||||
|
||||
private void assertBatteryIcon(LinearLayout linearLayout, int resId) {
|
||||
private void assertBatteryIcon(LinearLayout linearLayout, int resId, boolean charging) {
|
||||
final ImageView imageView = linearLayout.findViewById(R.id.bt_battery_icon);
|
||||
assertThat(shadowOf(imageView.getDrawable()).getCreatedFromResId())
|
||||
.isEqualTo(resId);
|
||||
if (charging) {
|
||||
assertThat(imageView.getContentDescription().toString())
|
||||
.isEqualTo(mContext.getString(R.string.device_details_battery_charging));
|
||||
} else {
|
||||
assertThat(imageView.getContentDescription().toString())
|
||||
.isEqualTo(mContext.getString(R.string.device_details_battery));
|
||||
}
|
||||
assertThat(shadowOf(imageView.getDrawable()).getCreatedFromResId()).isEqualTo(resId);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -18,10 +18,13 @@ package com.android.settings.wifi.tether;
|
||||
|
||||
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.anyString;
|
||||
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 static org.mockito.Mockito.when;
|
||||
@@ -32,6 +35,7 @@ import android.content.Context;
|
||||
import android.net.TetheringManager;
|
||||
import android.net.wifi.SoftApConfiguration;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
@@ -81,7 +85,7 @@ public class WifiTetherPasswordPreferenceControllerTest {
|
||||
when(featureFactory.getWifiFeatureProvider().getWifiHotspotRepository())
|
||||
.thenReturn(mWifiHotspotRepository);
|
||||
|
||||
mPreference = new ValidatedEditTextPreference(RuntimeEnvironment.application);
|
||||
mPreference = spy(new ValidatedEditTextPreference(RuntimeEnvironment.application));
|
||||
mConfig = new SoftApConfiguration.Builder().setSsid("test_1234")
|
||||
.setPassphrase(INITIAL_PASSWORD, SoftApConfiguration.SECURITY_TYPE_WPA2_PSK)
|
||||
.build();
|
||||
@@ -179,4 +183,20 @@ public class WifiTetherPasswordPreferenceControllerTest {
|
||||
mController.updateDisplay();
|
||||
assertThat(mPreference.isPassword()).isTrue();
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateDisplay_shouldSetOnBindEditTextListener() {
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
verify(mPreference).setOnBindEditTextListener(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindEditText_shouldSetHint() {
|
||||
EditText editText = mock(EditText.class);
|
||||
|
||||
mController.onBindEditText(editText);
|
||||
|
||||
verify(editText).setHint(anyInt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -18,10 +18,13 @@ package com.android.settings.wifi.tether;
|
||||
|
||||
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.anyString;
|
||||
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 static org.mockito.Mockito.when;
|
||||
@@ -32,6 +35,7 @@ import android.content.Context;
|
||||
import android.net.TetheringManager;
|
||||
import android.net.wifi.SoftApConfiguration;
|
||||
import android.net.wifi.WifiManager;
|
||||
import android.widget.EditText;
|
||||
|
||||
import androidx.preference.PreferenceScreen;
|
||||
|
||||
@@ -68,7 +72,7 @@ public class WifiTetherSSIDPreferenceControllerTest {
|
||||
@Before
|
||||
public void setUp() {
|
||||
MockitoAnnotations.initMocks(this);
|
||||
mPreference = new WifiTetherSsidPreference(RuntimeEnvironment.application);
|
||||
mPreference = spy(new WifiTetherSsidPreference(RuntimeEnvironment.application));
|
||||
|
||||
doReturn(mock(DevicePolicyManager.class)).when(mContext)
|
||||
.getSystemService(Context.DEVICE_POLICY_SERVICE);
|
||||
@@ -147,12 +151,19 @@ public class WifiTetherSSIDPreferenceControllerTest {
|
||||
assertThat(mPreference.getSummary()).isEqualTo(config.getSsid());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void updateDisplay_shouldSetOnBindEditTextListener() {
|
||||
mController.displayPreference(mScreen);
|
||||
|
||||
verify(mPreference).setOnBindEditTextListener(any());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void displayPreference_wifiApDisabled_shouldHideQrCodeIcon() {
|
||||
when(mWifiManager.isWifiApEnabled()).thenReturn(false);
|
||||
final SoftApConfiguration config = new SoftApConfiguration.Builder()
|
||||
.setSsid("test_1234").setPassphrase("test_password",
|
||||
SoftApConfiguration.SECURITY_TYPE_WPA2_PSK).build();
|
||||
SoftApConfiguration.SECURITY_TYPE_WPA2_PSK).build();
|
||||
when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
@@ -164,10 +175,19 @@ public class WifiTetherSSIDPreferenceControllerTest {
|
||||
when(mWifiManager.isWifiApEnabled()).thenReturn(true);
|
||||
final SoftApConfiguration config = new SoftApConfiguration.Builder()
|
||||
.setSsid("test_1234").setPassphrase("test_password",
|
||||
SoftApConfiguration.SECURITY_TYPE_WPA2_PSK).build();
|
||||
SoftApConfiguration.SECURITY_TYPE_WPA2_PSK).build();
|
||||
when(mWifiManager.getSoftApConfiguration()).thenReturn(config);
|
||||
|
||||
mController.displayPreference(mScreen);
|
||||
assertThat(mController.isQrCodeButtonAvailable()).isEqualTo(true);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void onBindEditText_shouldSetHint() {
|
||||
EditText editText = mock(EditText.class);
|
||||
|
||||
mController.onBindEditText(editText);
|
||||
|
||||
verify(editText).setHint(anyInt());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,6 +159,7 @@ class AppInfoSettingsMoreOptionsTest {
|
||||
packageName = PACKAGE_NAME
|
||||
uid = UID
|
||||
}
|
||||
whenever(userManager.isUserAdmin(app.userId)).thenReturn(true)
|
||||
whenever(userManager.aliveUsers).thenReturn(listOf(OTHER_USER))
|
||||
whenever(packageManagers.isPackageInstalledAsUser(PACKAGE_NAME, OTHER_USER_ID))
|
||||
.thenReturn(true)
|
||||
@@ -171,12 +172,30 @@ class AppInfoSettingsMoreOptionsTest {
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun uninstallForAllUsers_NotAdminUser_notDisplayed() {
|
||||
val app = ApplicationInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
uid = UID
|
||||
}
|
||||
whenever(userManager.isUserAdmin(app.userId)).thenReturn(false)
|
||||
whenever(userManager.aliveUsers).thenReturn(listOf(OTHER_USER))
|
||||
whenever(packageManagers.isPackageInstalledAsUser(PACKAGE_NAME, OTHER_USER_ID))
|
||||
.thenReturn(true)
|
||||
|
||||
setContent(app)
|
||||
composeTestRule.onRoot().performClick()
|
||||
|
||||
composeTestRule.onRoot().assertIsNotDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun uninstallForAllUsers_appHiddenNotInQuietModeAndPrimaryUser_displayed() {
|
||||
val app = ApplicationInfo().apply {
|
||||
packageName = PACKAGE_NAME
|
||||
uid = UID
|
||||
}
|
||||
whenever(userManager.isUserAdmin(app.userId)).thenReturn(true)
|
||||
whenever(userManager.aliveUsers).thenReturn(listOf(OTHER_USER))
|
||||
whenever(packageManagers
|
||||
.isPackageInstalledAsUser(PACKAGE_NAME, OTHER_USER_ID))
|
||||
@@ -198,6 +217,7 @@ class AppInfoSettingsMoreOptionsTest {
|
||||
packageName = PACKAGE_NAME
|
||||
uid = UID
|
||||
}
|
||||
whenever(userManager.isUserAdmin(app.userId)).thenReturn(true)
|
||||
whenever(userManager.aliveUsers).thenReturn(listOf(OTHER_USER))
|
||||
whenever(packageManagers
|
||||
.isPackageInstalledAsUser(PACKAGE_NAME, OTHER_USER_ID))
|
||||
|
||||
@@ -0,0 +1,101 @@
|
||||
/*
|
||||
* 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.network
|
||||
|
||||
import android.content.Context
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.test.assertIsDisplayed
|
||||
import androidx.compose.ui.test.junit4.createComposeRule
|
||||
import androidx.compose.ui.test.onNodeWithText
|
||||
import androidx.compose.ui.test.performClick
|
||||
import androidx.test.core.app.ApplicationProvider
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import com.android.settings.R
|
||||
import com.android.settings.network.telephony.MobileDataRepository
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import kotlinx.coroutines.flow.emptyFlow
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
import org.junit.Rule
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.kotlin.any
|
||||
import org.mockito.kotlin.doReturn
|
||||
import org.mockito.kotlin.mock
|
||||
import org.mockito.kotlin.spy
|
||||
import org.mockito.kotlin.stub
|
||||
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class MobileDataSwitchPreferenceTest {
|
||||
@get:Rule val composeTestRule = createComposeRule()
|
||||
|
||||
private val context: Context = spy(ApplicationProvider.getApplicationContext()) {}
|
||||
|
||||
private val mockMobileDataRepository =
|
||||
mock<MobileDataRepository> { on { isMobileDataEnabledFlow(any()) } doReturn emptyFlow() }
|
||||
|
||||
@Test
|
||||
fun title_displayed() {
|
||||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(LocalContext provides context) {
|
||||
MobileDataSwitchPreference(SUB_ID, mockMobileDataRepository) {}
|
||||
}
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(context.getString(R.string.mobile_data_settings_title))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun summary_displayed() {
|
||||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(LocalContext provides context) {
|
||||
MobileDataSwitchPreference(SUB_ID, mockMobileDataRepository) {}
|
||||
}
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(context.getString(R.string.mobile_data_settings_summary))
|
||||
.assertIsDisplayed()
|
||||
}
|
||||
|
||||
@Test
|
||||
fun onClick_whenOff_turnedOn() {
|
||||
mockMobileDataRepository.stub {
|
||||
on { isMobileDataEnabledFlow(SUB_ID) } doReturn flowOf(false)
|
||||
}
|
||||
var newCheckedCalled: Boolean? = null
|
||||
composeTestRule.setContent {
|
||||
CompositionLocalProvider(LocalContext provides context) {
|
||||
MobileDataSwitchPreference(SUB_ID, mockMobileDataRepository) {
|
||||
newCheckedCalled = it
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
composeTestRule
|
||||
.onNodeWithText(context.getString(R.string.mobile_data_settings_title))
|
||||
.performClick()
|
||||
|
||||
assertThat(newCheckedCalled).isTrue()
|
||||
}
|
||||
|
||||
private companion object {
|
||||
const val SUB_ID = 12
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user