summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/net/Uri.java36
-rw-r--r--core/java/android/view/WindowManagerPolicyConstants.java6
-rw-r--r--core/tests/coretests/src/android/net/UriTest.java116
-rw-r--r--packages/SystemUI/res/color/brightness_slider_overlay_color.xml22
-rw-r--r--packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml7
-rw-r--r--packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml2
-rw-r--r--packages/SystemUI/res/layout/biometric_prompt_layout.xml1
-rw-r--r--packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml1
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt (renamed from packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricModality.kt)2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt32
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt40
-rw-r--r--packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt85
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt15
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt47
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt8
-rw-r--r--packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt53
-rw-r--r--packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java43
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java41
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java20
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java28
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt113
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt5
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt13
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt17
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt222
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt91
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt8
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java24
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt7
-rw-r--r--services/autofill/java/com/android/server/autofill/Helper.java43
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java12
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/FillUi.java11
-rw-r--r--services/autofill/java/com/android/server/autofill/ui/SaveUi.java3
-rw-r--r--services/core/java/com/android/server/notification/NotificationManagerService.java2
-rw-r--r--services/core/java/com/android/server/policy/PhoneWindowManager.java6
-rw-r--r--services/credentials/java/com/android/server/credentials/CredentialManagerService.java11
-rwxr-xr-xservices/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java1
45 files changed, 815 insertions, 453 deletions
diff --git a/core/java/android/net/Uri.java b/core/java/android/net/Uri.java
index 3da696ad0bc7..7fbaf1027af6 100644
--- a/core/java/android/net/Uri.java
+++ b/core/java/android/net/Uri.java
@@ -882,10 +882,11 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
}
static Uri readFrom(Parcel parcel) {
+ final StringUri stringUri = new StringUri(parcel.readString8());
return new OpaqueUri(
- parcel.readString8(),
- Part.readFrom(parcel),
- Part.readFrom(parcel)
+ stringUri.parseScheme(),
+ stringUri.getSsp(),
+ stringUri.getFragmentPart()
);
}
@@ -895,9 +896,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(TYPE_ID);
- parcel.writeString8(scheme);
- ssp.writeTo(parcel);
- fragment.writeTo(parcel);
+ parcel.writeString8(toString());
}
public boolean isHierarchical() {
@@ -1196,22 +1195,25 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
Part query, Part fragment) {
this.scheme = scheme;
this.authority = Part.nonNull(authority);
- this.path = path == null ? PathPart.NULL : path;
+ this.path = generatePath(path);
this.query = Part.nonNull(query);
this.fragment = Part.nonNull(fragment);
}
- static Uri readFrom(Parcel parcel) {
- final String scheme = parcel.readString8();
- final Part authority = Part.readFrom(parcel);
+ private PathPart generatePath(PathPart originalPath) {
// In RFC3986 the path should be determined based on whether there is a scheme or
// authority present (https://www.rfc-editor.org/rfc/rfc3986.html#section-3.3).
final boolean hasSchemeOrAuthority =
(scheme != null && scheme.length() > 0) || !authority.isEmpty();
- final PathPart path = PathPart.readFrom(hasSchemeOrAuthority, parcel);
- final Part query = Part.readFrom(parcel);
- final Part fragment = Part.readFrom(parcel);
- return new HierarchicalUri(scheme, authority, path, query, fragment);
+ final PathPart newPath = hasSchemeOrAuthority ? PathPart.makeAbsolute(originalPath)
+ : originalPath;
+ return newPath == null ? PathPart.NULL : newPath;
+ }
+
+ static Uri readFrom(Parcel parcel) {
+ final StringUri stringUri = new StringUri(parcel.readString8());
+ return new HierarchicalUri(stringUri.getScheme(), stringUri.getAuthorityPart(),
+ stringUri.getPathPart(), stringUri.getQueryPart(), stringUri.getFragmentPart());
}
public int describeContents() {
@@ -1220,11 +1222,7 @@ public abstract class Uri implements Parcelable, Comparable<Uri> {
public void writeToParcel(Parcel parcel, int flags) {
parcel.writeInt(TYPE_ID);
- parcel.writeString8(scheme);
- authority.writeTo(parcel);
- path.writeTo(parcel);
- query.writeTo(parcel);
- fragment.writeTo(parcel);
+ parcel.writeString8(toString());
}
public boolean isHierarchical() {
diff --git a/core/java/android/view/WindowManagerPolicyConstants.java b/core/java/android/view/WindowManagerPolicyConstants.java
index afc567e5de5c..c88048c17160 100644
--- a/core/java/android/view/WindowManagerPolicyConstants.java
+++ b/core/java/android/view/WindowManagerPolicyConstants.java
@@ -97,6 +97,12 @@ public interface WindowManagerPolicyConstants {
*/
String EXTRA_START_REASON = "android.intent.extra.EXTRA_START_REASON";
+ /**
+ * Set to {@code true} when intent was invoked from pressing one of the brightness keys.
+ * @hide
+ */
+ String EXTRA_FROM_BRIGHTNESS_KEY = "android.intent.extra.FROM_BRIGHTNESS_KEY";
+
// TODO: move this to a more appropriate place.
interface PointerEventListener {
/**
diff --git a/core/tests/coretests/src/android/net/UriTest.java b/core/tests/coretests/src/android/net/UriTest.java
index 89632a46267e..2a4ca79d997e 100644
--- a/core/tests/coretests/src/android/net/UriTest.java
+++ b/core/tests/coretests/src/android/net/UriTest.java
@@ -25,8 +25,6 @@ import junit.framework.TestCase;
import java.io.File;
import java.lang.reflect.Constructor;
-import java.lang.reflect.InvocationTargetException;
-import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Iterator;
import java.util.List;
@@ -869,84 +867,90 @@ public class UriTest extends TestCase {
return (Uri) hierarchicalUriConstructor.newInstance("https", authority, path, null, null);
}
- /** Attempting to unparcel a legacy parcel format of Uri.{,Path}Part should fail. */
- public void testUnparcelLegacyPart_fails() throws Exception {
- assertUnparcelLegacyPart_fails(Class.forName("android.net.Uri$Part"));
- assertUnparcelLegacyPart_fails(Class.forName("android.net.Uri$PathPart"));
- }
-
- private static void assertUnparcelLegacyPart_fails(Class partClass) throws Exception {
- Parcel parcel = Parcel.obtain();
- parcel.writeInt(0 /* BOTH */);
- parcel.writeString("encoded");
- parcel.writeString("decoded");
- parcel.setDataPosition(0);
-
- Method readFromMethod = partClass.getDeclaredMethod("readFrom", Parcel.class);
- readFromMethod.setAccessible(true);
- try {
- readFromMethod.invoke(null, parcel);
- fail();
- } catch (InvocationTargetException expected) {
- Throwable targetException = expected.getTargetException();
- // Check that the exception was thrown for the correct reason.
- assertEquals("Unknown representation: 0", targetException.getMessage());
- } finally {
- parcel.recycle();
- }
- }
-
- private Uri buildUriFromRawParcel(boolean argumentsEncoded,
+ private Uri buildUriFromParts(boolean argumentsEncoded,
String scheme,
String authority,
String path,
String query,
String fragment) {
- // Representation value (from AbstractPart.REPRESENTATION_{ENCODED,DECODED}).
- final int representation = argumentsEncoded ? 1 : 2;
- Parcel parcel = Parcel.obtain();
- try {
- parcel.writeInt(3); // hierarchical
- parcel.writeString8(scheme);
- parcel.writeInt(representation);
- parcel.writeString8(authority);
- parcel.writeInt(representation);
- parcel.writeString8(path);
- parcel.writeInt(representation);
- parcel.writeString8(query);
- parcel.writeInt(representation);
- parcel.writeString8(fragment);
- parcel.setDataPosition(0);
- return Uri.CREATOR.createFromParcel(parcel);
- } finally {
- parcel.recycle();
+ final Uri.Builder builder = new Uri.Builder();
+ builder.scheme(scheme);
+ if (argumentsEncoded) {
+ builder.encodedAuthority(authority);
+ builder.encodedPath(path);
+ builder.encodedQuery(query);
+ builder.encodedFragment(fragment);
+ } else {
+ builder.authority(authority);
+ builder.path(path);
+ builder.query(query);
+ builder.fragment(fragment);
}
+ return builder.build();
}
public void testUnparcelMalformedPath() {
// Regression tests for b/171966843.
// Test cases with arguments encoded (covering testing `scheme` * `authority` options).
- Uri uri0 = buildUriFromRawParcel(true, "https", "google.com", "@evil.com", null, null);
+ Uri uri0 = buildUriFromParts(true, "https", "google.com", "@evil.com", null, null);
assertEquals("https://google.com/@evil.com", uri0.toString());
- Uri uri1 = buildUriFromRawParcel(true, null, "google.com", "@evil.com", "name=spark", "x");
+ Uri uri1 = buildUriFromParts(true, null, "google.com", "@evil.com", "name=spark", "x");
assertEquals("//google.com/@evil.com?name=spark#x", uri1.toString());
- Uri uri2 = buildUriFromRawParcel(true, "http:", null, "@evil.com", null, null);
+ Uri uri2 = buildUriFromParts(true, "http:", null, "@evil.com", null, null);
assertEquals("http::/@evil.com", uri2.toString());
- Uri uri3 = buildUriFromRawParcel(true, null, null, "@evil.com", null, null);
+ Uri uri3 = buildUriFromParts(true, null, null, "@evil.com", null, null);
assertEquals("@evil.com", uri3.toString());
// Test cases with arguments not encoded (covering testing `scheme` * `authority` options).
- Uri uriA = buildUriFromRawParcel(false, "https", "google.com", "@evil.com", null, null);
+ Uri uriA = buildUriFromParts(false, "https", "google.com", "@evil.com", null, null);
assertEquals("https://google.com/%40evil.com", uriA.toString());
- Uri uriB = buildUriFromRawParcel(false, null, "google.com", "@evil.com", null, null);
+ Uri uriB = buildUriFromParts(false, null, "google.com", "@evil.com", null, null);
assertEquals("//google.com/%40evil.com", uriB.toString());
- Uri uriC = buildUriFromRawParcel(false, "http:", null, "@evil.com", null, null);
+ Uri uriC = buildUriFromParts(false, "http:", null, "@evil.com", null, null);
assertEquals("http::/%40evil.com", uriC.toString());
- Uri uriD = buildUriFromRawParcel(false, null, null, "@evil.com", "name=spark", "y");
+ Uri uriD = buildUriFromParts(false, null, null, "@evil.com", "name=spark", "y");
assertEquals("%40evil.com?name%3Dspark#y", uriD.toString());
}
+ public void testParsedUriFromStringEquality() {
+ Uri uri = buildUriFromParts(
+ true, "https", "google.com", "@evil.com", null, null);
+ assertEquals(uri, Uri.parse(uri.toString()));
+ Uri uri2 = buildUriFromParts(
+ true, "content://evil.authority?foo=", "safe.authority", "@evil.com", null, null);
+ assertEquals(uri2, Uri.parse(uri2.toString()));
+ Uri uri3 = buildUriFromParts(
+ false, "content://evil.authority?foo=", "safe.authority", "@evil.com", null, null);
+ assertEquals(uri3, Uri.parse(uri3.toString()));
+ }
+
+ public void testParceledUrisAreEqual() {
+ Uri opaqueUri = Uri.fromParts("fake://uri#", "ssp", "fragment");
+ Parcel parcel = Parcel.obtain();
+ try {
+ opaqueUri.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ Uri postParcelUri = Uri.CREATOR.createFromParcel(parcel);
+ Uri parsedUri = Uri.parse(postParcelUri.toString());
+ assertEquals(parsedUri.getScheme(), postParcelUri.getScheme());
+ } finally {
+ parcel.recycle();
+ }
+
+ Uri hierarchicalUri = new Uri.Builder().scheme("fake://uri#").authority("auth").build();
+ parcel = Parcel.obtain();
+ try {
+ hierarchicalUri.writeToParcel(parcel, 0);
+ parcel.setDataPosition(0);
+ Uri postParcelUri = Uri.CREATOR.createFromParcel(parcel);
+ Uri parsedUri = Uri.parse(postParcelUri.toString());
+ assertEquals(parsedUri.getScheme(), postParcelUri.getScheme());
+ } finally {
+ parcel.recycle();
+ }
+ }
+
public void testToSafeString() {
checkToSafeString("tel:xxxxxx", "tel:Google");
checkToSafeString("tel:xxxxxxxxxx", "tel:1234567890");
diff --git a/packages/SystemUI/res/color/brightness_slider_overlay_color.xml b/packages/SystemUI/res/color/brightness_slider_overlay_color.xml
new file mode 100644
index 000000000000..a8abd793bd00
--- /dev/null
+++ b/packages/SystemUI/res/color/brightness_slider_overlay_color.xml
@@ -0,0 +1,22 @@
+<?xml version="1.0" encoding="utf-8"?>
+<!--
+ ~ Copyright (C) 2023 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.
+ -->
+
+<selector xmlns:android="http://schemas.android.com/apk/res/android">
+ <item android:state_pressed="true" android:color="?attr/onShadeActive" android:alpha="0.12" />
+ <item android:state_hovered="true" android:color="?attr/onShadeActive" android:alpha="0.09" />
+ <item android:color="@color/transparent" />
+</selector> \ No newline at end of file
diff --git a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
index 2ea90c717863..a9e7adf668a9 100644
--- a/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
+++ b/packages/SystemUI/res/drawable/brightness_progress_full_drawable.xml
@@ -26,6 +26,13 @@
<corners android:radius="@dimen/rounded_slider_corner_radius"/>
</shape>
</item>
+ <item>
+ <shape>
+ <corners android:radius="@dimen/rounded_slider_corner_radius" />
+ <size android:height="@dimen/rounded_slider_height" />
+ <solid android:color="@color/brightness_slider_overlay_color" />
+ </shape>
+ </item>
<item
android:id="@+id/slider_icon"
android:gravity="center_vertical|right"
diff --git a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
index 87b5a4c2fc5b..32dc4b335f7e 100644
--- a/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
+++ b/packages/SystemUI/res/drawable/keyguard_bottom_affordance_bg.xml
@@ -20,7 +20,7 @@
xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:androidprv="http://schemas.android.com/apk/prv/res/android"
android:shape="rectangle">
- <solid android:color="?androidprv:attr/colorSurface"/>
+ <solid android:color="?androidprv:attr/materialColorSurfaceContainerHigh"/>
<size
android:width="@dimen/keyguard_affordance_fixed_width"
android:height="@dimen/keyguard_affordance_fixed_height"/>
diff --git a/packages/SystemUI/res/layout/biometric_prompt_layout.xml b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
index 05ff1b1c2e6f..8eb62ecba5fa 100644
--- a/packages/SystemUI/res/layout/biometric_prompt_layout.xml
+++ b/packages/SystemUI/res/layout/biometric_prompt_layout.xml
@@ -39,6 +39,7 @@
android:singleLine="true"
android:marqueeRepeatLimit="1"
android:ellipsize="marquee"
+ android:importantForAccessibility="no"
style="@style/TextAppearance.AuthCredential.Subtitle"/>
<TextView
diff --git a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
index e95c6a79733c..91550b3dcac0 100644
--- a/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
+++ b/packages/SystemUI/res/layout/quick_settings_brightness_dialog.xml
@@ -34,5 +34,6 @@
android:paddingEnd="0dp"
android:progressDrawable="@drawable/brightness_progress_drawable"
android:splitTrack="false"
+ android:clickable="true"
/>
</com.android.systemui.settings.brightness.BrightnessSliderView>
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricModality.kt b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt
index 3197c0935d0b..fb580ca54aff 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/domain/model/BiometricModality.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/shared/model/BiometricModality.kt
@@ -14,7 +14,7 @@
* limitations under the License.
*/
-package com.android.systemui.biometrics.domain.model
+package com.android.systemui.biometrics.shared.model
import android.hardware.biometrics.BiometricAuthenticator
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
index 64df6a03001d..e5a4d1a644f1 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/binder/BiometricViewBinder.kt
@@ -46,9 +46,9 @@ import com.android.systemui.biometrics.AuthIconController
import com.android.systemui.biometrics.AuthPanelController
import com.android.systemui.biometrics.Utils
import com.android.systemui.biometrics.domain.model.BiometricModalities
-import com.android.systemui.biometrics.domain.model.BiometricModality
-import com.android.systemui.biometrics.domain.model.asBiometricModality
+import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.PromptKind
+import com.android.systemui.biometrics.shared.model.asBiometricModality
import com.android.systemui.biometrics.ui.BiometricPromptLayout
import com.android.systemui.biometrics.ui.viewmodel.FingerprintStartMode
import com.android.systemui.biometrics.ui.viewmodel.PromptMessage
@@ -396,7 +396,6 @@ private class Spaghetti(
private var lifecycleScope: CoroutineScope? = null
private var modalities: BiometricModalities = BiometricModalities()
- private var faceFailedAtLeastOnce = false
private var legacyCallback: Callback? = null
override var legacyIconController: AuthIconController? = null
@@ -476,19 +475,15 @@ private class Spaghetti(
viewModel.ensureFingerprintHasStarted(isDelayed = true)
applicationScope.launch {
- val suppress =
- modalities.hasFaceAndFingerprint &&
- (failedModality == BiometricModality.Face) &&
- faceFailedAtLeastOnce
- if (failedModality == BiometricModality.Face) {
- faceFailedAtLeastOnce = true
- }
-
viewModel.showTemporaryError(
failureReason,
messageAfterError = modalities.asDefaultHelpMessage(applicationContext),
authenticateAfterError = modalities.hasFingerprint,
- suppressIfErrorShowing = suppress,
+ suppressIf = { currentMessage ->
+ modalities.hasFaceAndFingerprint &&
+ failedModality == BiometricModality.Face &&
+ currentMessage.isError
+ },
failedModality = failedModality,
)
}
@@ -501,11 +496,10 @@ private class Spaghetti(
}
applicationScope.launch {
- val suppress =
- modalities.hasFaceAndFingerprint && (errorModality == BiometricModality.Face)
viewModel.showTemporaryError(
error,
- suppressIfErrorShowing = suppress,
+ messageAfterError = modalities.asDefaultHelpMessage(applicationContext),
+ authenticateAfterError = modalities.hasFingerprint,
)
delay(BiometricPrompt.HIDE_DIALOG_DELAY.toLong())
legacyCallback?.onAction(Callback.ACTION_ERROR)
@@ -522,6 +516,7 @@ private class Spaghetti(
viewModel.showTemporaryError(
help,
messageAfterError = modalities.asDefaultHelpMessage(applicationContext),
+ authenticateAfterError = modalities.hasFingerprint,
hapticFeedback = false,
)
}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt
index 444082ca2742..2f9557f70a32 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthState.kt
@@ -16,7 +16,7 @@
package com.android.systemui.biometrics.ui.viewmodel
-import com.android.systemui.biometrics.domain.model.BiometricModality
+import com.android.systemui.biometrics.shared.model.BiometricModality
/**
* The authenticated state with the [authenticatedModality] (when [isAuthenticated]) with an
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt
index 219da716f7d9..50f491142949 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptMessage.kt
@@ -33,9 +33,9 @@ sealed interface PromptMessage {
else -> ""
}
- /** If this is an [Error] or [Help] message. */
- val isErrorOrHelp: Boolean
- get() = this is Error || this is Help
+ /** If this is an [Error]. */
+ val isError: Boolean
+ get() = this is Error
/** An error message. */
data class Error(val errorMessage: String) : PromptMessage
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
index d63bf57013e5..8a2e4059ee73 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModel.kt
@@ -20,7 +20,7 @@ import android.util.Log
import com.android.systemui.biometrics.AuthBiometricView
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.model.BiometricModalities
-import com.android.systemui.biometrics.domain.model.BiometricModality
+import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.biometrics.shared.model.PromptKind
import com.android.systemui.statusbar.VibratorHelper
import javax.inject.Inject
@@ -210,35 +210,33 @@ constructor(
* Show a temporary error [message] associated with an optional [failedModality] and play
* [hapticFeedback].
*
- * An optional [messageAfterError] will be shown via [showAuthenticating] when
- * [authenticateAfterError] is set (or via [showHelp] when not set) after the error is
- * dismissed.
+ * The [messageAfterError] will be shown via [showAuthenticating] when [authenticateAfterError]
+ * is set (or via [showHelp] when not set) after the error is dismissed.
*
- * The error is ignored if the user has already authenticated or if [suppressIfErrorShowing] is
- * set and an error message is already showing.
+ * The error is ignored if the user has already authenticated or if [suppressIf] is true given
+ * the currently showing [PromptMessage].
*/
suspend fun showTemporaryError(
message: String,
+ messageAfterError: String,
+ authenticateAfterError: Boolean,
+ suppressIf: (PromptMessage) -> Boolean = { false },
hapticFeedback: Boolean = true,
- messageAfterError: String = "",
- authenticateAfterError: Boolean = false,
- suppressIfErrorShowing: Boolean = false,
failedModality: BiometricModality = BiometricModality.None,
) = coroutineScope {
if (_isAuthenticated.value.isAuthenticated) {
return@coroutineScope
}
- if (_message.value.isErrorOrHelp && suppressIfErrorShowing) {
- if (_isAuthenticated.value.isNotAuthenticated) {
- _canTryAgainNow.value = supportsRetry(failedModality)
- }
+
+ _canTryAgainNow.value = supportsRetry(failedModality)
+
+ if (suppressIf(_message.value)) {
return@coroutineScope
}
_isAuthenticating.value = false
_isAuthenticated.value = PromptAuthState(false)
_forceMediumSize.value = true
- _canTryAgainNow.value = supportsRetry(failedModality)
_message.value = PromptMessage.Error(message)
_legacyState.value = AuthBiometricView.STATE_ERROR
@@ -374,7 +372,9 @@ constructor(
AuthBiometricView.STATE_AUTHENTICATED
}
- vibrator.success(modality)
+ if (!needsUserConfirmation) {
+ vibrator.success(modality)
+ }
messageJob?.cancel()
messageJob = null
@@ -420,6 +420,8 @@ constructor(
_message.value = PromptMessage.Empty
_legacyState.value = AuthBiometricView.STATE_AUTHENTICATED
+ vibrator.success(authState.authenticatedModality)
+
messageJob?.cancel()
messageJob = null
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
index 2513c81c17f8..62a484d42dec 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractor.kt
@@ -25,6 +25,8 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.authentication.shared.model.AuthenticationThrottlingModel
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
@@ -38,9 +40,6 @@ import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
-import kotlinx.coroutines.flow.flatMapLatest
-import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
@@ -53,6 +52,7 @@ constructor(
private val repository: BouncerRepository,
private val authenticationInteractor: AuthenticationInteractor,
private val sceneInteractor: SceneInteractor,
+ featureFlags: FeatureFlags,
@Assisted private val containerName: String,
) {
@@ -95,36 +95,14 @@ constructor(
val isPatternVisible: StateFlow<Boolean> = authenticationInteractor.isPatternVisible
init {
- // UNLOCKING SHOWS Gone.
- //
- // Move to the gone scene if the device becomes unlocked while on the bouncer scene.
- applicationScope.launch {
- sceneInteractor
- .currentScene(containerName)
- .flatMapLatest { currentScene ->
- if (currentScene.key == SceneKey.Bouncer) {
- authenticationInteractor.isUnlocked
- } else {
- flowOf(false)
- }
- }
- .distinctUntilChanged()
- .collect { isUnlocked ->
- if (isUnlocked) {
- sceneInteractor.setCurrentScene(
- containerName = containerName,
- scene = SceneModel(SceneKey.Gone),
- )
+ if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
+ // Clear the message if moved from throttling to no-longer throttling.
+ applicationScope.launch {
+ isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) ->
+ if (wasThrottled && !currentlyThrottled) {
+ clearMessage()
}
}
- }
-
- // Clear the message if moved from throttling to no-longer throttling.
- applicationScope.launch {
- isThrottled.pairwise().collect { (wasThrottled, currentlyThrottled) ->
- if (wasThrottled && !currentlyThrottled) {
- clearMessage()
- }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
index 5425022e11b7..a4ef5cec6525 100644
--- a/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModel.kt
@@ -23,6 +23,8 @@ import com.android.systemui.R
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.util.kotlin.pairwise
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
@@ -49,6 +51,7 @@ constructor(
@Application private val applicationContext: Context,
@Application private val applicationScope: CoroutineScope,
interactorFactory: BouncerInteractor.Factory,
+ featureFlags: FeatureFlags,
@Assisted containerName: String,
) {
private val interactor: BouncerInteractor = interactorFactory.create(containerName)
@@ -102,15 +105,48 @@ constructor(
)
init {
- applicationScope.launch {
- _authMethod.subscriptionCount
- .pairwise()
- .map { (previousCount, currentCount) -> currentCount > previousCount }
- .collect { subscriberAdded ->
- if (subscriberAdded) {
- reloadAuthMethod()
+ if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
+ applicationScope.launch {
+ interactor.isThrottled
+ .map { isThrottled ->
+ if (isThrottled) {
+ when (interactor.getAuthenticationMethod()) {
+ is AuthenticationMethodModel.Pin ->
+ R.string.kg_too_many_failed_pin_attempts_dialog_message
+ is AuthenticationMethodModel.Password ->
+ R.string.kg_too_many_failed_password_attempts_dialog_message
+ is AuthenticationMethodModel.Pattern ->
+ R.string.kg_too_many_failed_pattern_attempts_dialog_message
+ else -> null
+ }?.let { stringResourceId ->
+ applicationContext.getString(
+ stringResourceId,
+ interactor.throttling.value.failedAttemptCount,
+ ceil(interactor.throttling.value.remainingMs / 1000f).toInt(),
+ )
+ }
+ } else {
+ null
+ }
+ }
+ .distinctUntilChanged()
+ .collect { dialogMessageOrNull ->
+ if (dialogMessageOrNull != null) {
+ _throttlingDialogMessage.value = dialogMessageOrNull
+ }
+ }
+ }
+
+ applicationScope.launch {
+ _authMethod.subscriptionCount
+ .pairwise()
+ .map { (previousCount, currentCount) -> currentCount > previousCount }
+ .collect { subscriberAdded ->
+ if (subscriberAdded) {
+ reloadAuthMethod()
+ }
}
- }
+ }
}
}
@@ -144,39 +180,6 @@ constructor(
*/
val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow()
- init {
- applicationScope.launch {
- interactor.isThrottled
- .map { isThrottled ->
- if (isThrottled) {
- when (interactor.getAuthenticationMethod()) {
- is AuthenticationMethodModel.Pin ->
- R.string.kg_too_many_failed_pin_attempts_dialog_message
- is AuthenticationMethodModel.Password ->
- R.string.kg_too_many_failed_password_attempts_dialog_message
- is AuthenticationMethodModel.Pattern ->
- R.string.kg_too_many_failed_pattern_attempts_dialog_message
- else -> null
- }?.let { stringResourceId ->
- applicationContext.getString(
- stringResourceId,
- interactor.throttling.value.failedAttemptCount,
- ceil(interactor.throttling.value.remainingMs / 1000f).toInt(),
- )
- }
- } else {
- null
- }
- }
- .distinctUntilChanged()
- .collect { dialogMessageOrNull ->
- if (dialogMessageOrNull != null) {
- _throttlingDialogMessage.value = dialogMessageOrNull
- }
- }
- }
- }
-
/** Notifies that the emergency services button was clicked. */
fun onEmergencyServicesButtonClicked() {
// TODO(b/280877228): implement this
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index 69c381b4a58e..a5a799565eaf 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -91,6 +91,11 @@ object Flags {
val NOTIFICATION_SHELF_REFACTOR =
unreleasedFlag(271161129, "notification_shelf_refactor", teamfood = true)
+ // TODO(b/290787599): Tracking Bug
+ @JvmField
+ val NOTIFICATION_ICON_CONTAINER_REFACTOR =
+ unreleasedFlag(278765923, "notification_icon_container_refactor")
+
// TODO(b/288326013): Tracking Bug
@JvmField
val NOTIFICATION_ASYNC_HYBRID_VIEW_INFLATION =
@@ -258,6 +263,16 @@ object Flags {
@JvmField
val MIGRATE_INDICATION_AREA = unreleasedFlag(236, "migrate_indication_area", teamfood = true)
+ /**
+ * Migrate the bottom area to the new keyguard root view.
+ * Because there is no such thing as a "bottom area" after this, this also breaks it up into
+ * many smaller, modular pieces.
+ */
+ // TODO(b/290652751): Tracking bug.
+ @JvmField
+ val MIGRATE_SPLIT_KEYGUARD_BOTTOM_AREA =
+ unreleasedFlag(290652751, "migrate_split_keyguard_bottom_area")
+
/** Whether to listen for fingerprint authentication over keyguard occluding activities. */
// TODO(b/283260512): Tracking bug.
@JvmField
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt
index c8f7efbeb397..1c200b086990 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractor.kt
@@ -20,20 +20,14 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.scene.domain.interactor.SceneInteractor
-import com.android.systemui.scene.shared.model.SceneKey
-import com.android.systemui.scene.shared.model.SceneModel
import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
-import kotlinx.coroutines.flow.combine
-import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
-import kotlinx.coroutines.launch
/** Hosts business and application state accessing logic for the lockscreen scene. */
class LockscreenSceneInteractor
@@ -42,7 +36,6 @@ constructor(
@Application applicationScope: CoroutineScope,
private val authenticationInteractor: AuthenticationInteractor,
bouncerInteractorFactory: BouncerInteractor.Factory,
- private val sceneInteractor: SceneInteractor,
@Assisted private val containerName: String,
) {
private val bouncerInteractor: BouncerInteractor =
@@ -72,46 +65,6 @@ constructor(
initialValue = false,
)
- init {
- // LOCKING SHOWS Lockscreen.
- //
- // Move to the lockscreen scene if the device becomes locked while in any scene.
- applicationScope.launch {
- authenticationInteractor.isUnlocked
- .map { !it }
- .distinctUntilChanged()
- .collect { isLocked ->
- if (isLocked) {
- sceneInteractor.setCurrentScene(
- containerName = containerName,
- scene = SceneModel(SceneKey.Lockscreen),
- )
- }
- }
- }
-
- // BYPASS UNLOCK.
- //
- // Moves to the gone scene if bypass is enabled and the device becomes unlocked while in the
- // lockscreen scene.
- applicationScope.launch {
- combine(
- authenticationInteractor.isBypassEnabled,
- authenticationInteractor.isUnlocked,
- sceneInteractor.currentScene(containerName),
- ::Triple,
- )
- .collect { (isBypassEnabled, isUnlocked, currentScene) ->
- if (isBypassEnabled && isUnlocked && currentScene.key == SceneKey.Lockscreen) {
- sceneInteractor.setCurrentScene(
- containerName = containerName,
- scene = SceneModel(SceneKey.Gone),
- )
- }
- }
- }
- }
-
/** Attempts to dismiss the lockscreen. This will cause the bouncer to show, if needed. */
fun dismissLockscreen() {
bouncerInteractor.showOrUnlockDevice(containerName = containerName)
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
index 7d14198bdb17..db84268f7c58 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBottomAreaViewBinder.kt
@@ -343,9 +343,9 @@ object KeyguardBottomAreaViewBinder {
Utils.getColorAttrDefaultColor(
view.context,
if (viewModel.isActivated) {
- com.android.internal.R.attr.textColorPrimaryInverse
+ com.android.internal.R.attr.materialColorOnPrimaryFixed
} else {
- com.android.internal.R.attr.textColorPrimary
+ com.android.internal.R.attr.materialColorOnSurface
},
)
)
@@ -355,9 +355,9 @@ object KeyguardBottomAreaViewBinder {
Utils.getColorAttr(
view.context,
if (viewModel.isActivated) {
- com.android.internal.R.attr.colorAccentPrimary
+ com.android.internal.R.attr.materialColorPrimaryFixed
} else {
- com.android.internal.R.attr.colorSurface
+ com.android.internal.R.attr.materialColorSurfaceContainerHigh
}
)
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt
index 91400196bbb1..59f82f034723 100644
--- a/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartable.kt
@@ -17,6 +17,7 @@
package com.android.systemui.scene.domain.startable
import com.android.systemui.CoreStartable
+import com.android.systemui.authentication.domain.interactor.AuthenticationInteractor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
import com.android.systemui.flags.FeatureFlags
@@ -24,9 +25,11 @@ import com.android.systemui.flags.Flags
import com.android.systemui.scene.domain.interactor.SceneInteractor
import com.android.systemui.scene.shared.model.SceneContainerNames
import com.android.systemui.scene.shared.model.SceneKey
+import com.android.systemui.scene.shared.model.SceneModel
import javax.inject.Inject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.distinctUntilChanged
+import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.launch
@@ -41,17 +44,19 @@ class SystemUiDefaultSceneContainerStartable
constructor(
@Application private val applicationScope: CoroutineScope,
private val sceneInteractor: SceneInteractor,
+ private val authenticationInteractor: AuthenticationInteractor,
private val featureFlags: FeatureFlags,
) : CoreStartable {
override fun start() {
if (featureFlags.isEnabled(Flags.SCENE_CONTAINER)) {
- keepVisibilityUpdated()
+ hydrateVisibility()
+ automaticallySwitchScenes()
}
}
- /** Drives visibility of the scene container. */
- private fun keepVisibilityUpdated() {
+ /** Updates the visibility of the scene container based on the current scene. */
+ private fun hydrateVisibility() {
applicationScope.launch {
sceneInteractor
.currentScene(CONTAINER_NAME)
@@ -63,6 +68,48 @@ constructor(
}
}
+ /** Switches between scenes based on ever-changing application state. */
+ private fun automaticallySwitchScenes() {
+ applicationScope.launch {
+ authenticationInteractor.isUnlocked
+ .map { isUnlocked ->
+ val currentSceneKey = sceneInteractor.currentScene(CONTAINER_NAME).value.key
+ val isBypassEnabled = authenticationInteractor.isBypassEnabled.value
+ when {
+ isUnlocked ->
+ when (currentSceneKey) {
+ // When the device becomes unlocked in Bouncer, go to the Gone.
+ is SceneKey.Bouncer -> SceneKey.Gone
+ // When the device becomes unlocked in Lockscreen, go to Gone if
+ // bypass is enabled.
+ is SceneKey.Lockscreen -> SceneKey.Gone.takeIf { isBypassEnabled }
+ // We got unlocked while on a scene that's not Lockscreen or
+ // Bouncer, no need to change scenes.
+ else -> null
+ }
+ // When the device becomes locked, to Lockscreen.
+ !isUnlocked ->
+ when (currentSceneKey) {
+ // Already on lockscreen or bouncer, no need to change scenes.
+ is SceneKey.Lockscreen,
+ is SceneKey.Bouncer -> null
+ // We got locked while on a scene that's not Lockscreen or Bouncer,
+ // go to Lockscreen.
+ else -> SceneKey.Lockscreen
+ }
+ else -> null
+ }
+ }
+ .filterNotNull()
+ .collect { targetSceneKey ->
+ sceneInteractor.setCurrentScene(
+ containerName = CONTAINER_NAME,
+ scene = SceneModel(targetSceneKey),
+ )
+ }
+ }
+ }
+
companion object {
private const val CONTAINER_NAME = SceneContainerNames.SYSTEM_UI_DEFAULT
}
diff --git a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
index 5199bd43f982..182e4569b549 100644
--- a/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
+++ b/packages/SystemUI/src/com/android/systemui/settings/brightness/BrightnessDialog.java
@@ -18,6 +18,7 @@ package com.android.systemui.settings.brightness;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import static android.view.ViewGroup.LayoutParams.WRAP_CONTENT;
+import static android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY;
import android.app.Activity;
import android.graphics.Rect;
@@ -29,8 +30,10 @@ import android.view.View;
import android.view.ViewGroup;
import android.view.Window;
import android.view.WindowManager;
+import android.view.accessibility.AccessibilityManager;
import android.widget.FrameLayout;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.logging.MetricsLogger;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.systemui.R;
@@ -38,34 +41,42 @@ import com.android.systemui.dagger.qualifiers.Background;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.settings.DisplayTracker;
import com.android.systemui.settings.UserTracker;
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper;
+import com.android.systemui.util.concurrency.DelayableExecutor;
import java.util.List;
-import java.util.concurrent.Executor;
import javax.inject.Inject;
/** A dialog that provides controls for adjusting the screen brightness. */
public class BrightnessDialog extends Activity {
+ @VisibleForTesting
+ static final int DIALOG_TIMEOUT_MILLIS = 3000;
+
private BrightnessController mBrightnessController;
private final BrightnessSliderController.Factory mToggleSliderFactory;
private final UserTracker mUserTracker;
private final DisplayTracker mDisplayTracker;
- private final Executor mMainExecutor;
+ private final DelayableExecutor mMainExecutor;
private final Handler mBackgroundHandler;
+ private final AccessibilityManagerWrapper mAccessibilityMgr;
+ private Runnable mCancelTimeoutRunnable;
@Inject
public BrightnessDialog(
UserTracker userTracker,
DisplayTracker displayTracker,
BrightnessSliderController.Factory factory,
- @Main Executor mainExecutor,
- @Background Handler bgHandler) {
+ @Main DelayableExecutor mainExecutor,
+ @Background Handler bgHandler,
+ AccessibilityManagerWrapper accessibilityMgr) {
mUserTracker = userTracker;
mDisplayTracker = displayTracker;
mToggleSliderFactory = factory;
mMainExecutor = mainExecutor;
mBackgroundHandler = bgHandler;
+ mAccessibilityMgr = accessibilityMgr;
}
@@ -122,6 +133,14 @@ public class BrightnessDialog extends Activity {
}
@Override
+ protected void onResume() {
+ super.onResume();
+ if (triggeredByBrightnessKey()) {
+ scheduleTimeout();
+ }
+ }
+
+ @Override
protected void onPause() {
super.onPause();
overridePendingTransition(android.R.anim.fade_in, android.R.anim.fade_out);
@@ -139,9 +158,25 @@ public class BrightnessDialog extends Activity {
if (keyCode == KeyEvent.KEYCODE_VOLUME_DOWN
|| keyCode == KeyEvent.KEYCODE_VOLUME_UP
|| keyCode == KeyEvent.KEYCODE_VOLUME_MUTE) {
+ if (mCancelTimeoutRunnable != null) {
+ mCancelTimeoutRunnable.run();
+ }
finish();
}
return super.onKeyDown(keyCode, event);
}
+
+ private boolean triggeredByBrightnessKey() {
+ return getIntent().getBooleanExtra(EXTRA_FROM_BRIGHTNESS_KEY, false);
+ }
+
+ private void scheduleTimeout() {
+ if (mCancelTimeoutRunnable != null) {
+ mCancelTimeoutRunnable.run();
+ }
+ final int timeout = mAccessibilityMgr.getRecommendedTimeoutMillis(DIALOG_TIMEOUT_MILLIS,
+ AccessibilityManager.FLAG_CONTENT_CONTROLS);
+ mCancelTimeoutRunnable = mMainExecutor.executeDelayed(this::finish, timeout);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
index cff71d2edd23..fbbee53468c5 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/StatusBarIconView.java
@@ -160,7 +160,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
private ObjectAnimator mIconAppearAnimator;
private ObjectAnimator mDotAnimator;
private float mDotAppearAmount;
- private OnVisibilityChangedListener mOnVisibilityChangedListener;
private int mDrawableColor;
private int mIconColor;
private int mDecorColor;
@@ -179,7 +178,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
private int mCachedContrastBackgroundColor = NO_COLOR;
private float[] mMatrix;
private ColorMatrixColorFilter mMatrixColorFilter;
- private boolean mIsInShelf;
private Runnable mLayoutRunnable;
private boolean mDismissed;
private Runnable mOnDismissListener;
@@ -357,19 +355,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
return mNotification != null;
}
- private static boolean streq(String a, String b) {
- if (a == b) {
- return true;
- }
- if (a == null && b != null) {
- return false;
- }
- if (a != null && b == null) {
- return false;
- }
- return a.equals(b);
- }
-
public boolean equalIcons(Icon a, Icon b) {
if (a == b) return true;
if (a.getType() != b.getType()) return false;
@@ -908,7 +893,7 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
if (targetAmount != currentAmount) {
mDotAnimator = ObjectAnimator.ofFloat(this, DOT_APPEAR_AMOUNT,
currentAmount, targetAmount);
- mDotAnimator.setInterpolator(interpolator);;
+ mDotAnimator.setInterpolator(interpolator);
mDotAnimator.setDuration(duration == 0 ? ANIMATION_DURATION_FAST
: duration);
final boolean runRunnable = !runnableAdded;
@@ -965,22 +950,10 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
}
}
- @Override
- public void setVisibility(int visibility) {
- super.setVisibility(visibility);
- if (mOnVisibilityChangedListener != null) {
- mOnVisibilityChangedListener.onVisibilityChanged(visibility);
- }
- }
-
public float getDotAppearAmount() {
return mDotAppearAmount;
}
- public void setOnVisibilityChangedListener(OnVisibilityChangedListener listener) {
- mOnVisibilityChangedListener = listener;
- }
-
public void setDozing(boolean dozing, boolean fade, long delay) {
mDozer.setDozing(f -> {
mDozeAmount = f;
@@ -1014,14 +987,6 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
outRect.bottom += translationY;
}
- public void setIsInShelf(boolean isInShelf) {
- mIsInShelf = isInShelf;
- }
-
- public boolean isInShelf() {
- return mIsInShelf;
- }
-
@Override
protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
super.onLayout(changed, left, top, right, bottom);
@@ -1103,8 +1068,4 @@ public class StatusBarIconView extends AnimatedImageView implements StatusIconDi
public boolean showsConversation() {
return mShowsConversation;
}
-
- public interface OnVisibilityChangedListener {
- void onVisibilityChanged(int newVisibility);
- }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
index 0fde3ab3d19c..e18c9d86d74b 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconAreaController.java
@@ -33,14 +33,11 @@ import com.android.systemui.statusbar.NotificationMediaManager;
import com.android.systemui.statusbar.NotificationShelfController;
import com.android.systemui.statusbar.StatusBarIconView;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.AnimatableProperty;
import com.android.systemui.statusbar.notification.NotificationUtils;
import com.android.systemui.statusbar.notification.NotificationWakeUpCoordinator;
-import com.android.systemui.statusbar.notification.PropertyAnimator;
import com.android.systemui.statusbar.notification.collection.ListEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.provider.SectionStyleProvider;
-import com.android.systemui.statusbar.notification.stack.AnimationProperties;
import com.android.systemui.statusbar.window.StatusBarWindowController;
import com.android.wm.shell.bubbles.Bubbles;
@@ -91,8 +88,6 @@ public class NotificationIconAreaController implements
private final ArrayList<Rect> mTintAreas = new ArrayList<>();
private final Context mContext;
- private final DemoModeController mDemoModeController;
-
private final FeatureFlags mFeatureFlags;
private int mAodIconAppearTranslation;
@@ -139,8 +134,7 @@ public class NotificationIconAreaController implements
wakeUpCoordinator.addListener(this);
mBypassController = keyguardBypassController;
mBubblesOptional = bubblesOptional;
- mDemoModeController = demoModeController;
- mDemoModeController.addCallback(this);
+ demoModeController.addCallback(this);
mStatusBarWindowController = statusBarWindowController;
mScreenOffAnimationController = screenOffAnimationController;
notificationListener.addNotificationSettingsListener(mSettingsListener);
@@ -163,7 +157,6 @@ public class NotificationIconAreaController implements
LayoutInflater layoutInflater = LayoutInflater.from(context);
mNotificationIconArea = inflateIconArea(layoutInflater);
mNotificationIcons = mNotificationIconArea.findViewById(R.id.notificationIcons);
-
}
/**
@@ -185,16 +178,6 @@ public class NotificationIconAreaController implements
updateIconLayoutParams(mContext);
}
- /**
- * Update position of the view, with optional animation
- */
- public void updatePosition(int x, AnimationProperties props, boolean animate) {
- if (mAodIcons != null) {
- PropertyAnimator.setProperty(mAodIcons, AnimatableProperty.TRANSLATION_X, x, props,
- animate);
- }
- }
-
public void setupShelf(NotificationShelfController notificationShelfController) {
NotificationShelfController.assertRefactorFlagDisabled(mFeatureFlags);
mShelfIcons = notificationShelfController.getShelfIcons();
@@ -303,6 +286,7 @@ public class NotificationIconAreaController implements
}
return true;
}
+
/**
* Updates the notifications with the given list of notifications to display.
*/
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
index 418f9203761a..3770c1df77ff 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/NotificationIconContainer.java
@@ -54,19 +54,13 @@ import java.util.function.Consumer;
* correctly on the screen.
*/
public class NotificationIconContainer extends ViewGroup {
- /**
- * A float value indicating how much before the overflow start the icons should transform into
- * a dot. A value of 0 means that they are exactly at the end and a value of 1 means it starts
- * 1 icon width early.
- */
- public static final float OVERFLOW_EARLY_AMOUNT = 0.2f;
private static final int NO_VALUE = Integer.MIN_VALUE;
private static final String TAG = "NotificationIconContainer";
private static final boolean DEBUG = false;
private static final boolean DEBUG_OVERFLOW = false;
private static final int CANNED_ANIMATION_DURATION = 100;
private static final AnimationProperties DOT_ANIMATION_PROPERTIES = new AnimationProperties() {
- private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
+ private final AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
@Override
public AnimationFilter getAnimationFilter() {
@@ -75,7 +69,7 @@ public class NotificationIconContainer extends ViewGroup {
}.setDuration(200);
private static final AnimationProperties ICON_ANIMATION_PROPERTIES = new AnimationProperties() {
- private AnimationFilter mAnimationFilter = new AnimationFilter()
+ private final AnimationFilter mAnimationFilter = new AnimationFilter()
.animateX()
.animateY()
.animateAlpha()
@@ -92,7 +86,7 @@ public class NotificationIconContainer extends ViewGroup {
* Temporary AnimationProperties to avoid unnecessary allocations.
*/
private static final AnimationProperties sTempProperties = new AnimationProperties() {
- private AnimationFilter mAnimationFilter = new AnimationFilter();
+ private final AnimationFilter mAnimationFilter = new AnimationFilter();
@Override
public AnimationFilter getAnimationFilter() {
@@ -101,7 +95,7 @@ public class NotificationIconContainer extends ViewGroup {
};
private static final AnimationProperties ADD_ICON_PROPERTIES = new AnimationProperties() {
- private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
+ private final AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
@Override
public AnimationFilter getAnimationFilter() {
@@ -115,7 +109,7 @@ public class NotificationIconContainer extends ViewGroup {
*/
private static final AnimationProperties UNISOLATION_PROPERTY_OTHERS
= new AnimationProperties() {
- private AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
+ private final AnimationFilter mAnimationFilter = new AnimationFilter().animateAlpha();
@Override
public AnimationFilter getAnimationFilter() {
@@ -128,7 +122,7 @@ public class NotificationIconContainer extends ViewGroup {
* This animates the translation back to the right position.
*/
private static final AnimationProperties UNISOLATION_PROPERTY = new AnimationProperties() {
- private AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
+ private final AnimationFilter mAnimationFilter = new AnimationFilter().animateX();
@Override
public AnimationFilter getAnimationFilter() {
@@ -147,7 +141,6 @@ public class NotificationIconContainer extends ViewGroup {
private boolean mIsStaticLayout = true;
private final HashMap<View, IconState> mIconStates = new HashMap<>();
private int mDotPadding;
- private int mStaticDotRadius;
private int mStaticDotDiameter;
private int mActualLayoutWidth = NO_VALUE;
private float mActualPaddingEnd = NO_VALUE;
@@ -170,7 +163,7 @@ public class NotificationIconContainer extends ViewGroup {
private boolean mIsShowingOverflowDot;
private StatusBarIconView mIsolatedIcon;
private Rect mIsolatedIconLocation;
- private int[] mAbsolutePosition = new int[2];
+ private final int[] mAbsolutePosition = new int[2];
private View mIsolatedIconForAnimation;
private int mThemedTextColorPrimary;
@@ -186,8 +179,8 @@ public class NotificationIconContainer extends ViewGroup {
mMaxStaticIcons = getResources().getInteger(R.integer.max_notif_static_icons);
mDotPadding = getResources().getDimensionPixelSize(R.dimen.overflow_icon_dot_padding);
- mStaticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
- mStaticDotDiameter = 2 * mStaticDotRadius;
+ int staticDotRadius = getResources().getDimensionPixelSize(R.dimen.overflow_dot_radius);
+ mStaticDotDiameter = 2 * staticDotRadius;
final Context themedContext = new ContextThemeWrapper(getContext(),
com.android.internal.R.style.Theme_DeviceDefault_DayNight);
@@ -699,7 +692,6 @@ public class NotificationIconContainer extends ViewGroup {
}
public class IconState extends ViewState {
- public static final int NO_VALUE = NotificationIconContainer.NO_VALUE;
public float iconAppearAmount = 1.0f;
public float clampedAppearAmount = 1.0f;
public int visibleState;
@@ -817,8 +809,6 @@ public class NotificationIconContainer extends ViewGroup {
super.applyToView(view);
}
sTempProperties.setAnimationEndAction(null);
- boolean inShelf = iconAppearAmount == 1.0f;
- icon.setIsInShelf(inShelf);
}
justAdded = false;
justReplaced = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
index cb2a78d8be35..5c1dfbe42cf9 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -51,6 +51,7 @@ import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardUpdateMonitorCallback;
import com.android.keyguard.KeyguardViewController;
+import com.android.keyguard.TrustGrantFlags;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
import com.android.systemui.bouncer.domain.interactor.AlternateBouncerInteractor;
@@ -306,6 +307,16 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Nullable private TaskbarDelegate mTaskbarDelegate;
private final KeyguardUpdateMonitorCallback mUpdateMonitorCallback =
new KeyguardUpdateMonitorCallback() {
+ @Override
+ public void onTrustGrantedForCurrentUser(
+ boolean dismissKeyguard,
+ boolean newlyUnlocked,
+ @NonNull TrustGrantFlags flags,
+ @Nullable String message
+ ) {
+ updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide());
+ }
+
@Override
public void onEmergencyCallAction() {
// Since we won't get a setOccluded call we have to reset the view manually such that
@@ -431,7 +442,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mDockManager.addListener(mDockEventListener);
mIsDocked = mDockManager.isDocked();
}
- mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
}
/** Register a callback, to be invoked by the Predictive Back system. */
@@ -1570,14 +1580,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
|| mode == KeyguardSecurityModel.SecurityMode.SimPuk;
}
- private KeyguardStateController.Callback mKeyguardStateControllerCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onUnlockedChanged() {
- updateAlternateBouncerShowing(mAlternateBouncerInteractor.maybeHide());
- }
- };
-
/**
* Delegate used to send show and hide events to an alternate authentication method instead of
* the regular pin/pattern/password bouncer.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
index fff1b81db628..278a43ea1bf1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptAuthStateTest.kt
@@ -18,7 +18,7 @@ package com.android.systemui.biometrics.ui.viewmodel
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
-import com.android.systemui.biometrics.domain.model.BiometricModality
+import com.android.systemui.biometrics.shared.model.BiometricModality
import com.google.common.truth.Truth.assertThat
import org.junit.Test
import org.junit.runner.RunWith
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
index 87c9e583af4d..91140a9b0fc4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/ui/viewmodel/PromptViewModelTest.kt
@@ -27,11 +27,12 @@ import com.android.systemui.biometrics.data.repository.FakePromptRepository
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractor
import com.android.systemui.biometrics.domain.interactor.PromptSelectorInteractorImpl
import com.android.systemui.biometrics.domain.model.BiometricModalities
-import com.android.systemui.biometrics.domain.model.BiometricModality
import com.android.systemui.biometrics.extractAuthenticatorTypes
import com.android.systemui.biometrics.faceSensorPropertiesInternal
import com.android.systemui.biometrics.fingerprintSensorPropertiesInternal
+import com.android.systemui.biometrics.shared.model.BiometricModality
import com.android.systemui.coroutines.collectLastValue
+import com.android.systemui.coroutines.collectValues
import com.android.systemui.statusbar.VibratorHelper
import com.android.systemui.util.mockito.any
import com.google.common.truth.Truth.assertThat
@@ -131,20 +132,22 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
}
@Test
- fun plays_haptic_on_authenticated() = runGenericTest {
- viewModel.showAuthenticated(testCase.authenticatedModality, 1000L)
+ fun play_haptic_on_confirm_when_confirmation_required_otherwise_on_authenticated() =
+ runGenericTest {
+ val expectConfirmation = testCase.expectConfirmation(atLeastOneFailure = false)
- verify(vibrator).vibrateAuthSuccess(any())
- verify(vibrator, never()).vibrateAuthError(any())
- }
+ viewModel.showAuthenticated(testCase.authenticatedModality, 1_000L)
- @Test
- fun plays_no_haptic_on_confirm() = runGenericTest {
- viewModel.confirmAuthenticated()
+ verify(vibrator, if (expectConfirmation) never() else times(1))
+ .vibrateAuthSuccess(any())
- verify(vibrator, never()).vibrateAuthSuccess(any())
- verify(vibrator, never()).vibrateAuthError(any())
- }
+ if (expectConfirmation) {
+ viewModel.confirmAuthenticated()
+ }
+
+ verify(vibrator).vibrateAuthSuccess(any())
+ verify(vibrator, never()).vibrateAuthError(any())
+ }
private suspend fun TestScope.showAuthenticated(
authenticatedModality: BiometricModality,
@@ -204,7 +207,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun plays_haptic_on_errors() = runGenericTest {
- viewModel.showTemporaryError("so sad", hapticFeedback = true)
+ viewModel.showTemporaryError(
+ "so sad",
+ messageAfterError = "",
+ authenticateAfterError = false,
+ hapticFeedback = true,
+ )
verify(vibrator).vibrateAuthError(any())
verify(vibrator, never()).vibrateAuthSuccess(any())
@@ -212,7 +220,12 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
@Test
fun plays_haptic_on_errors_unless_skipped() = runGenericTest {
- viewModel.showTemporaryError("still sad", hapticFeedback = false)
+ viewModel.showTemporaryError(
+ "still sad",
+ messageAfterError = "",
+ authenticateAfterError = false,
+ hapticFeedback = false,
+ )
verify(vibrator, never()).vibrateAuthError(any())
verify(vibrator, never()).vibrateAuthSuccess(any())
@@ -287,7 +300,13 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(canTryAgain).isFalse()
}
- val errorJob = launch { viewModel.showTemporaryError("error") }
+ val errorJob = launch {
+ viewModel.showTemporaryError(
+ "error",
+ messageAfterError = "",
+ authenticateAfterError = false,
+ )
+ }
verifyNoError()
errorJob.join()
verifyNoError()
@@ -306,12 +325,66 @@ internal class PromptViewModelTest(private val testCase: TestCase) : SysuiTestCa
assertThat(messageIsShowing).isTrue()
}
- // @Test
- fun `suppress errors`() = runGenericTest {
- val errorMessage = "woot"
- val message by collectLastValue(viewModel.message)
+ @Test
+ fun suppress_temporary_error() = runGenericTest {
+ val messages by collectValues(viewModel.message)
+
+ for (error in listOf("never", "see", "me")) {
+ launch {
+ viewModel.showTemporaryError(
+ error,
+ messageAfterError = "or me",
+ authenticateAfterError = false,
+ suppressIf = { _ -> true },
+ )
+ }
+ }
+
+ testScheduler.advanceUntilIdle()
+ assertThat(messages).containsExactly(PromptMessage.Empty)
+ }
- val errorJob = launch { viewModel.showTemporaryError(errorMessage) }
+ @Test
+ fun suppress_temporary_error_when_already_showing_when_requested() =
+ suppress_temporary_error_when_already_showing(suppress = true)
+
+ @Test
+ fun do_not_suppress_temporary_error_when_already_showing_when_not_requested() =
+ suppress_temporary_error_when_already_showing(suppress = false)
+
+ private fun suppress_temporary_error_when_already_showing(suppress: Boolean) = runGenericTest {
+ val errors = listOf("woot", "oh yeah", "nope")
+ val afterSuffix = "(after)"
+ val expectedErrorMessage = if (suppress) errors.first() else errors.last()
+ val messages by collectValues(viewModel.message)
+
+ for (error in errors) {
+ launch {
+ viewModel.showTemporaryError(
+ error,
+ messageAfterError = "$error $afterSuffix",
+ authenticateAfterError = false,
+ suppressIf = { currentMessage -> suppress && currentMessage.isError },
+ )
+ }
+ }
+
+ testScheduler.runCurrent()
+ assertThat(messages)
+ .containsExactly(
+ PromptMessage.Empty,
+ PromptMessage.Error(expectedErrorMessage),
+ )
+ .inOrder()
+
+ testScheduler.advanceUntilIdle()
+ assertThat(messages)
+ .containsExactly(
+ PromptMessage.Empty,
+ PromptMessage.Error(expectedErrorMessage),
+ PromptMessage.Help("$expectedErrorMessage $afterSuffix"),
+ )
+ .inOrder()
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
index c2219a4d82eb..481f36e8ea38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/domain/interactor/BouncerInteractorTest.kt
@@ -345,23 +345,6 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(throttling).isEqualTo(AuthenticationThrottlingModel())
}
- @Test
- fun switchesToGone_whenUnlocked() =
- testScope.runTest {
- utils.authenticationRepository.setUnlocked(false)
- sceneInteractor.setCurrentScene(
- SceneTestUtils.CONTAINER_1,
- SceneModel(SceneKey.Bouncer)
- )
- val currentScene by
- collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1))
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Bouncer))
-
- utils.authenticationRepository.setUnlocked(true)
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
- }
-
private fun assertTryAgainMessage(
message: String?,
time: Int,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
index 0356036fa78f..0df0a17931f4 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/BouncerViewModelTest.kt
@@ -52,7 +52,10 @@ class BouncerViewModelTest : SysuiTestCase() {
authenticationInteractor = authenticationInteractor,
sceneInteractor = utils.sceneInteractor(),
)
- private val underTest = utils.bouncerViewModel(bouncerInteractor)
+ private val underTest =
+ utils.bouncerViewModel(
+ bouncerInteractor = bouncerInteractor,
+ )
@Test
fun authMethod_nonNullForSecureMethods_nullForNotSecureMethods() =
diff --git a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
index 7d6c4a1a1455..5c6d4c69b50b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/bouncer/ui/viewmodel/PinBouncerViewModelTest.kt
@@ -20,7 +20,6 @@ import androidx.test.filters.SmallTest
import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
-import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneKey
@@ -54,16 +53,8 @@ class PinBouncerViewModelTest : SysuiTestCase() {
sceneInteractor = sceneInteractor,
)
private val bouncerViewModel =
- BouncerViewModel(
- applicationContext = context,
- applicationScope = testScope.backgroundScope,
- interactorFactory =
- object : BouncerInteractor.Factory {
- override fun create(containerName: String): BouncerInteractor {
- return bouncerInteractor
- }
- },
- containerName = SceneTestUtils.CONTAINER_1,
+ utils.bouncerViewModel(
+ bouncerInteractor = bouncerInteractor,
)
private val underTest =
PinBouncerViewModel(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
index 8b36284b8620..ca6a5b6234b9 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/LockscreenSceneInteractorTest.kt
@@ -47,7 +47,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
private val underTest =
utils.lockScreenSceneInteractor(
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
bouncerInteractor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,
@@ -129,22 +128,6 @@ class LockscreenSceneInteractorTest : SysuiTestCase() {
}
@Test
- fun deviceLockedInNonLockScreenScene_switchesToLockScreenScene() =
- testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
- runCurrent()
- sceneInteractor.setCurrentScene(CONTAINER_1, SceneModel(SceneKey.Gone))
- runCurrent()
- utils.authenticationRepository.setUnlocked(true)
- runCurrent()
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Gone))
-
- utils.authenticationRepository.setUnlocked(false)
-
- assertThat(currentScene).isEqualTo(SceneModel(SceneKey.Lockscreen))
- }
-
- @Test
fun switchFromLockScreenToGone_authMethodNotSwipe_doesNotUnlockDevice() =
testScope.runTest {
val isUnlocked by collectLastValue(authenticationInteractor.isUnlocked)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
index 161dd660671e..ba8e0f277b6b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/ui/viewmodel/LockscreenSceneViewModelTest.kt
@@ -56,7 +56,6 @@ class LockscreenSceneViewModelTest : SysuiTestCase() {
override fun create(containerName: String): LockscreenSceneInteractor {
return utils.lockScreenSceneInteractor(
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
bouncerInteractor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
index 4eedc99839c6..ed7a59ea7032 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/ui/viewmodel/QuickSettingsSceneViewModelTest.kt
@@ -53,7 +53,6 @@ class QuickSettingsSceneViewModelTest : SysuiTestCase() {
override fun create(containerName: String): LockscreenSceneInteractor {
return utils.lockScreenSceneInteractor(
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
bouncerInteractor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt
index df3701edc6de..df5e7bcd16be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/scene/domain/startable/SystemUiDefaultSceneContainerStartableTest.kt
@@ -14,14 +14,11 @@
* limitations under the License.
*/
-@file:OptIn(ExperimentalCoroutinesApi::class, ExperimentalCoroutinesApi::class)
-
package com.android.systemui.scene.domain.startable
import androidx.test.filters.SmallTest
import com.android.systemui.SysuiTestCase
import com.android.systemui.coroutines.collectLastValue
-import com.android.systemui.flags.FakeFeatureFlags
import com.android.systemui.flags.Flags
import com.android.systemui.scene.SceneTestUtils
import com.android.systemui.scene.shared.model.SceneContainerNames
@@ -29,11 +26,14 @@ import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
import kotlinx.coroutines.ExperimentalCoroutinesApi
+import kotlinx.coroutines.flow.map
import kotlinx.coroutines.test.runTest
+import org.junit.Before
import org.junit.Test
import org.junit.runner.RunWith
import org.junit.runners.JUnit4
+@OptIn(ExperimentalCoroutinesApi::class)
@SmallTest
@RunWith(JUnit4::class)
class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() {
@@ -41,29 +41,47 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() {
private val utils = SceneTestUtils(this)
private val testScope = utils.testScope
private val sceneInteractor = utils.sceneInteractor()
- private val featureFlags = FakeFeatureFlags()
+ private val featureFlags = utils.featureFlags
+ private val authenticationRepository = utils.authenticationRepository()
+ private val authenticationInteractor =
+ utils.authenticationInteractor(
+ repository = authenticationRepository,
+ )
private val underTest =
SystemUiDefaultSceneContainerStartable(
applicationScope = testScope.backgroundScope,
sceneInteractor = sceneInteractor,
+ authenticationInteractor = authenticationInteractor,
featureFlags = featureFlags,
)
+ @Before
+ fun setUp() {
+ prepareState()
+ }
+
@Test
- fun start_featureEnabled_keepsVisibilityUpdated() =
+ fun hydrateVisibility_featureEnabled() =
testScope.runTest {
- featureFlags.set(Flags.SCENE_CONTAINER, true)
+ val currentSceneKey by
+ collectLastValue(
+ sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
+ it.key
+ }
+ )
val isVisible by
collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT))
+ prepareState(
+ isFeatureEnabled = true,
+ isDeviceUnlocked = true,
+ initialSceneKey = SceneKey.Gone,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
assertThat(isVisible).isTrue()
underTest.start()
- sceneInteractor.setCurrentScene(
- SceneContainerNames.SYSTEM_UI_DEFAULT,
- SceneModel(SceneKey.Gone)
- )
assertThat(isVisible).isFalse()
sceneInteractor.setCurrentScene(
@@ -74,14 +92,26 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() {
}
@Test
- fun start_featureDisabled_doesNotUpdateVisibility() =
+ fun hydrateVisibility_featureDisabled() =
testScope.runTest {
- featureFlags.set(Flags.SCENE_CONTAINER, false)
+ val currentSceneKey by
+ collectLastValue(
+ sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
+ it.key
+ }
+ )
val isVisible by
collectLastValue(sceneInteractor.isVisible(SceneContainerNames.SYSTEM_UI_DEFAULT))
+ prepareState(
+ isFeatureEnabled = false,
+ isDeviceUnlocked = true,
+ initialSceneKey = SceneKey.Lockscreen,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
assertThat(isVisible).isTrue()
underTest.start()
+ assertThat(isVisible).isTrue()
sceneInteractor.setCurrentScene(
SceneContainerNames.SYSTEM_UI_DEFAULT,
@@ -95,4 +125,172 @@ class SystemUiDefaultSceneContainerStartableTest : SysuiTestCase() {
)
assertThat(isVisible).isTrue()
}
+
+ @Test
+ fun switchToLockscreenWhenDeviceLocks_featureEnabled() =
+ testScope.runTest {
+ val currentSceneKey by
+ collectLastValue(
+ sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
+ it.key
+ }
+ )
+ prepareState(
+ isFeatureEnabled = true,
+ isDeviceUnlocked = true,
+ initialSceneKey = SceneKey.Gone,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ underTest.start()
+
+ authenticationRepository.setUnlocked(false)
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ @Test
+ fun switchToLockscreenWhenDeviceLocks_featureDisabled() =
+ testScope.runTest {
+ val currentSceneKey by
+ collectLastValue(
+ sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
+ it.key
+ }
+ )
+ prepareState(
+ isFeatureEnabled = false,
+ isDeviceUnlocked = false,
+ initialSceneKey = SceneKey.Gone,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ underTest.start()
+
+ authenticationRepository.setUnlocked(false)
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ }
+
+ @Test
+ fun switchFromBouncerToGoneWhenDeviceUnlocked_featureEnabled() =
+ testScope.runTest {
+ val currentSceneKey by
+ collectLastValue(
+ sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
+ it.key
+ }
+ )
+ prepareState(
+ isFeatureEnabled = true,
+ isDeviceUnlocked = false,
+ initialSceneKey = SceneKey.Bouncer,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
+ underTest.start()
+
+ authenticationRepository.setUnlocked(true)
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ }
+
+ @Test
+ fun switchFromBouncerToGoneWhenDeviceUnlocked_featureDisabled() =
+ testScope.runTest {
+ val currentSceneKey by
+ collectLastValue(
+ sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
+ it.key
+ }
+ )
+ prepareState(
+ isFeatureEnabled = false,
+ isDeviceUnlocked = false,
+ initialSceneKey = SceneKey.Bouncer,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
+ underTest.start()
+
+ authenticationRepository.setUnlocked(true)
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Bouncer)
+ }
+
+ @Test
+ fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOn() =
+ testScope.runTest {
+ val currentSceneKey by
+ collectLastValue(
+ sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
+ it.key
+ }
+ )
+ prepareState(
+ isFeatureEnabled = true,
+ isBypassEnabled = true,
+ initialSceneKey = SceneKey.Lockscreen,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ underTest.start()
+
+ authenticationRepository.setUnlocked(true)
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Gone)
+ }
+
+ @Test
+ fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOn_bypassOff() =
+ testScope.runTest {
+ val currentSceneKey by
+ collectLastValue(
+ sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
+ it.key
+ }
+ )
+ prepareState(
+ isFeatureEnabled = true,
+ isBypassEnabled = false,
+ initialSceneKey = SceneKey.Lockscreen,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ underTest.start()
+
+ authenticationRepository.setUnlocked(true)
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ @Test
+ fun switchFromLockscreenToGoneWhenDeviceUnlocksWithBypassOn_featureOff_bypassOn() =
+ testScope.runTest {
+ val currentSceneKey by
+ collectLastValue(
+ sceneInteractor.currentScene(SceneContainerNames.SYSTEM_UI_DEFAULT).map {
+ it.key
+ }
+ )
+ prepareState(
+ isFeatureEnabled = false,
+ isBypassEnabled = true,
+ initialSceneKey = SceneKey.Lockscreen,
+ )
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ underTest.start()
+
+ authenticationRepository.setUnlocked(true)
+
+ assertThat(currentSceneKey).isEqualTo(SceneKey.Lockscreen)
+ }
+
+ private fun prepareState(
+ isFeatureEnabled: Boolean = true,
+ isDeviceUnlocked: Boolean = false,
+ isBypassEnabled: Boolean = false,
+ initialSceneKey: SceneKey? = null,
+ ) {
+ featureFlags.set(Flags.SCENE_CONTAINER, isFeatureEnabled)
+ authenticationRepository.setUnlocked(isDeviceUnlocked)
+ authenticationRepository.setBypassEnabled(isBypassEnabled)
+ initialSceneKey?.let {
+ sceneInteractor.setCurrentScene(SceneContainerNames.SYSTEM_UI_DEFAULT, SceneModel(it))
+ }
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
index 16751c937f9e..5c35913f6e20 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/settings/brightness/BrightnessDialogTest.kt
@@ -16,12 +16,14 @@
package com.android.systemui.settings.brightness
+import android.content.Intent
import android.graphics.Rect
import android.os.Handler
import android.testing.AndroidTestingRunner
import android.testing.TestableLooper
import android.view.View
import android.view.ViewGroup
+import android.view.WindowManagerPolicyConstants.EXTRA_FROM_BRIGHTNESS_KEY
import androidx.test.filters.SmallTest
import androidx.test.rule.ActivityTestRule
import com.android.systemui.R
@@ -29,15 +31,20 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.activity.SingleActivityFactory
import com.android.systemui.settings.FakeDisplayTracker
import com.android.systemui.settings.UserTracker
+import com.android.systemui.statusbar.policy.AccessibilityManagerWrapper
+import com.android.systemui.util.concurrency.DelayableExecutor
+import com.android.systemui.util.concurrency.FakeExecutor
import com.android.systemui.util.mockito.any
+import com.android.systemui.util.time.FakeSystemClock
import com.google.common.truth.Truth.assertThat
-import java.util.concurrent.Executor
import org.junit.After
import org.junit.Before
import org.junit.Rule
import org.junit.Test
import org.junit.runner.RunWith
import org.mockito.Mock
+import org.mockito.Mockito.anyInt
+import org.mockito.Mockito.eq
import org.mockito.Mockito.`when`
import org.mockito.MockitoAnnotations
@@ -48,9 +55,12 @@ class BrightnessDialogTest : SysuiTestCase() {
@Mock private lateinit var userTracker: UserTracker
@Mock private lateinit var brightnessSliderControllerFactory: BrightnessSliderController.Factory
- @Mock private lateinit var mainExecutor: Executor
@Mock private lateinit var backgroundHandler: Handler
@Mock private lateinit var brightnessSliderController: BrightnessSliderController
+ @Mock private lateinit var accessibilityMgr: AccessibilityManagerWrapper
+
+ private val clock = FakeSystemClock()
+ private val mainExecutor = FakeExecutor(clock)
private var displayTracker = FakeDisplayTracker(mContext)
@@ -64,7 +74,8 @@ class BrightnessDialogTest : SysuiTestCase() {
displayTracker,
brightnessSliderControllerFactory,
mainExecutor,
- backgroundHandler
+ backgroundHandler,
+ accessibilityMgr
)
},
/* initialTouchMode= */ false,
@@ -77,8 +88,6 @@ class BrightnessDialogTest : SysuiTestCase() {
`when`(brightnessSliderControllerFactory.create(any(), any()))
.thenReturn(brightnessSliderController)
`when`(brightnessSliderController.rootView).thenReturn(View(context))
-
- activityRule.launchActivity(null)
}
@After
@@ -88,6 +97,7 @@ class BrightnessDialogTest : SysuiTestCase() {
@Test
fun testGestureExclusion() {
+ activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG))
val frame = activityRule.activity.requireViewById<View>(R.id.brightness_mirror_container)
val lp = frame.layoutParams as ViewGroup.MarginLayoutParams
@@ -104,18 +114,83 @@ class BrightnessDialogTest : SysuiTestCase() {
.isEqualTo(Rect(-horizontalMargin, 0, frame.width + horizontalMargin, frame.height))
}
+ @Test
+ fun testTimeout() {
+ `when`(
+ accessibilityMgr.getRecommendedTimeoutMillis(
+ eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
+ anyInt()
+ )
+ )
+ .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
+ val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)
+ intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true)
+ activityRule.launchActivity(intent)
+
+ assertThat(activityRule.activity.isFinishing()).isFalse()
+
+ clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong())
+ assertThat(activityRule.activity.isFinishing()).isTrue()
+ }
+
+ @Test
+ fun testRestartTimeout() {
+ `when`(
+ accessibilityMgr.getRecommendedTimeoutMillis(
+ eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
+ anyInt()
+ )
+ )
+ .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
+ val intent = Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG)
+ intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true)
+ activityRule.launchActivity(intent)
+
+ assertThat(activityRule.activity.isFinishing()).isFalse()
+
+ clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2)
+ // Restart the timeout
+ activityRule.activity.onResume()
+
+ clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2)
+ // The dialog should not have disappeared yet
+ assertThat(activityRule.activity.isFinishing()).isFalse()
+
+ clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong() / 2)
+ assertThat(activityRule.activity.isFinishing()).isTrue()
+ }
+
+ @Test
+ fun testNoTimeoutIfNotStartedByBrightnessKey() {
+ `when`(
+ accessibilityMgr.getRecommendedTimeoutMillis(
+ eq(BrightnessDialog.DIALOG_TIMEOUT_MILLIS),
+ anyInt()
+ )
+ )
+ .thenReturn(BrightnessDialog.DIALOG_TIMEOUT_MILLIS)
+ activityRule.launchActivity(Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG))
+
+ assertThat(activityRule.activity.isFinishing()).isFalse()
+
+ clock.advanceTime(BrightnessDialog.DIALOG_TIMEOUT_MILLIS.toLong())
+ assertThat(activityRule.activity.isFinishing()).isFalse()
+ }
+
class TestDialog(
userTracker: UserTracker,
displayTracker: FakeDisplayTracker,
brightnessSliderControllerFactory: BrightnessSliderController.Factory,
- mainExecutor: Executor,
- backgroundHandler: Handler
+ mainExecutor: DelayableExecutor,
+ backgroundHandler: Handler,
+ accessibilityMgr: AccessibilityManagerWrapper
) :
BrightnessDialog(
userTracker,
displayTracker,
brightnessSliderControllerFactory,
mainExecutor,
- backgroundHandler
+ backgroundHandler,
+ accessibilityMgr
)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
index 309ab058fb64..6e9fba64263b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/ui/viewmodel/ShadeSceneViewModelTest.kt
@@ -22,7 +22,6 @@ import com.android.systemui.authentication.shared.model.AuthenticationMethodMode
import com.android.systemui.coroutines.collectLastValue
import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor
import com.android.systemui.scene.SceneTestUtils
-import com.android.systemui.scene.SceneTestUtils.Companion.CONTAINER_1
import com.android.systemui.scene.shared.model.SceneKey
import com.android.systemui.scene.shared.model.SceneModel
import com.google.common.truth.Truth.assertThat
@@ -54,7 +53,6 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
override fun create(containerName: String): LockscreenSceneInteractor {
return utils.lockScreenSceneInteractor(
authenticationInteractor = authenticationInteractor,
- sceneInteractor = sceneInteractor,
bouncerInteractor =
utils.bouncerInteractor(
authenticationInteractor = authenticationInteractor,
@@ -89,7 +87,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
@Test
fun onContentClicked_deviceUnlocked_switchesToGone() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
+ val currentScene by
+ collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1))
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(true)
runCurrent()
@@ -102,7 +101,8 @@ class ShadeSceneViewModelTest : SysuiTestCase() {
@Test
fun onContentClicked_deviceLockedSecurely_switchesToBouncer() =
testScope.runTest {
- val currentScene by collectLastValue(sceneInteractor.currentScene(CONTAINER_1))
+ val currentScene by
+ collectLastValue(sceneInteractor.currentScene(SceneTestUtils.CONTAINER_1))
utils.authenticationRepository.setAuthenticationMethod(AuthenticationMethodModel.Pin)
utils.authenticationRepository.setUnlocked(false)
runCurrent()
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
index c7143debf8a8..ed9cf3f2f158 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java
@@ -35,6 +35,7 @@ import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.service.trust.TrustAgentService;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import android.view.MotionEvent;
@@ -57,6 +58,8 @@ import com.android.keyguard.KeyguardMessageArea;
import com.android.keyguard.KeyguardMessageAreaController;
import com.android.keyguard.KeyguardSecurityModel;
import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.keyguard.KeyguardUpdateMonitorCallback;
+import com.android.keyguard.TrustGrantFlags;
import com.android.keyguard.ViewMediatorCallback;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.biometrics.domain.interactor.UdfpsOverlayInteractor;
@@ -84,7 +87,6 @@ import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.SysuiStatusBarStateController;
import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
import com.android.systemui.unfold.SysUIUnfoldComponent;
import com.google.common.truth.Truth;
@@ -154,7 +156,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<OnBackInvokedCallback> mBackCallbackCaptor;
@Captor
- private ArgumentCaptor<KeyguardStateController.Callback> mKeyguardStateControllerCallback;
+ private ArgumentCaptor<KeyguardUpdateMonitorCallback> mKeyguardUpdateMonitorCallback;
@Before
@@ -936,18 +938,24 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
}
@Test
- public void onDeviceUnlocked_hideAlternateBouncerAndClearMessageArea() {
+ public void onTrustChanged_hideAlternateBouncerAndClearMessageArea() {
+ // GIVEN keyguard update monitor callback is registered
+ verify(mKeyguardUpdateMonitor).registerCallback(mKeyguardUpdateMonitorCallback.capture());
+
reset(mKeyguardUpdateMonitor);
reset(mKeyguardMessageAreaController);
- // GIVEN keyguard state controller callback is registered
- verify(mKeyguardStateController).addCallback(mKeyguardStateControllerCallback.capture());
-
// GIVEN alternate bouncer state = not visible
when(mAlternateBouncerInteractor.isVisibleState()).thenReturn(false);
- // WHEN the device is unlocked
- mKeyguardStateControllerCallback.getValue().onUnlockedChanged();
+ // WHEN the device is trusted by active unlock
+ mKeyguardUpdateMonitorCallback.getValue().onTrustGrantedForCurrentUser(
+ true,
+ true,
+ new TrustGrantFlags(TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD
+ | TrustAgentService.FLAG_GRANT_TRUST_TEMPORARY_AND_RENEWABLE),
+ null
+ );
// THEN the false visibility state is propagated to the keyguardUpdateMonitor
verify(mKeyguardUpdateMonitor).setAlternateBouncerShowing(eq(false));
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
index fec1187d8d11..d797962bae8b 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/scene/SceneTestUtils.kt
@@ -24,6 +24,8 @@ import com.android.systemui.authentication.domain.interactor.AuthenticationInter
import com.android.systemui.bouncer.data.repository.BouncerRepository
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.bouncer.ui.viewmodel.BouncerViewModel
+import com.android.systemui.flags.FakeFeatureFlags
+import com.android.systemui.flags.Flags
import com.android.systemui.keyguard.domain.interactor.LockscreenSceneInteractor
import com.android.systemui.scene.data.repository.SceneContainerRepository
import com.android.systemui.scene.domain.interactor.SceneInteractor
@@ -51,6 +53,7 @@ class SceneTestUtils(
) {
val testDispatcher = StandardTestDispatcher()
val testScope = TestScope(testDispatcher)
+ val featureFlags = FakeFeatureFlags().apply { set(Flags.SCENE_CONTAINER, true) }
private val userRepository: UserRepository by lazy {
FakeUserRepository().apply {
val users = listOf(UserInfo(/* id= */ 0, "name", /* flags= */ 0))
@@ -129,6 +132,7 @@ class SceneTestUtils(
repository = BouncerRepository(),
authenticationInteractor = authenticationInteractor,
sceneInteractor = sceneInteractor,
+ featureFlags = featureFlags,
containerName = CONTAINER_1,
)
}
@@ -145,13 +149,13 @@ class SceneTestUtils(
return bouncerInteractor
}
},
+ featureFlags = featureFlags,
containerName = CONTAINER_1,
)
}
fun lockScreenSceneInteractor(
authenticationInteractor: AuthenticationInteractor,
- sceneInteractor: SceneInteractor,
bouncerInteractor: BouncerInteractor,
): LockscreenSceneInteractor {
return LockscreenSceneInteractor(
@@ -163,7 +167,6 @@ class SceneTestUtils(
return bouncerInteractor
}
},
- sceneInteractor = sceneInteractor,
containerName = CONTAINER_1,
)
}
diff --git a/services/autofill/java/com/android/server/autofill/Helper.java b/services/autofill/java/com/android/server/autofill/Helper.java
index 82af38200166..7557071d0d4b 100644
--- a/services/autofill/java/com/android/server/autofill/Helper.java
+++ b/services/autofill/java/com/android/server/autofill/Helper.java
@@ -20,6 +20,8 @@ import static com.android.server.autofill.Helper.sDebug;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.UserIdInt;
+import android.app.ActivityManager;
import android.app.assist.AssistStructure;
import android.app.assist.AssistStructure.ViewNode;
import android.app.assist.AssistStructure.WindowNode;
@@ -40,6 +42,7 @@ import android.view.View;
import android.view.WindowManager;
import android.view.autofill.AutofillId;
import android.view.autofill.AutofillValue;
+import android.widget.RemoteViews;
import com.android.internal.logging.nano.MetricsProto.MetricsEvent;
import com.android.internal.util.ArrayUtils;
@@ -50,6 +53,8 @@ import java.lang.ref.WeakReference;
import java.util.ArrayDeque;
import java.util.ArrayList;
import java.util.Arrays;
+import java.util.concurrent.atomic.AtomicBoolean;
+
public final class Helper {
@@ -83,6 +88,44 @@ public final class Helper {
throw new UnsupportedOperationException("contains static members only");
}
+ private static boolean checkRemoteViewUriPermissions(
+ @UserIdInt int userId, @NonNull RemoteViews rView) {
+ final AtomicBoolean permissionsOk = new AtomicBoolean(true);
+
+ rView.visitUris(uri -> {
+ int uriOwnerId = android.content.ContentProvider.getUserIdFromUri(uri);
+ boolean allowed = uriOwnerId == userId;
+ permissionsOk.set(allowed && permissionsOk.get());
+ });
+
+ return permissionsOk.get();
+ }
+
+ /**
+ * Checks the URI permissions of the remote view,
+ * to see if the current userId is able to access it.
+ *
+ * Returns the RemoteView that is passed if user is able, null otherwise.
+ *
+ * TODO: instead of returning a null remoteview when
+ * the current userId cannot access an URI,
+ * return a new RemoteView with the URI removed.
+ */
+ public static @Nullable RemoteViews sanitizeRemoteView(RemoteViews rView) {
+ if (rView == null) return null;
+
+ int userId = ActivityManager.getCurrentUser();
+
+ boolean ok = checkRemoteViewUriPermissions(userId, rView);
+ if (!ok) {
+ Slog.w(TAG,
+ "sanitizeRemoteView() user: " + userId
+ + " tried accessing resource that does not belong to them");
+ }
+ return (ok ? rView : null);
+ }
+
+
@Nullable
static AutofillId[] toArray(@Nullable ArraySet<AutofillId> set) {
if (set == null) return null;
diff --git a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
index dbeb624bd202..fa414e3b172b 100644
--- a/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/DialogFillUi.java
@@ -53,6 +53,7 @@ import android.widget.TextView;
import com.android.internal.R;
import com.android.server.autofill.AutofillManagerService;
+import com.android.server.autofill.Helper;
import java.io.PrintWriter;
import java.util.ArrayList;
@@ -208,7 +209,8 @@ final class DialogFillUi {
}
private void setHeader(View decor, FillResponse response) {
- final RemoteViews presentation = response.getDialogHeader();
+ final RemoteViews presentation =
+ Helper.sanitizeRemoteView(response.getDialogHeader());
if (presentation == null) {
return;
}
@@ -243,9 +245,10 @@ final class DialogFillUi {
}
private void initialAuthenticationLayout(View decor, FillResponse response) {
- RemoteViews presentation = response.getDialogPresentation();
+ RemoteViews presentation = Helper.sanitizeRemoteView(
+ response.getDialogPresentation());
if (presentation == null) {
- presentation = response.getPresentation();
+ presentation = Helper.sanitizeRemoteView(response.getPresentation());
}
if (presentation == null) {
throw new RuntimeException("No presentation for fill dialog authentication");
@@ -289,7 +292,8 @@ final class DialogFillUi {
final Dataset dataset = response.getDatasets().get(i);
final int index = dataset.getFieldIds().indexOf(focusedViewId);
if (index >= 0) {
- RemoteViews presentation = dataset.getFieldDialogPresentation(index);
+ RemoteViews presentation = Helper.sanitizeRemoteView(
+ dataset.getFieldDialogPresentation(index));
if (presentation == null) {
if (sDebug) {
Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
diff --git a/services/autofill/java/com/android/server/autofill/ui/FillUi.java b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
index 129ce72e037d..cdfe7bb4f4a7 100644
--- a/services/autofill/java/com/android/server/autofill/ui/FillUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/FillUi.java
@@ -148,8 +148,9 @@ final class FillUi {
final LayoutInflater inflater = LayoutInflater.from(mContext);
- final RemoteViews headerPresentation = response.getHeader();
- final RemoteViews footerPresentation = response.getFooter();
+ final RemoteViews headerPresentation = Helper.sanitizeRemoteView(response.getHeader());
+ final RemoteViews footerPresentation = Helper.sanitizeRemoteView(response.getFooter());
+
final ViewGroup decor;
if (mFullScreen) {
decor = (ViewGroup) inflater.inflate(R.layout.autofill_dataset_picker_fullscreen, null);
@@ -227,6 +228,9 @@ final class FillUi {
ViewGroup container = decor.findViewById(R.id.autofill_dataset_picker);
final View content;
try {
+ if (Helper.sanitizeRemoteView(response.getPresentation()) == null) {
+ throw new RuntimeException("Permission error accessing RemoteView");
+ }
content = response.getPresentation().applyWithTheme(
mContext, decor, interceptionHandler, mThemeId);
container.addView(content);
@@ -306,7 +310,8 @@ final class FillUi {
final Dataset dataset = response.getDatasets().get(i);
final int index = dataset.getFieldIds().indexOf(focusedViewId);
if (index >= 0) {
- final RemoteViews presentation = dataset.getFieldPresentation(index);
+ final RemoteViews presentation = Helper.sanitizeRemoteView(
+ dataset.getFieldPresentation(index));
if (presentation == null) {
Slog.w(TAG, "not displaying UI on field " + focusedViewId + " because "
+ "service didn't provide a presentation for it on " + dataset);
diff --git a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
index f035d0764279..70382f1d5274 100644
--- a/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
+++ b/services/autofill/java/com/android/server/autofill/ui/SaveUi.java
@@ -384,8 +384,7 @@ final class SaveUi {
return false;
}
writeLog(MetricsEvent.AUTOFILL_SAVE_CUSTOM_DESCRIPTION);
-
- final RemoteViews template = customDescription.getPresentation();
+ final RemoteViews template = Helper.sanitizeRemoteView(customDescription.getPresentation());
if (template == null) {
Slog.w(TAG, "No remote view on custom description");
return false;
diff --git a/services/core/java/com/android/server/notification/NotificationManagerService.java b/services/core/java/com/android/server/notification/NotificationManagerService.java
index f1c0e130b2be..3325ddd58330 100644
--- a/services/core/java/com/android/server/notification/NotificationManagerService.java
+++ b/services/core/java/com/android/server/notification/NotificationManagerService.java
@@ -7008,7 +7008,7 @@ public class NotificationManagerService extends SystemService {
*/
private boolean canBeNonDismissible(ApplicationInfo ai, Notification notification) {
return notification.isMediaNotification() || isEnterpriseExempted(ai)
- || isCallNotification(ai.packageName, ai.uid, notification)
+ || notification.isStyle(Notification.CallStyle.class)
|| isDefaultSearchSelectorPackage(ai.packageName);
}
diff --git a/services/core/java/com/android/server/policy/PhoneWindowManager.java b/services/core/java/com/android/server/policy/PhoneWindowManager.java
index 279a48084252..5cfbcaafe82e 100644
--- a/services/core/java/com/android/server/policy/PhoneWindowManager.java
+++ b/services/core/java/com/android/server/policy/PhoneWindowManager.java
@@ -3211,8 +3211,10 @@ public class PhoneWindowManager implements WindowManagerPolicy {
minLinearBrightness, maxLinearBrightness);
mDisplayManager.setBrightness(screenDisplayId, adjustedLinearBrightness);
- startActivityAsUser(new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG),
- UserHandle.CURRENT_OR_SELF);
+ Intent intent = new Intent(Intent.ACTION_SHOW_BRIGHTNESS_DIALOG);
+ intent.addFlags(Intent.FLAG_ACTIVITY_NO_USER_ACTION);
+ intent.putExtra(EXTRA_FROM_BRIGHTNESS_KEY, true);
+ startActivityAsUser(intent, UserHandle.CURRENT_OR_SELF);
}
return true;
case KeyEvent.KEYCODE_KEYBOARD_BACKLIGHT_DOWN:
diff --git a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
index a1199d99f1c3..6747cea80115 100644
--- a/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
+++ b/services/credentials/java/com/android/server/credentials/CredentialManagerService.java
@@ -91,8 +91,6 @@ public final class CredentialManagerService
CredentialManagerService, CredentialManagerServiceImpl> {
private static final String TAG = "CredManSysService";
- private static final String DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API =
- "enable_credential_description_api";
private static final String PERMISSION_DENIED_ERROR = "permission_denied";
private static final String PERMISSION_DENIED_WRITE_SECURE_SETTINGS_ERROR =
"Caller is missing WRITE_SECURE_SETTINGS permission";
@@ -311,14 +309,7 @@ public final class CredentialManagerService
}
public static boolean isCredentialDescriptionApiEnabled() {
- final long origId = Binder.clearCallingIdentity();
- try {
- return DeviceConfig.getBoolean(
- DeviceConfig.NAMESPACE_CREDENTIAL, DEVICE_CONFIG_ENABLE_CREDENTIAL_DESC_API,
- false);
- } finally {
- Binder.restoreCallingIdentity(origId);
- }
+ return true;
}
@SuppressWarnings("GuardedBy") // ErrorProne requires initiateProviderSessionForRequestLocked
diff --git a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
index 30180e8f43a7..7e81ef22eb65 100755
--- a/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
+++ b/services/tests/uiservicestests/src/com/android/server/notification/NotificationManagerServiceTest.java
@@ -11269,7 +11269,6 @@ public class NotificationManagerServiceTest extends UiServiceTestCase {
// Given: a call notification has the flag FLAG_ONGOING_EVENT set
// feature flag: ALLOW_DISMISS_ONGOING is on
mTestFlagResolver.setFlagOverride(ALLOW_DISMISS_ONGOING, true);
- when(mTelecomManager.isInManagedCall()).thenReturn(true);
Person person = new Person.Builder()
.setName("caller")