diff --git a/res/layout/sfps_enroll_find_sensor_layout.xml b/res/layout/sfps_enroll_find_sensor_layout.xml
new file mode 100644
index 00000000000..fe74e583e89
--- /dev/null
+++ b/res/layout/sfps_enroll_find_sensor_layout.xml
@@ -0,0 +1,56 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/res/raw/fingerprint_edu_lottie.json b/res/raw/fingerprint_edu_lottie.json
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/res/raw/fingerprint_edu_lottie_portrait.json b/res/raw/fingerprint_edu_lottie_portrait.json
new file mode 100644
index 00000000000..e69de29bb2d
diff --git a/res/values/strings.xml b/res/values/strings.xml
index 3994678a335..0b7a3d5fccb 100644
--- a/res/values/strings.xml
+++ b/res/values/strings.xml
@@ -13328,6 +13328,11 @@
Allow access to wallet from lock screen and quick settings
+
+ Show QR Code Scanner
+
+ Allow access to QR code scanner from lock screen
+
Show device controls
diff --git a/res/xml/security_lockscreen_settings.xml b/res/xml/security_lockscreen_settings.xml
index 82cb8609114..5796f2ac553 100644
--- a/res/xml/security_lockscreen_settings.xml
+++ b/res/xml/security_lockscreen_settings.xml
@@ -51,6 +51,12 @@
android:summary="@string/lockscreen_privacy_wallet_summary"
settings:controller="com.android.settings.display.WalletPrivacyPreferenceController" />
+
+
props =
fingerprintManager.getSensorPropertiesInternal();
mCanAssumeUdfps = props != null && props.size() == 1 && props.get(0).isAnyUdfpsType();
+ mCanAssumeSidefps = props != null && props.size() == 1 && props.get(0).isAnySidefpsType();
setContentView(getContentView());
mFooterBarMixin = getLayout().getMixin(FooterBarMixin.class);
mFooterBarMixin.setSecondaryButton(
@@ -72,6 +79,8 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
.build()
);
+ listenOrientationEvent();
+
if (mCanAssumeUdfps) {
setHeaderText(R.string.security_settings_udfps_enroll_find_sensor_title);
setDescriptionText(R.string.security_settings_udfps_enroll_find_sensor_message);
@@ -90,6 +99,28 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
lottieAnimationView.setAnimation(R.raw.udfps_edu_a11y_lottie);
}
+ } else if (mCanAssumeSidefps) {
+ setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title);
+ setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message);
+ final LottieAnimationView lottieAnimationView = findViewById(R.id.illustration_lottie);
+ final LottieAnimationView lottieAnimationViewPortrait =
+ findViewById(R.id.illustration_lottie_portrait);
+ final int rotation = getApplicationContext().getDisplay().getRotation();
+ switch(rotation) {
+ case Surface.ROTATION_90:
+ lottieAnimationView.setVisibility(View.GONE);
+ lottieAnimationViewPortrait.setVisibility(View.VISIBLE);
+ break;
+ case Surface.ROTATION_270:
+ lottieAnimationView.setVisibility(View.GONE);
+ lottieAnimationViewPortrait.setVisibility(View.VISIBLE);
+ lottieAnimationViewPortrait.setRotation(180);
+ break;
+ default:
+ lottieAnimationView.setVisibility(View.VISIBLE);
+ lottieAnimationViewPortrait.setVisibility(View.GONE);
+ break;
+ }
} else {
setHeaderText(R.string.security_settings_fingerprint_enroll_find_sensor_title);
setDescriptionText(R.string.security_settings_fingerprint_enroll_find_sensor_message);
@@ -145,6 +176,8 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
protected int getContentView() {
if (mCanAssumeUdfps) {
return R.layout.udfps_enroll_find_sensor_layout;
+ } else if (mCanAssumeSidefps) {
+ return R.layout.sfps_enroll_find_sensor_layout;
}
return R.layout.fingerprint_enroll_find_sensor;
}
@@ -220,6 +253,7 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
@Override
protected void onDestroy() {
+ stopListenOrientationEvent();
super.onDestroy();
if (mAnimation != null) {
mAnimation.stopAnimation();
@@ -297,4 +331,37 @@ public class FingerprintEnrollFindSensor extends BiometricEnrollBase implements
public int getMetricsCategory() {
return SettingsEnums.FINGERPRINT_FIND_SENSOR;
}
+
+ private void listenOrientationEvent() {
+ if (!mCanAssumeSidefps) {
+ // Do nothing if the device doesn't support SideFPS.
+ return;
+ }
+ mOrientationEventListener = new OrientationEventListener(this) {
+ @Override
+ public void onOrientationChanged(int orientation) {
+ final int currentRotation = getDisplay().getRotation();
+ if ((mPreviousRotation == Surface.ROTATION_90
+ && currentRotation == Surface.ROTATION_270) || (
+ mPreviousRotation == Surface.ROTATION_270
+ && currentRotation == Surface.ROTATION_90)) {
+ mPreviousRotation = currentRotation;
+ recreate();
+ }
+ }
+ };
+ mOrientationEventListener.enable();
+ mPreviousRotation = getDisplay().getRotation();
+ }
+
+ private void stopListenOrientationEvent() {
+ if (!mCanAssumeSidefps) {
+ // Do nothing if the device doesn't support SideFPS.
+ return;
+ }
+ if (mOrientationEventListener != null) {
+ mOrientationEventListener.disable();
+ }
+ mOrientationEventListener = null;
+ }
}
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java b/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java
index 704d00b4b2c..402982fc4ea 100644
--- a/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java
+++ b/src/com/android/settings/dashboard/profileselector/ProfileFragmentBridge.java
@@ -22,7 +22,6 @@ import com.android.settings.accounts.AccountDashboardFragment;
import com.android.settings.applications.manageapplications.ManageApplications;
import com.android.settings.deviceinfo.StorageDashboardFragment;
import com.android.settings.location.LocationServices;
-import com.android.settings.location.RecentLocationAccessSeeAllFragment;
import java.util.Map;
@@ -43,8 +42,6 @@ public class ProfileFragmentBridge {
ProfileSelectAccountFragment.class.getName());
FRAGMENT_MAP.put(ManageApplications.class.getName(),
ProfileSelectManageApplications.class.getName());
- FRAGMENT_MAP.put(RecentLocationAccessSeeAllFragment.class.getName(),
- ProfileSelectRecentLocationAccessFragment.class.getName());
FRAGMENT_MAP.put(LocationServices.class.getName(),
ProfileSelectLocationServicesFragment.class.getName());
FRAGMENT_MAP.put(StorageDashboardFragment.class.getName(),
diff --git a/src/com/android/settings/dashboard/profileselector/ProfileSelectRecentLocationAccessFragment.java b/src/com/android/settings/dashboard/profileselector/ProfileSelectRecentLocationAccessFragment.java
deleted file mode 100644
index 3cb77c545a4..00000000000
--- a/src/com/android/settings/dashboard/profileselector/ProfileSelectRecentLocationAccessFragment.java
+++ /dev/null
@@ -1,46 +0,0 @@
-/*
- * Copyright (C) 2021 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.dashboard.profileselector;
-
-import android.os.Bundle;
-
-import androidx.fragment.app.Fragment;
-
-import com.android.settings.location.RecentLocationAccessSeeAllFragment;
-
-/**
- * Recent location request page for personal/managed profile.
- */
-public class ProfileSelectRecentLocationAccessFragment extends ProfileSelectFragment {
-
- @Override
- public Fragment[] getFragments() {
- final Bundle workOnly = new Bundle();
- workOnly.putInt(EXTRA_PROFILE, ProfileType.WORK);
- final Fragment workFragment = new RecentLocationAccessSeeAllFragment();
- workFragment.setArguments(workOnly);
-
- final Bundle personalOnly = new Bundle();
- personalOnly.putInt(EXTRA_PROFILE, ProfileType.PERSONAL);
- final Fragment personalFragment = new RecentLocationAccessSeeAllFragment();
- personalFragment.setArguments(personalOnly);
- return new Fragment[]{
- personalFragment, //0
- workFragment
- };
- }
-}
diff --git a/src/com/android/settings/display/QRCodeScannerPreferenceController.java b/src/com/android/settings/display/QRCodeScannerPreferenceController.java
new file mode 100644
index 00000000000..16e594a62d5
--- /dev/null
+++ b/src/com/android/settings/display/QRCodeScannerPreferenceController.java
@@ -0,0 +1,108 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+import static android.provider.Settings.Secure.SHOW_QR_CODE_SCANNER_SETTING;
+
+import static androidx.lifecycle.Lifecycle.Event.ON_START;
+import static androidx.lifecycle.Lifecycle.Event.ON_STOP;
+
+import android.content.ContentResolver;
+import android.content.Context;
+import android.database.ContentObserver;
+import android.net.Uri;
+import android.provider.Settings;
+
+import androidx.lifecycle.OnLifecycleEvent;
+import androidx.preference.Preference;
+import androidx.preference.PreferenceScreen;
+
+import com.android.settings.R;
+import com.android.settings.core.TogglePreferenceController;
+
+/**
+ * Preference controller for enabling/disabling QR code scanner button on lock screen.
+ */
+public class QRCodeScannerPreferenceController extends TogglePreferenceController {
+ private static final String SETTING_KEY = Settings.Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER;
+ private final ContentObserver mSettingsObserver;
+ private final ContentResolver mContentResolver;
+ private Preference mPreference;
+
+ public QRCodeScannerPreferenceController(Context context, String preferenceKey) {
+ super(context, preferenceKey);
+ mContentResolver = context.getContentResolver();
+ mSettingsObserver = new ContentObserver(null) {
+ @Override
+ public void onChange(boolean selfChange, Uri uri) {
+ updateState(mPreference);
+ }
+ };
+ }
+
+ @Override
+ public void displayPreference(PreferenceScreen screen) {
+ super.displayPreference(screen);
+ mPreference = screen.findPreference(getPreferenceKey());
+ }
+
+ /** Called when activity starts being displayed to user. */
+ @OnLifecycleEvent(ON_START)
+ public void onStart() {
+ mContentResolver.registerContentObserver(
+ Settings.Global.getUriFor(SHOW_QR_CODE_SCANNER_SETTING), false,
+ mSettingsObserver);
+ }
+
+ /** Called when activity stops being displayed to user. */
+ @OnLifecycleEvent(ON_STOP)
+ public void onStop() {
+ mContentResolver.unregisterContentObserver(mSettingsObserver);
+ }
+
+ @Override
+ public boolean isChecked() {
+ return Settings.Secure.getInt(mContext.getContentResolver(), SETTING_KEY, 0) != 0;
+ }
+
+ @Override
+ public boolean setChecked(boolean isChecked) {
+ return Settings.Secure.putInt(mContext.getContentResolver(), SETTING_KEY,
+ isChecked ? 1 : 0);
+ }
+
+ @Override
+ public int getAvailabilityStatus() {
+ return isScannerActivityAvailable() ? AVAILABLE : UNSUPPORTED_ON_DEVICE;
+ }
+
+ @Override
+ public void updateState(Preference preference) {
+ super.updateState(preference);
+ refreshSummary(preference);
+ }
+
+ @Override
+ public int getSliceHighlightMenuRes() {
+ return R.string.menu_key_display;
+ }
+
+ private boolean isScannerActivityAvailable() {
+ return Settings.Secure.getString(mContext.getContentResolver(),
+ SHOW_QR_CODE_SCANNER_SETTING) != null;
+ }
+}
diff --git a/src/com/android/settings/location/RecentLocationAccessSeeAllFragment.java b/src/com/android/settings/location/RecentLocationAccessSeeAllFragment.java
index a8417fb8936..e27b28c8238 100644
--- a/src/com/android/settings/location/RecentLocationAccessSeeAllFragment.java
+++ b/src/com/android/settings/location/RecentLocationAccessSeeAllFragment.java
@@ -24,7 +24,6 @@ import android.view.MenuItem;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.settings.R;
import com.android.settings.dashboard.DashboardFragment;
-import com.android.settings.dashboard.profileselector.ProfileSelectFragment;
import com.android.settings.search.BaseSearchIndexProvider;
import com.android.settingslib.search.SearchIndexable;
@@ -52,13 +51,8 @@ public class RecentLocationAccessSeeAllFragment extends DashboardFragment {
@Override
public void onAttach(Context context) {
super.onAttach(context);
- final int profileType = getArguments().getInt(ProfileSelectFragment.EXTRA_PROFILE);
-
mController = use(RecentLocationAccessSeeAllPreferenceController.class);
mController.init(this);
- if (profileType != 0) {
- mController.setProfileType(profileType);
- }
}
@Override
diff --git a/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java b/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java
index eba6271f5cf..bca4486f01c 100644
--- a/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java
+++ b/src/com/android/settings/location/RecentLocationAccessSeeAllPreferenceController.java
@@ -40,7 +40,6 @@ public class RecentLocationAccessSeeAllPreferenceController
private final RecentAppOpsAccess mRecentLocationAccesses;
private boolean mShowSystem = false;
private Preference mPreference;
- private int mType = ProfileSelectFragment.ProfileType.ALL;
public RecentLocationAccessSeeAllPreferenceController(Context context, String key) {
super(context, key);
@@ -68,7 +67,8 @@ public class RecentLocationAccessSeeAllPreferenceController
final List recentLocationAccesses = new ArrayList<>();
for (RecentAppOpsAccess.Access access : mRecentLocationAccesses.getAppListSorted(
mShowSystem)) {
- if (isRequestMatchesProfileType(userManager, access, mType)) {
+ if (isRequestMatchesProfileType(
+ userManager, access, ProfileSelectFragment.ProfileType.ALL)) {
recentLocationAccesses.add(access);
}
}
@@ -89,15 +89,6 @@ public class RecentLocationAccessSeeAllPreferenceController
}
}
- /**
- * Initialize {@link ProfileSelectFragment.ProfileType} of the controller
- *
- * @param type {@link ProfileSelectFragment.ProfileType} of the controller.
- */
- public void setProfileType(@ProfileSelectFragment.ProfileType int type) {
- mType = type;
- }
-
/**
* Set the value of {@link #mShowSystem}.
*/
diff --git a/tests/robotests/src/com/android/settings/display/QRCodeScannerPreferenceControllerTest.java b/tests/robotests/src/com/android/settings/display/QRCodeScannerPreferenceControllerTest.java
new file mode 100644
index 00000000000..548d65fb332
--- /dev/null
+++ b/tests/robotests/src/com/android/settings/display/QRCodeScannerPreferenceControllerTest.java
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.settings.display;
+
+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 android.content.ContentResolver;
+import android.content.Context;
+import android.provider.Settings;
+
+import androidx.preference.Preference;
+
+import org.junit.Before;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+import org.robolectric.RobolectricTestRunner;
+import org.robolectric.RuntimeEnvironment;
+
+@RunWith(RobolectricTestRunner.class)
+public class QRCodeScannerPreferenceControllerTest {
+ private static final String TEST_KEY = "test_key";
+ private static final String SETTING_KEY = Settings.Secure.LOCK_SCREEN_SHOW_QR_CODE_SCANNER;
+ private static final String DEFAULT_COMPONENT =
+ Settings.Secure.SHOW_QR_CODE_SCANNER_SETTING;
+
+ private Context mContext;
+ private ContentResolver mContentResolver;
+ private QRCodeScannerPreferenceController mController;
+
+ @Mock
+ private Preference mPreference;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ mContext = RuntimeEnvironment.application;
+ mContentResolver = mContext.getContentResolver();
+ mController = new QRCodeScannerPreferenceController(mContext, TEST_KEY);
+ }
+
+ @Test
+ public void isChecked_SettingIs1_returnTrue() {
+ Settings.Secure.putInt(mContentResolver, SETTING_KEY, 1);
+
+ assertThat(mController.isChecked()).isTrue();
+ }
+
+ @Test
+ public void isChecked_SettingIs0_returnFalse() {
+ Settings.Secure.putInt(mContentResolver, SETTING_KEY, 0);
+
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void isChecked_SettingIsNotSet_returnFalse() {
+ Settings.Secure.putString(mContentResolver, SETTING_KEY, null);
+
+ assertThat(mController.isChecked()).isFalse();
+ }
+
+ @Test
+ public void setChecked_true_SettingIsNot0() {
+ mController.setChecked(true);
+
+ assertThat(Settings.Secure.getInt(mContentResolver, SETTING_KEY, 0)).isNotEqualTo(0);
+ }
+
+ @Test
+ public void setChecked_false_SettingIs0() {
+ mController.setChecked(false);
+
+ assertThat(Settings.Secure.getInt(mContentResolver, SETTING_KEY, 0)).isEqualTo(0);
+ }
+
+ @Test
+ public void getAvailabilityStatus_defaultComponentNotSet() {
+ Settings.Secure.putString(mContext.getContentResolver(), DEFAULT_COMPONENT, null);
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(UNSUPPORTED_ON_DEVICE);
+ }
+
+ @Test
+ public void getAvailabilityStatus_defaultComponentSet() {
+ Settings.Secure.putString(mContext.getContentResolver(), DEFAULT_COMPONENT, "abc");
+ assertThat(mController.getAvailabilityStatus()).isEqualTo(AVAILABLE);
+ }
+}