summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--core/java/android/app/Activity.java38
-rw-r--r--core/java/android/app/ActivityManagerInternal.java5
-rw-r--r--core/java/android/appwidget/AppWidgetManager.java37
-rw-r--r--core/java/android/appwidget/AppWidgetProvider.java7
-rw-r--r--core/java/android/hardware/fingerprint/FingerprintManager.java1
-rw-r--r--core/java/android/provider/Settings.java7
-rw-r--r--core/java/android/service/voice/VoiceInteractionSession.java22
-rw-r--r--core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl25
-rw-r--r--core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java5
-rw-r--r--core/res/AndroidManifest.xml1
-rw-r--r--core/res/res/values/bools.xml1
-rw-r--r--core/res/res/values/config.xml10
-rw-r--r--core/res/res/values/symbols.xml5
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java26
-rw-r--r--libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java24
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java6
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java40
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java12
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java15
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java2
-rw-r--r--libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java12
-rw-r--r--libs/WindowManager/Shell/tests/OWNERS1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java1
-rw-r--r--packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java1
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl19
-rw-r--r--packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl3
-rw-r--r--packages/SystemUI/src/com/android/keyguard/ClockEventController.kt8
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java11
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java1
-rw-r--r--packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java78
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java8
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java3
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java548
-rw-r--r--packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt550
-rw-r--r--packages/SystemUI/src/com/android/systemui/flags/Flags.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt65
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt81
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt45
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt13
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt1
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt54
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt2
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt50
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt3
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt6
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java50
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java7
-rw-r--r--packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java56
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java33
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java4
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt22
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java2
-rw-r--r--packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java25
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java30
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java12
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java26
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt21
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java81
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java66
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java13
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java116
-rw-r--r--packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt2
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt1
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java9
-rw-r--r--packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java82
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt31
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java20
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthCredentialViewTest.kt163
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt7
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java10
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java172
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java178
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt102
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt65
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt23
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt39
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt74
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java4
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java15
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java6
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt3
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java21
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java34
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt43
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java2
-rw-r--r--packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManagerTest.java4
-rw-r--r--packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt7
-rw-r--r--services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java40
-rw-r--r--services/core/java/com/android/server/am/ActivityManagerService.java11
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java4
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java2
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java25
-rw-r--r--services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java2
-rw-r--r--services/core/java/com/android/server/dreams/DreamManagerService.java2
-rw-r--r--services/core/java/com/android/server/dreams/DreamUiEventLoggerImpl.java21
-rw-r--r--services/core/java/com/android/server/wm/ActivityRecord.java71
-rw-r--r--services/core/java/com/android/server/wm/AppTransitionController.java4
-rw-r--r--services/core/java/com/android/server/wm/Dimmer.java38
-rw-r--r--services/core/java/com/android/server/wm/DisplayPolicy.java88
-rw-r--r--services/core/java/com/android/server/wm/DisplayRotation.java4
-rw-r--r--services/core/java/com/android/server/wm/LockTaskController.java15
-rw-r--r--services/core/java/com/android/server/wm/StartingData.java3
-rw-r--r--services/core/java/com/android/server/wm/Task.java10
-rw-r--r--services/core/java/com/android/server/wm/TaskFragment.java8
-rw-r--r--services/core/java/com/android/server/wm/WindowManagerService.java5
-rw-r--r--services/core/java/com/android/server/wm/WindowState.java4
-rw-r--r--services/core/java/com/android/server/wm/WindowStateAnimator.java27
-rw-r--r--services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java14
-rw-r--r--services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java31
-rw-r--r--services/tests/servicestests/src/com/android/server/appwidget/TestAppWidgetProvider.java50
-rw-r--r--services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java14
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java7
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java2
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java1
-rw-r--r--services/tests/wmtests/src/com/android/server/wm/DimmerTests.java44
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java23
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java20
-rw-r--r--services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java229
136 files changed, 3037 insertions, 1576 deletions
diff --git a/core/java/android/app/Activity.java b/core/java/android/app/Activity.java
index 855366abae4e..81c3e8957cd1 100644
--- a/core/java/android/app/Activity.java
+++ b/core/java/android/app/Activity.java
@@ -87,10 +87,12 @@ import android.os.Parcelable;
import android.os.PersistableBundle;
import android.os.Process;
import android.os.RemoteException;
+import android.os.ServiceManager;
import android.os.ServiceManager.ServiceNotFoundException;
import android.os.StrictMode;
import android.os.Trace;
import android.os.UserHandle;
+import android.service.voice.VoiceInteractionSession;
import android.text.Selection;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
@@ -154,6 +156,7 @@ import android.window.WindowOnBackInvokedDispatcher;
import com.android.internal.R;
import com.android.internal.annotations.GuardedBy;
import com.android.internal.annotations.VisibleForTesting;
+import com.android.internal.app.IVoiceInteractionManagerService;
import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.ToolbarActionBar;
import com.android.internal.app.WindowDecorActionBar;
@@ -1601,6 +1604,25 @@ public class Activity extends ContextThemeWrapper
return callbacks;
}
+ private void notifyVoiceInteractionManagerServiceActivityEvent(
+ @VoiceInteractionSession.VoiceInteractionActivityEventType int type) {
+
+ final IVoiceInteractionManagerService service =
+ IVoiceInteractionManagerService.Stub.asInterface(
+ ServiceManager.getService(Context.VOICE_INTERACTION_MANAGER_SERVICE));
+ if (service == null) {
+ Log.w(TAG, "notifyVoiceInteractionManagerServiceActivityEvent: Can not get "
+ + "VoiceInteractionManagerService");
+ return;
+ }
+
+ try {
+ service.notifyActivityEventChanged(mToken, type);
+ } catch (RemoteException e) {
+ // Empty
+ }
+ }
+
/**
* Called when the activity is starting. This is where most initialization
* should go: calling {@link #setContentView(int)} to inflate the
@@ -1876,6 +1898,9 @@ public class Activity extends ContextThemeWrapper
mCalled = true;
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_START);
+
+ notifyVoiceInteractionManagerServiceActivityEvent(
+ VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_START);
}
/**
@@ -2019,6 +2044,12 @@ public class Activity extends ContextThemeWrapper
final Window win = getWindow();
if (win != null) win.makeActive();
if (mActionBar != null) mActionBar.setShowHideAnimationEnabled(true);
+
+ // Because the test case "com.android.launcher3.jank.BinderTests#testPressHome" doesn't
+ // allow any binder call in onResume, we call this method in onPostResume.
+ notifyVoiceInteractionManagerServiceActivityEvent(
+ VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_RESUME);
+
mCalled = true;
}
@@ -2394,6 +2425,10 @@ public class Activity extends ContextThemeWrapper
getAutofillClientController().onActivityPaused();
notifyContentCaptureManagerIfNeeded(CONTENT_CAPTURE_PAUSE);
+
+ notifyVoiceInteractionManagerServiceActivityEvent(
+ VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE);
+
mCalled = true;
}
@@ -2623,6 +2658,9 @@ public class Activity extends ContextThemeWrapper
getAutofillClientController().onActivityStopped(mIntent, mChangingConfigurations);
mEnterAnimationComplete = false;
+
+ notifyVoiceInteractionManagerServiceActivityEvent(
+ VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_STOP);
}
/**
diff --git a/core/java/android/app/ActivityManagerInternal.java b/core/java/android/app/ActivityManagerInternal.java
index f2ea060b1694..c95a7deba61f 100644
--- a/core/java/android/app/ActivityManagerInternal.java
+++ b/core/java/android/app/ActivityManagerInternal.java
@@ -731,10 +731,9 @@ public abstract class ActivityManagerInternal {
*/
public interface VoiceInteractionManagerProvider {
/**
- * Notifies the service when a high-level activity event has been changed, for example,
- * an activity was resumed or stopped.
+ * Notifies the service when an activity is destroyed.
*/
- void notifyActivityEventChanged();
+ void notifyActivityDestroyed(IBinder activityToken);
}
/**
diff --git a/core/java/android/appwidget/AppWidgetManager.java b/core/java/android/appwidget/AppWidgetManager.java
index a432b8dec2cb..fd9496947628 100644
--- a/core/java/android/appwidget/AppWidgetManager.java
+++ b/core/java/android/appwidget/AppWidgetManager.java
@@ -42,12 +42,15 @@ import android.os.Handler;
import android.os.RemoteException;
import android.os.UserHandle;
import android.util.DisplayMetrics;
+import android.util.Log;
import android.widget.RemoteViews;
import com.android.internal.appwidget.IAppWidgetService;
+import com.android.internal.os.BackgroundThread;
import java.util.Collections;
import java.util.List;
+import java.util.Objects;
/**
* Updates AppWidget state; gets information about installed AppWidget providers and other
@@ -63,6 +66,7 @@ import java.util.List;
@RequiresFeature(PackageManager.FEATURE_APP_WIDGETS)
public class AppWidgetManager {
+
/**
* Activity action to launch from your {@link AppWidgetHost} activity when you want to
* pick an AppWidget to display. The AppWidget picker activity will be launched.
@@ -332,6 +336,17 @@ public class AppWidgetManager {
public static final String ACTION_APPWIDGET_UPDATE = "android.appwidget.action.APPWIDGET_UPDATE";
/**
+ * A combination broadcast of APPWIDGET_ENABLED and APPWIDGET_UPDATE.
+ * Sent during boot time and when the host is binding the widget for the very first time
+ *
+ * @hide
+ */
+ @SdkConstant(SdkConstantType.BROADCAST_INTENT_ACTION)
+ @BroadcastBehavior(explicitOnly = true)
+ public static final String ACTION_APPWIDGET_ENABLE_AND_UPDATE = "android.appwidget.action"
+ + ".APPWIDGET_ENABLE_AND_UPDATE";
+
+ /**
* Sent when the custom extras for an AppWidget change.
*
* <p class="note">This is a protected intent that can only be sent
@@ -456,6 +471,8 @@ public class AppWidgetManager {
public static final String ACTION_APPWIDGET_HOST_RESTORED
= "android.appwidget.action.APPWIDGET_HOST_RESTORED";
+ private static final String TAG = "AppWidgetManager";
+
/**
* An intent extra that contains multiple appWidgetIds. These are id values as
* they were provided to the application during a recent restore from backup. It is
@@ -511,6 +528,26 @@ public class AppWidgetManager {
mPackageName = context.getOpPackageName();
mService = service;
mDisplayMetrics = context.getResources().getDisplayMetrics();
+ if (mService == null) {
+ return;
+ }
+ BackgroundThread.getExecutor().execute(() -> {
+ try {
+ mService.notifyProviderInheritance(getInstalledProvidersForPackage(mPackageName,
+ null)
+ .stream().filter(Objects::nonNull)
+ .map(info -> info.provider).filter(p -> {
+ try {
+ Class clazz = Class.forName(p.getClassName());
+ return AppWidgetProvider.class.isAssignableFrom(clazz);
+ } catch (Exception e) {
+ return false;
+ }
+ }).toArray(ComponentName[]::new));
+ } catch (Exception e) {
+ Log.e(TAG, "Nofity service of inheritance info", e);
+ }
+ });
}
/**
diff --git a/core/java/android/appwidget/AppWidgetProvider.java b/core/java/android/appwidget/AppWidgetProvider.java
index a5d2198a6e17..3344ebc083a2 100644
--- a/core/java/android/appwidget/AppWidgetProvider.java
+++ b/core/java/android/appwidget/AppWidgetProvider.java
@@ -58,7 +58,12 @@ public class AppWidgetProvider extends BroadcastReceiver {
// Protect against rogue update broadcasts (not really a security issue,
// just filter bad broacasts out so subclasses are less likely to crash).
String action = intent.getAction();
- if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
+ if (AppWidgetManager.ACTION_APPWIDGET_ENABLE_AND_UPDATE.equals(action)) {
+ this.onReceive(context, new Intent(intent)
+ .setAction(AppWidgetManager.ACTION_APPWIDGET_ENABLED));
+ this.onReceive(context, new Intent(intent)
+ .setAction(AppWidgetManager.ACTION_APPWIDGET_UPDATE));
+ } else if (AppWidgetManager.ACTION_APPWIDGET_UPDATE.equals(action)) {
Bundle extras = intent.getExtras();
if (extras != null) {
int[] appWidgetIds = extras.getIntArray(AppWidgetManager.EXTRA_APPWIDGET_IDS);
diff --git a/core/java/android/hardware/fingerprint/FingerprintManager.java b/core/java/android/hardware/fingerprint/FingerprintManager.java
index 0fd164de8ffb..085bfca158c1 100644
--- a/core/java/android/hardware/fingerprint/FingerprintManager.java
+++ b/core/java/android/hardware/fingerprint/FingerprintManager.java
@@ -918,7 +918,6 @@ public class FingerprintManager implements BiometricAuthenticator, BiometricFing
}
}
-
/**
* Forwards BiometricStateListener to FingerprintService
* @param listener new BiometricStateListener being added
diff --git a/core/java/android/provider/Settings.java b/core/java/android/provider/Settings.java
index c9fd1295a6e1..a955dbba97e7 100644
--- a/core/java/android/provider/Settings.java
+++ b/core/java/android/provider/Settings.java
@@ -9793,6 +9793,13 @@ public final class Settings {
"fingerprint_side_fps_auth_downtime";
/**
+ * Whether or not a SFPS device is required to be interactive for auth to unlock the device.
+ * @hide
+ */
+ public static final String SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED =
+ "sfps_require_screen_on_to_auth_enabled";
+
+ /**
* Whether or not debugging is enabled.
* @hide
*/
diff --git a/core/java/android/service/voice/VoiceInteractionSession.java b/core/java/android/service/voice/VoiceInteractionSession.java
index df727e9b7d3d..48f732ec9f12 100644
--- a/core/java/android/service/voice/VoiceInteractionSession.java
+++ b/core/java/android/service/voice/VoiceInteractionSession.java
@@ -19,6 +19,7 @@ package android.service.voice;
import static android.view.ViewGroup.LayoutParams.MATCH_PARENT;
import android.annotation.CallbackExecutor;
+import android.annotation.IntDef;
import android.annotation.IntRange;
import android.annotation.NonNull;
import android.annotation.Nullable;
@@ -71,6 +72,8 @@ import com.android.internal.util.function.pooled.PooledLambda;
import java.io.FileDescriptor;
import java.io.PrintWriter;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.Collections;
@@ -143,6 +146,25 @@ public class VoiceInteractionSession implements KeyEvent.Callback, ComponentCall
*/
public static final int SHOW_SOURCE_AUTOMOTIVE_SYSTEM_UI = 1 << 7;
+ /** @hide */
+ public static final int VOICE_INTERACTION_ACTIVITY_EVENT_START = 1;
+ /** @hide */
+ public static final int VOICE_INTERACTION_ACTIVITY_EVENT_RESUME = 2;
+ /** @hide */
+ public static final int VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE = 3;
+ /** @hide */
+ public static final int VOICE_INTERACTION_ACTIVITY_EVENT_STOP = 4;
+
+ /** @hide */
+ @IntDef(prefix = { "VOICE_INTERACTION_ACTIVITY_EVENT_" }, value = {
+ VOICE_INTERACTION_ACTIVITY_EVENT_START,
+ VOICE_INTERACTION_ACTIVITY_EVENT_RESUME,
+ VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE,
+ VOICE_INTERACTION_ACTIVITY_EVENT_STOP
+ })
+ @Retention(RetentionPolicy.SOURCE)
+ public @interface VoiceInteractionActivityEventType{}
+
final Context mContext;
final HandlerCaller mHandlerCaller;
diff --git a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
index 681693b1dbad..bd51f12539e8 100644
--- a/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
+++ b/core/java/com/android/internal/app/IVoiceInteractionManagerService.aidl
@@ -18,6 +18,8 @@ package com.android.internal.app;
import android.content.ComponentName;
import android.content.Intent;
+import android.hardware.soundtrigger.KeyphraseMetadata;
+import android.hardware.soundtrigger.SoundTrigger;
import android.media.AudioFormat;
import android.media.permission.Identity;
import android.os.Bundle;
@@ -25,18 +27,17 @@ import android.os.ParcelFileDescriptor;
import android.os.PersistableBundle;
import android.os.RemoteCallback;
import android.os.SharedMemory;
+import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
+import android.service.voice.IVoiceInteractionService;
+import android.service.voice.IVoiceInteractionSession;
+import android.service.voice.VisibleActivityInfo;
import com.android.internal.app.IHotwordRecognitionStatusCallback;
import com.android.internal.app.IVoiceActionCheckCallback;
-import com.android.internal.app.IVoiceInteractionSessionShowCallback;
-import com.android.internal.app.IVoiceInteractor;
import com.android.internal.app.IVoiceInteractionSessionListener;
+import com.android.internal.app.IVoiceInteractionSessionShowCallback;
import com.android.internal.app.IVoiceInteractionSoundTriggerSession;
-import android.hardware.soundtrigger.KeyphraseMetadata;
-import android.hardware.soundtrigger.SoundTrigger;
-import android.service.voice.IVoiceInteractionService;
-import android.service.voice.IVoiceInteractionSession;
-import android.service.voice.IMicrophoneHotwordDetectionVoiceInteractionCallback;
+import com.android.internal.app.IVoiceInteractor;
interface IVoiceInteractionManagerService {
void showSession(in Bundle sessionArgs, int flags);
@@ -289,4 +290,14 @@ interface IVoiceInteractionManagerService {
* Notifies when the session window is shown or hidden.
*/
void setSessionWindowVisible(in IBinder token, boolean visible);
+
+ /**
+ * Notifies when the Activity lifecycle event changed.
+ *
+ * @param activityToken The token of activity.
+ * @param type The type of lifecycle event of the activity lifecycle.
+ */
+ oneway void notifyActivityEventChanged(
+ in IBinder activityToken,
+ int type);
}
diff --git a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
index 6c689ff2b725..44997b4a9c30 100644
--- a/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
+++ b/core/java/com/android/internal/config/sysui/SystemUiDeviceConfigFlags.java
@@ -578,6 +578,11 @@ public final class SystemUiDeviceConfigFlags {
*/
public static final String CLIPBOARD_OVERLAY_SHOW_ACTIONS = "clipboard_overlay_show_actions";
+ /**
+ * (boolean) Whether to combine the broadcasts APPWIDGET_ENABLED and APPWIDGET_UPDATE
+ */
+ public static final String COMBINED_BROADCAST_ENABLED = "combined_broadcast_enabled";
+
private SystemUiDeviceConfigFlags() {
}
}
diff --git a/core/res/AndroidManifest.xml b/core/res/AndroidManifest.xml
index f7467b5a9f86..7787b7c61593 100644
--- a/core/res/AndroidManifest.xml
+++ b/core/res/AndroidManifest.xml
@@ -142,6 +142,7 @@
<protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLED" />
<protected-broadcast android:name="android.appwidget.action.APPWIDGET_HOST_RESTORED" />
<protected-broadcast android:name="android.appwidget.action.APPWIDGET_RESTORED" />
+ <protected-broadcast android:name="android.appwidget.action.APPWIDGET_ENABLE_AND_UPDATE" />
<protected-broadcast android:name="android.os.action.SETTING_RESTORED" />
diff --git a/core/res/res/values/bools.xml b/core/res/res/values/bools.xml
index 988303ea7bc5..4b27bf2849fb 100644
--- a/core/res/res/values/bools.xml
+++ b/core/res/res/values/bools.xml
@@ -31,5 +31,4 @@
lockscreen, setting this to true should come with customized drawables. -->
<bool name="use_lock_pattern_drawable">false</bool>
<bool name="resolver_landscape_phone">true</bool>
- <bool name="system_server_plays_face_haptics">true</bool>
</resources>
diff --git a/core/res/res/values/config.xml b/core/res/res/values/config.xml
index aa9a9499ed97..bd8a42920af6 100644
--- a/core/res/res/values/config.xml
+++ b/core/res/res/values/config.xml
@@ -2491,8 +2491,10 @@
assistant activities (ACTIVITY_TYPE_ASSISTANT) -->
<bool name="config_dismissDreamOnActivityStart">false</bool>
- <!-- The prefix of dream component names that are loggable. If empty, logs "other" for all. -->
- <string name="config_loggable_dream_prefix" translatable="false"></string>
+ <!-- The prefixes of dream component names that are loggable.
+ Matched against ComponentName#flattenToString() for dream components.
+ If empty, logs "other" for all. -->
+ <string-array name="config_loggable_dream_prefixes"></string-array>
<!-- ComponentName of a dream to show whenever the system would otherwise have
gone to sleep. When the PowerManager is asked to go to sleep, it will instead
@@ -4967,6 +4969,10 @@
<!-- If face auth sends the user directly to home/last open app, or stays on keyguard -->
<bool name="config_faceAuthDismissesKeyguard">true</bool>
+ <!-- Default value for whether a SFPS device is required to be interactive for fingerprint auth
+ to unlock the device. -->
+ <bool name="config_requireScreenOnToAuthEnabled">false</bool>
+
<!-- The component name for the default profile supervisor, which can be set as a profile owner
even after user setup is complete. The defined component should be used for supervision purposes
only. The component must be part of a system app. -->
diff --git a/core/res/res/values/symbols.xml b/core/res/res/values/symbols.xml
index 7a7b43a3632a..44d84683abf9 100644
--- a/core/res/res/values/symbols.xml
+++ b/core/res/res/values/symbols.xml
@@ -2245,7 +2245,7 @@
<java-symbol type="integer" name="config_dreamOverlayReconnectTimeoutMs" />
<java-symbol type="integer" name="config_dreamOverlayMaxReconnectAttempts" />
<java-symbol type="integer" name="config_minDreamOverlayDurationMs" />
- <java-symbol type="string" name="config_loggable_dream_prefix" />
+ <java-symbol type="array" name="config_loggable_dream_prefixes" />
<java-symbol type="string" name="config_dozeComponent" />
<java-symbol type="string" name="enable_explore_by_touch_warning_title" />
<java-symbol type="string" name="enable_explore_by_touch_warning_message" />
@@ -2723,6 +2723,7 @@
<java-symbol type="array" name="config_face_acquire_vendor_biometricprompt_ignorelist" />
<java-symbol type="bool" name="config_faceAuthSupportsSelfIllumination" />
<java-symbol type="bool" name="config_faceAuthDismissesKeyguard" />
+ <java-symbol type="bool" name="config_requireScreenOnToAuthEnabled" />
<!-- Face config -->
<java-symbol type="integer" name="config_faceMaxTemplatesPerUser" />
@@ -4861,6 +4862,4 @@
<java-symbol type="id" name="language_picker_header" />
<java-symbol type="dimen" name="status_bar_height_default" />
-
- <java-symbol type="bool" name="system_server_plays_face_haptics" />
</resources>
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
index 1d513e444050..16760e26b3f1 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitController.java
@@ -1873,6 +1873,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Override
public void onActivityPreCreated(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
+ if (activity.isChild()) {
+ // Skip Activity that is child of another Activity (ActivityGroup) because it's
+ // window will just be a child of the parent Activity window.
+ return;
+ }
synchronized (mLock) {
final IBinder activityToken = activity.getActivityToken();
final IBinder initialTaskFragmentToken =
@@ -1904,6 +1909,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Override
public void onActivityPostCreated(@NonNull Activity activity,
@Nullable Bundle savedInstanceState) {
+ if (activity.isChild()) {
+ // Skip Activity that is child of another Activity (ActivityGroup) because it's
+ // window will just be a child of the parent Activity window.
+ return;
+ }
// Calling after Activity#onCreate is complete to allow the app launch something
// first. In case of a configured placeholder activity we want to make sure
// that we don't launch it if an activity itself already requested something to be
@@ -1921,6 +1931,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Override
public void onActivityConfigurationChanged(@NonNull Activity activity) {
+ if (activity.isChild()) {
+ // Skip Activity that is child of another Activity (ActivityGroup) because it's
+ // window will just be a child of the parent Activity window.
+ return;
+ }
synchronized (mLock) {
final TransactionRecord transactionRecord = mTransactionManager
.startNewTransaction();
@@ -1934,6 +1949,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
@Override
public void onActivityPostDestroyed(@NonNull Activity activity) {
+ if (activity.isChild()) {
+ // Skip Activity that is child of another Activity (ActivityGroup) because it's
+ // window will just be a child of the parent Activity window.
+ return;
+ }
synchronized (mLock) {
SplitController.this.onActivityDestroyed(activity);
}
@@ -1969,7 +1989,11 @@ public class SplitController implements JetpackTaskFragmentOrganizer.TaskFragmen
if (who instanceof Activity) {
// We will check if the new activity should be split with the activity that launched
// it.
- launchingActivity = (Activity) who;
+ final Activity activity = (Activity) who;
+ // For Activity that is child of another Activity (ActivityGroup), treat the parent
+ // Activity as the launching one because it's window will just be a child of the
+ // parent Activity window.
+ launchingActivity = activity.isChild() ? activity.getParent() : activity;
if (isInPictureInPicture(launchingActivity)) {
// We don't embed activity when it is in PIP.
return super.onStartActivity(who, intent, options);
diff --git a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
index 79603233ae14..362f1fa096cc 100644
--- a/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
+++ b/libs/WindowManager/Jetpack/src/androidx/window/extensions/embedding/SplitPresenter.java
@@ -227,7 +227,7 @@ class SplitPresenter extends JetpackTaskFragmentOrganizer {
final TaskFragmentContainer curSecondaryContainer = mController.getContainerWithActivity(
secondaryActivity);
TaskFragmentContainer containerToAvoid = primaryContainer;
- if (curSecondaryContainer != null
+ if (curSecondaryContainer != null && curSecondaryContainer != primaryContainer
&& (rule.shouldClearTop() || primaryContainer.isAbove(curSecondaryContainer))) {
// Do not reuse the current TaskFragment if the rule is to clear top, or if it is below
// the primary TaskFragment.
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS
new file mode 100644
index 000000000000..7237d2bde39f
--- /dev/null
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/OWNERS
@@ -0,0 +1,2 @@
+# WM shell sub-modules splitscreen owner
+chenghsiuchang@google.com
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
index 5b7d141591ea..295a2e3c4244 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/common/split/SplitLayout.java
@@ -86,9 +86,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
private static final int FLING_ENTER_DURATION = 350;
private static final int FLING_EXIT_DURATION = 350;
- private final int mDividerWindowWidth;
- private final int mDividerInsets;
- private final int mDividerSize;
+ private int mDividerWindowWidth;
+ private int mDividerInsets;
+ private int mDividerSize;
private final Rect mTempRect = new Rect();
private final Rect mRootBounds = new Rect();
@@ -131,6 +131,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mContext = context.createConfigurationContext(configuration);
mOrientation = configuration.orientation;
mRotation = configuration.windowConfiguration.getRotation();
+ mDensity = configuration.densityDpi;
mSplitLayoutHandler = splitLayoutHandler;
mDisplayImeController = displayImeController;
mSplitWindowManager = new SplitWindowManager(windowName, mContext, configuration,
@@ -139,24 +140,22 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mImePositionProcessor = new ImePositionProcessor(mContext.getDisplayId());
mSurfaceEffectPolicy = new ResizingEffectPolicy(parallaxType);
- final Resources resources = context.getResources();
- mDividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width);
- mDividerInsets = getDividerInsets(resources, context.getDisplay());
- mDividerWindowWidth = mDividerSize + 2 * mDividerInsets;
+ updateDividerConfig(mContext);
mRootBounds.set(configuration.windowConfiguration.getBounds());
mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null);
resetDividerPosition();
- mDimNonImeSide = resources.getBoolean(R.bool.config_dimNonImeAttachedSide);
+ mDimNonImeSide = mContext.getResources().getBoolean(R.bool.config_dimNonImeAttachedSide);
updateInvisibleRect();
}
- private int getDividerInsets(Resources resources, Display display) {
+ private void updateDividerConfig(Context context) {
+ final Resources resources = context.getResources();
+ final Display display = context.getDisplay();
final int dividerInset = resources.getDimensionPixelSize(
com.android.internal.R.dimen.docked_stack_divider_insets);
-
int radius = 0;
RoundedCorner corner = display.getRoundedCorner(RoundedCorner.POSITION_TOP_LEFT);
radius = corner != null ? Math.max(radius, corner.getRadius()) : radius;
@@ -167,7 +166,9 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
corner = display.getRoundedCorner(RoundedCorner.POSITION_BOTTOM_LEFT);
radius = corner != null ? Math.max(radius, corner.getRadius()) : radius;
- return Math.max(dividerInset, radius);
+ mDividerInsets = Math.max(dividerInset, radius);
+ mDividerSize = resources.getDimensionPixelSize(R.dimen.split_divider_bar_width);
+ mDividerWindowWidth = mDividerSize + 2 * mDividerInsets;
}
/** Gets bounds of the primary split with screen based coordinate. */
@@ -309,6 +310,7 @@ public final class SplitLayout implements DisplayInsetsController.OnInsetsChange
mRotation = rotation;
mDensity = density;
mDividerSnapAlgorithm = getSnapAlgorithm(mContext, mRootBounds, null);
+ updateDividerConfig(mContext);
initDividerPosition(mTempRect);
updateInvisibleRect();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
index 16f1d1c2944c..f81c9f80830a 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipMenuController.java
@@ -23,6 +23,7 @@ import static android.view.WindowManager.LayoutParams.FLAG_WATCH_OUTSIDE_TOUCH;
import static android.view.WindowManager.LayoutParams.PRIVATE_FLAG_TRUSTED_OVERLAY;
import static android.view.WindowManager.LayoutParams.TYPE_APPLICATION_OVERLAY;
+import android.annotation.NonNull;
import android.annotation.Nullable;
import android.app.ActivityManager.RunningTaskInfo;
import android.app.RemoteAction;
@@ -70,6 +71,11 @@ public interface PipMenuController {
void setAppActions(List<RemoteAction> appActions, RemoteAction closeAction);
/**
+ * Wait until the next frame to run the given Runnable.
+ */
+ void runWithNextFrame(@NonNull Runnable runnable);
+
+ /**
* Resize the PiP menu with the given bounds. The PiP SurfaceControl is given if there is a
* need to synchronize the movements on the same frame as PiP.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
index f170e774739f..2d7c5ce6feb5 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/PipTaskOrganizer.java
@@ -179,8 +179,10 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
// This is necessary in case there was a resize animation ongoing when exit PIP
// started, in which case the first resize will be skipped to let the exit
// operation handle the final resize out of PIP mode. See b/185306679.
- finishResize(tx, destinationBounds, direction, animationType);
- sendOnPipTransitionFinished(direction);
+ finishResizeDelayedIfNeeded(() -> {
+ finishResize(tx, destinationBounds, direction, animationType);
+ sendOnPipTransitionFinished(direction);
+ });
}
}
@@ -196,6 +198,34 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
}
};
+ /**
+ * Finishes resizing the PiP, delaying the operation if it has to be synced with the PiP menu.
+ *
+ * This is done to avoid a race condition between the last transaction applied in
+ * onAnimationUpdate and the finishResize in onAnimationEnd. finishResize creates a
+ * WindowContainerTransaction, which is to be applied by WmCore later. It may happen that it
+ * gets applied before the transaction created by the last onAnimationUpdate. As a result of
+ * this, the PiP surface may get scaled after the new bounds are applied by WmCore, which
+ * makes the PiP surface have unexpected bounds. To avoid this, we delay the finishResize
+ * operation until the next frame. This aligns the last onAnimationUpdate transaction with the
+ * WCT application.
+ *
+ * The race only happens when the PiP surface transaction has to be synced with the PiP menu
+ * due to the necessity for a delay when syncing the PiP surface, the PiP menu surface and
+ * the PiP menu contents.
+ */
+ private void finishResizeDelayedIfNeeded(Runnable finishResizeRunnable) {
+ if (!shouldSyncPipTransactionWithMenu()) {
+ finishResizeRunnable.run();
+ return;
+ }
+ mPipMenuController.runWithNextFrame(finishResizeRunnable);
+ }
+
+ private boolean shouldSyncPipTransactionWithMenu() {
+ return mPipMenuController.isMenuVisible();
+ }
+
@VisibleForTesting
final PipTransitionController.PipTransitionCallback mPipTransitionCallback =
new PipTransitionController.PipTransitionCallback() {
@@ -221,7 +251,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
@Override
public boolean handlePipTransaction(SurfaceControl leash,
SurfaceControl.Transaction tx, Rect destinationBounds) {
- if (mPipMenuController.isMenuVisible()) {
+ if (shouldSyncPipTransactionWithMenu()) {
mPipMenuController.movePipMenu(leash, tx, destinationBounds);
return true;
}
@@ -1223,7 +1253,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.crop(tx, mLeash, toBounds)
.round(tx, mLeash, mPipTransitionState.isInPip());
- if (mPipMenuController.isMenuVisible()) {
+ if (shouldSyncPipTransactionWithMenu()) {
mPipMenuController.resizePipMenu(mLeash, tx, toBounds);
} else {
tx.apply();
@@ -1265,7 +1295,7 @@ public class PipTaskOrganizer implements ShellTaskOrganizer.TaskListener,
mSurfaceTransactionHelper
.scale(tx, mLeash, startBounds, toBounds, degrees)
.round(tx, mLeash, startBounds, toBounds);
- if (mPipMenuController.isMenuVisible()) {
+ if (shouldSyncPipTransactionWithMenu()) {
mPipMenuController.movePipMenu(mLeash, tx, toBounds);
} else {
tx.apply();
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
index 281ea530e9e1..27902b2231ba 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PhonePipMenuController.java
@@ -305,6 +305,18 @@ public class PhonePipMenuController implements PipMenuController {
showResizeHandle);
}
+ @Override
+ public void runWithNextFrame(Runnable runnable) {
+ if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+ runnable.run();
+ }
+
+ mPipMenuView.getViewRootImpl().registerRtFrameCallback(frame -> {
+ mMainHandler.post(runnable);
+ });
+ mPipMenuView.invalidate();
+ }
+
/**
* Move the PiP menu, which does a translation and possibly a scale transformation.
*/
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
index 616d447247de..d28a9f3cf8ff 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipController.java
@@ -612,9 +612,24 @@ public class PipController implements PipTransitionController.PipTransitionCallb
new DisplayInsetsController.OnInsetsChangedListener() {
@Override
public void insetsChanged(InsetsState insetsState) {
+ int oldMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
onDisplayChanged(
mDisplayController.getDisplayLayout(mPipBoundsState.getDisplayId()),
false /* saveRestoreSnapFraction */);
+ int newMaxMovementBound = mPipBoundsState.getMovementBounds().bottom;
+ if (!mEnablePipKeepClearAlgorithm) {
+ int pipTop = mPipBoundsState.getBounds().top;
+ int diff = newMaxMovementBound - oldMaxMovementBound;
+ if (diff < 0 && pipTop > newMaxMovementBound) {
+ // bottom inset has increased, move PiP up if it is too low
+ mPipMotionHelper.animateToOffset(mPipBoundsState.getBounds(),
+ newMaxMovementBound - pipTop);
+ }
+ if (diff > 0 && oldMaxMovementBound == pipTop) {
+ // bottom inset has decreased, move PiP down if it was by the edge
+ mPipMotionHelper.animateToOffset(mPipBoundsState.getBounds(), diff);
+ }
+ }
}
});
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
index 975d4bba276e..a9a97beb9180 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/phone/PipTouchHandler.java
@@ -427,7 +427,7 @@ public class PipTouchHandler {
// If this is from an IME or shelf adjustment, then we should move the PiP so that it is not
// occluded by the IME or shelf.
if (fromImeAdjustment || fromShelfAdjustment) {
- if (mTouchState.isUserInteracting()) {
+ if (mTouchState.isUserInteracting() && mTouchState.isDragging()) {
// Defer the update of the current movement bounds until after the user finishes
// touching the screen
} else if (ENABLE_PIP_KEEP_CLEAR_ALGORITHM) {
diff --git a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
index 4ce45e142c64..7d4b43be4f73 100644
--- a/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
+++ b/libs/WindowManager/Shell/src/com/android/wm/shell/pip/tv/TvPipMenuController.java
@@ -466,6 +466,18 @@ public class TvPipMenuController implements PipMenuController, TvPipMenuView.Lis
}
@Override
+ public void runWithNextFrame(Runnable runnable) {
+ if (mPipMenuView == null || mPipMenuView.getViewRootImpl() == null) {
+ runnable.run();
+ }
+
+ mPipMenuView.getViewRootImpl().registerRtFrameCallback(frame -> {
+ mMainHandler.post(runnable);
+ });
+ mPipMenuView.invalidate();
+ }
+
+ @Override
public void movePipMenu(SurfaceControl pipLeash, SurfaceControl.Transaction transaction,
Rect pipDestBounds) {
if (DEBUG) {
diff --git a/libs/WindowManager/Shell/tests/OWNERS b/libs/WindowManager/Shell/tests/OWNERS
index f4efc374ecc2..1c28c3d58ccb 100644
--- a/libs/WindowManager/Shell/tests/OWNERS
+++ b/libs/WindowManager/Shell/tests/OWNERS
@@ -6,3 +6,4 @@ pablogamito@google.com
lbill@google.com
madym@google.com
hwwang@google.com
+chenghsiuchang@google.com
diff --git a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
index 453a71327b50..8946516af8a2 100644
--- a/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
+++ b/packages/SettingsProvider/src/android/provider/settings/backup/SecureSettings.java
@@ -122,6 +122,7 @@ public class SecureSettings {
Settings.Secure.FINGERPRINT_SIDE_FPS_BP_POWER_WINDOW,
Settings.Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
Settings.Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME,
+ Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED,
Settings.Secure.ACTIVE_UNLOCK_ON_WAKE,
Settings.Secure.ACTIVE_UNLOCK_ON_UNLOCK_INTENT,
Settings.Secure.ACTIVE_UNLOCK_ON_BIOMETRIC_FAIL,
diff --git a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
index a39735ffe2c7..cbf79530a6b6 100644
--- a/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
+++ b/packages/SettingsProvider/src/android/provider/settings/validators/SecureSettingsValidators.java
@@ -177,6 +177,7 @@ public class SecureSettingsValidators {
VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_ENROLL_TAP_WINDOW,
NON_NEGATIVE_INTEGER_VALIDATOR);
VALIDATORS.put(Secure.FINGERPRINT_SIDE_FPS_AUTH_DOWNTIME, NON_NEGATIVE_INTEGER_VALIDATOR);
+ VALIDATORS.put(Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.SHOW_MEDIA_WHEN_BYPASSING, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_APP_ENABLED, BOOLEAN_VALIDATOR);
VALIDATORS.put(Secure.FACE_UNLOCK_ALWAYS_REQUIRE_CONFIRMATION, BOOLEAN_VALIDATOR);
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
index e743ec87bd1c..bfbe88c475ac 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/IOverviewProxy.aidl
@@ -20,6 +20,7 @@ import android.graphics.Rect;
import android.graphics.Region;
import android.os.Bundle;
import android.view.MotionEvent;
+import android.view.SurfaceControl;
import com.android.systemui.shared.recents.ISystemUiProxy;
oneway interface IOverviewProxy {
@@ -44,12 +45,6 @@ oneway interface IOverviewProxy {
void onOverviewHidden(boolean triggeredFromAltTab, boolean triggeredFromHomeKey) = 8;
/**
- * Sent when there was an action on one of the onboarding tips view.
- * TODO: Move this implementation to SystemUI completely
- */
- void onTip(int actionType, int viewType) = 10;
-
- /**
* Sent when device assistant changes its default assistant whether it is available or not.
*/
void onAssistantAvailable(boolean available) = 13;
@@ -60,13 +55,6 @@ oneway interface IOverviewProxy {
void onAssistantVisibilityChanged(float visibility) = 14;
/**
- * Sent when back is triggered.
- * TODO: Move this implementation to SystemUI completely
- */
- void onBackAction(boolean completed, int downX, int downY, boolean isButton,
- boolean gestureSwipeLeft) = 15;
-
- /**
* Sent when some system ui state changes.
*/
void onSystemUiStateChanged(int stateFlags) = 16;
@@ -115,4 +103,9 @@ oneway interface IOverviewProxy {
* Sent when split keyboard shortcut is triggered to enter stage split.
*/
void enterStageSplitFromRunningApp(boolean leftOrTop) = 25;
+
+ /**
+ * Sent when the surface for navigation bar is created or changed
+ */
+ void onNavigationBarSurface(in SurfaceControl surface) = 26;
}
diff --git a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
index 2b2b05ce2fbf..b99b72bb4275 100644
--- a/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
+++ b/packages/SystemUI/shared/src/com/android/systemui/shared/recents/ISystemUiProxy.aidl
@@ -106,9 +106,6 @@ interface ISystemUiProxy {
/** Sets home rotation enabled. */
void setHomeRotationEnabled(boolean enabled) = 45;
- /** Notifies that a swipe-up gesture has started */
- oneway void notifySwipeUpGestureStarted() = 46;
-
/** Notifies when taskbar status updated */
oneway void notifyTaskbarStatus(boolean visible, boolean stashed) = 47;
diff --git a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
index d48d7ffcd824..c9b8712bdde9 100644
--- a/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
+++ b/packages/SystemUI/src/com/android/keyguard/ClockEventController.kt
@@ -228,7 +228,7 @@ open class ClockEventController @Inject constructor(
listenForDozing(this)
if (featureFlags.isEnabled(DOZING_MIGRATION_1)) {
listenForDozeAmountTransition(this)
- listenForGoneToAodTransition(this)
+ listenForAnyStateToAodTransition(this)
} else {
listenForDozeAmount(this)
}
@@ -286,10 +286,10 @@ open class ClockEventController @Inject constructor(
* dozing.
*/
@VisibleForTesting
- internal fun listenForGoneToAodTransition(scope: CoroutineScope): Job {
+ internal fun listenForAnyStateToAodTransition(scope: CoroutineScope): Job {
return scope.launch {
- keyguardTransitionInteractor.goneToAodTransition.filter {
- it.transitionState == TransitionState.STARTED
+ keyguardTransitionInteractor.anyStateToAodTransition.filter {
+ it.transitionState == TransitionState.FINISHED
}.collect {
dozeAmount = 1f
clock?.animations?.doze(dozeAmount)
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
index 71470e8870de..a0206f1f1e70 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardListenModel.kt
@@ -35,6 +35,7 @@ data class KeyguardFingerprintListenModel(
val keyguardOccluded: Boolean,
val occludingAppRequestingFp: Boolean,
val primaryUser: Boolean,
+ val shouldListenSfpsState: Boolean,
val shouldListenForFingerprintAssistant: Boolean,
val switchingUser: Boolean,
val udfps: Boolean,
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
index c756a17976bf..2bb3a5f437f5 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainer.java
@@ -106,8 +106,9 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
static final int USER_TYPE_WORK_PROFILE = 2;
static final int USER_TYPE_SECONDARY_USER = 3;
- @IntDef({MODE_DEFAULT, MODE_ONE_HANDED, MODE_USER_SWITCHER})
+ @IntDef({MODE_UNINITIALIZED, MODE_DEFAULT, MODE_ONE_HANDED, MODE_USER_SWITCHER})
public @interface Mode {}
+ static final int MODE_UNINITIALIZED = -1;
static final int MODE_DEFAULT = 0;
static final int MODE_ONE_HANDED = 1;
static final int MODE_USER_SWITCHER = 2;
@@ -154,7 +155,11 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
private boolean mDisappearAnimRunning;
private SwipeListener mSwipeListener;
private ViewMode mViewMode = new DefaultViewMode();
- private @Mode int mCurrentMode = MODE_DEFAULT;
+ /*
+ * Using MODE_UNINITIALIZED to mean the view mode is set to DefaultViewMode, but init() has not
+ * yet been called on it. This will happen when the ViewController is initialized.
+ */
+ private @Mode int mCurrentMode = MODE_UNINITIALIZED;
private int mWidth = -1;
private final WindowInsetsAnimation.Callback mWindowInsetsAnimationCallback =
@@ -347,6 +352,8 @@ public class KeyguardSecurityContainer extends ConstraintLayout {
private String modeToString(@Mode int mode) {
switch (mode) {
+ case MODE_UNINITIALIZED:
+ return "Uninitialized";
case MODE_DEFAULT:
return "Default";
case MODE_ONE_HANDED:
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
index 79a01b9c9717..fbb114c72add 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardSecurityContainerController.java
@@ -318,6 +318,7 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
@Override
public void onInit() {
mSecurityViewFlipperController.init();
+ configureMode();
}
@Override
diff --git a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
index bad75e8dd862..558869c46373 100644
--- a/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
+++ b/packages/SystemUI/src/com/android/keyguard/KeyguardUpdateMonitor.java
@@ -151,6 +151,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.Assert;
+import com.android.systemui.util.settings.SecureSettings;
import com.google.android.collect.Lists;
@@ -337,17 +338,20 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
private final ArrayList<WeakReference<KeyguardUpdateMonitorCallback>>
mCallbacks = Lists.newArrayList();
private ContentObserver mDeviceProvisionedObserver;
+ private ContentObserver mSfpsRequireScreenOnToAuthPrefObserver;
private final ContentObserver mTimeFormatChangeObserver;
private boolean mSwitchingUser;
private boolean mDeviceInteractive;
+ private boolean mSfpsRequireScreenOnToAuthPrefEnabled;
private final SubscriptionManager mSubscriptionManager;
private final TelephonyListenerManager mTelephonyListenerManager;
private final TrustManager mTrustManager;
private final UserManager mUserManager;
private final DevicePolicyManager mDevicePolicyManager;
private final BroadcastDispatcher mBroadcastDispatcher;
+ private final SecureSettings mSecureSettings;
private final InteractionJankMonitor mInteractionJankMonitor;
private final LatencyTracker mLatencyTracker;
private final StatusBarStateController mStatusBarStateController;
@@ -396,6 +400,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
protected Handler getHandler() {
return mHandler;
}
+
private final Handler mHandler;
private final IBiometricEnabledOnKeyguardCallback mBiometricEnabledCallback =
@@ -720,6 +725,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
/**
* Request to listen for face authentication when an app is occluding keyguard.
+ *
* @param request if true and mKeyguardOccluded, request face auth listening, else default
* to normal behavior.
* See {@link KeyguardUpdateMonitor#shouldListenForFace()}
@@ -732,6 +738,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
/**
* Request to listen for fingerprint when an app is occluding keyguard.
+ *
* @param request if true and mKeyguardOccluded, request fingerprint listening, else default
* to normal behavior.
* See {@link KeyguardUpdateMonitor#shouldListenForFingerprint(boolean)}
@@ -1946,6 +1953,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
Context context,
@Main Looper mainLooper,
BroadcastDispatcher broadcastDispatcher,
+ SecureSettings secureSettings,
DumpManager dumpManager,
@Background Executor backgroundExecutor,
@Main Executor mainExecutor,
@@ -1988,6 +1996,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mStatusBarState = mStatusBarStateController.getState();
mLockPatternUtils = lockPatternUtils;
mAuthController = authController;
+ mSecureSettings = secureSettings;
dumpManager.registerDumpable(getClass().getName(), this);
mSensorPrivacyManager = sensorPrivacyManager;
mActiveUnlockConfig = activeUnlockConfiguration;
@@ -2229,9 +2238,35 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
Settings.System.TIME_12_24)));
}
};
+
mContext.getContentResolver().registerContentObserver(
Settings.System.getUriFor(Settings.System.TIME_12_24),
false, mTimeFormatChangeObserver, UserHandle.USER_ALL);
+
+ updateSfpsRequireScreenOnToAuthPref();
+ mSfpsRequireScreenOnToAuthPrefObserver = new ContentObserver(mHandler) {
+ @Override
+ public void onChange(boolean selfChange) {
+ updateSfpsRequireScreenOnToAuthPref();
+ }
+ };
+
+ mContext.getContentResolver().registerContentObserver(
+ mSecureSettings.getUriFor(
+ Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED),
+ false,
+ mSfpsRequireScreenOnToAuthPrefObserver,
+ getCurrentUser());
+ }
+
+ protected void updateSfpsRequireScreenOnToAuthPref() {
+ final int defaultSfpsRequireScreenOnToAuthValue =
+ mContext.getResources().getBoolean(
+ com.android.internal.R.bool.config_requireScreenOnToAuthEnabled) ? 1 : 0;
+ mSfpsRequireScreenOnToAuthPrefEnabled = mSecureSettings.getIntForUser(
+ Settings.Secure.SFPS_REQUIRE_SCREEN_ON_TO_AUTH_ENABLED,
+ defaultSfpsRequireScreenOnToAuthValue,
+ getCurrentUser()) != 0;
}
private void initializeSimState() {
@@ -2276,6 +2311,22 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
}
/**
+ * @return true if there's at least one sfps enrollment for the current user.
+ */
+ public boolean isSfpsEnrolled() {
+ return mAuthController.isSfpsEnrolled(getCurrentUser());
+ }
+
+ /**
+ * @return true if sfps HW is supported on this device. Can return true even if the user has
+ * not enrolled sfps. This may be false if called before onAllAuthenticatorsRegistered.
+ */
+ public boolean isSfpsSupported() {
+ return mAuthController.getSfpsProps() != null
+ && !mAuthController.getSfpsProps().isEmpty();
+ }
+
+ /**
* @return true if there's at least one face enrolled
*/
public boolean isFaceEnrolled() {
@@ -2598,13 +2649,21 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
!(mFingerprintLockedOut && mBouncerIsOrWillBeShowing && mCredentialAttempted);
final boolean isEncryptedOrLockdownForUser = isEncryptedOrLockdown(user);
+
final boolean shouldListenUdfpsState = !isUdfps
|| (!userCanSkipBouncer
- && !isEncryptedOrLockdownForUser
- && userDoesNotHaveTrust);
+ && !isEncryptedOrLockdownForUser
+ && userDoesNotHaveTrust);
+
+ boolean shouldListenSideFpsState = true;
+ if (isSfpsSupported() && isSfpsEnrolled()) {
+ shouldListenSideFpsState =
+ mSfpsRequireScreenOnToAuthPrefEnabled ? isDeviceInteractive() : true;
+ }
- final boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
- && shouldListenBouncerState && shouldListenUdfpsState && !isFingerprintLockedOut();
+ boolean shouldListen = shouldListenKeyguardState && shouldListenUserState
+ && shouldListenBouncerState && shouldListenUdfpsState && !isFingerprintLockedOut()
+ && shouldListenSideFpsState;
maybeLogListenerModelData(
new KeyguardFingerprintListenModel(
@@ -2626,6 +2685,7 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mKeyguardOccluded,
mOccludingAppRequestingFp,
mIsPrimaryUser,
+ shouldListenSideFpsState,
shouldListenForFingerprintAssistant,
mSwitchingUser,
isUdfps,
@@ -3727,6 +3787,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
mContext.getContentResolver().unregisterContentObserver(mTimeFormatChangeObserver);
}
+ if (mSfpsRequireScreenOnToAuthPrefObserver != null) {
+ mContext.getContentResolver().unregisterContentObserver(
+ mSfpsRequireScreenOnToAuthPrefObserver);
+ }
+
try {
ActivityManager.getService().unregisterUserSwitchObserver(mUserSwitchObserver);
} catch (RemoteException e) {
@@ -3799,6 +3864,11 @@ public class KeyguardUpdateMonitor implements TrustManager.TrustListener, Dumpab
pw.println(" mBouncerIsOrWillBeShowing=" + mBouncerIsOrWillBeShowing);
pw.println(" mStatusBarState=" + StatusBarState.toString(mStatusBarState));
pw.println(" mUdfpsBouncerShowing=" + mUdfpsBouncerShowing);
+ } else if (isSfpsSupported()) {
+ pw.println(" sfpsEnrolled=" + isSfpsEnrolled());
+ pw.println(" shouldListenForSfps=" + shouldListenForFingerprint(false));
+ pw.println(" mSfpsRequireScreenOnToAuthPrefEnabled="
+ + mSfpsRequireScreenOnToAuthPrefEnabled);
}
}
if (mFaceManager != null && mFaceManager.isHardwareDetected()) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
index 80c6c48cb7ee..0a2d8ec97ba6 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthContainerView.java
@@ -120,7 +120,7 @@ public class AuthContainerView extends LinearLayout
@VisibleForTesting final BiometricCallback mBiometricCallback;
@Nullable private AuthBiometricView mBiometricView;
- @Nullable private AuthCredentialView mCredentialView;
+ @VisibleForTesting @Nullable AuthCredentialView mCredentialView;
private final AuthPanelController mPanelController;
private final FrameLayout mFrameLayout;
private final ImageView mBackgroundView;
@@ -762,6 +762,12 @@ public class AuthContainerView extends LinearLayout
}
mContainerState = STATE_ANIMATING_OUT;
+ // Request hiding soft-keyboard before animating away credential UI, in case IME insets
+ // animation get delayed by dismissing animation.
+ if (isAttachedToWindow() && getRootWindowInsets().isVisible(WindowInsets.Type.ime())) {
+ getWindowInsetsController().hide(WindowInsets.Type.ime());
+ }
+
if (sendReason) {
mPendingCallbackReason = reason;
} else {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
index c015a21c7db4..ff18eeea45a5 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthController.java
@@ -78,6 +78,7 @@ import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
@@ -150,6 +151,7 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
@Nullable private List<FingerprintSensorPropertiesInternal> mSidefpsProps;
@NonNull private final SparseBooleanArray mUdfpsEnrolledForUser;
+ @NonNull private final SparseBooleanArray mSfpsEnrolledForUser;
@NonNull private final SensorPrivacyManager mSensorPrivacyManager;
private final WakefulnessLifecycle mWakefulnessLifecycle;
private boolean mAllFingerprintAuthenticatorsRegistered;
@@ -159,6 +161,19 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
private final @Background DelayableExecutor mBackgroundExecutor;
private final DisplayInfo mCachedDisplayInfo = new DisplayInfo();
+
+ private final VibratorHelper mVibratorHelper;
+
+ private void vibrateSuccess(int modality) {
+ mVibratorHelper.vibrateAuthSuccess(
+ getClass().getSimpleName() + ", modality = " + modality + "BP::success");
+ }
+
+ private void vibrateError(int modality) {
+ mVibratorHelper.vibrateAuthError(
+ getClass().getSimpleName() + ", modality = " + modality + "BP::error");
+ }
+
@VisibleForTesting
final TaskStackListener mTaskStackListener = new TaskStackListener() {
@Override
@@ -325,6 +340,16 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
}
}
}
+
+ if (mSidefpsProps == null) {
+ Log.d(TAG, "handleEnrollmentsChanged, mSidefpsProps is null");
+ } else {
+ for (FingerprintSensorPropertiesInternal prop : mSidefpsProps) {
+ if (prop.sensorId == sensorId) {
+ mSfpsEnrolledForUser.put(userId, hasEnrollments);
+ }
+ }
+ }
for (Callback cb : mCallbacks) {
cb.onEnrollmentsChanged();
}
@@ -660,7 +685,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
@NonNull StatusBarStateController statusBarStateController,
@NonNull InteractionJankMonitor jankMonitor,
@Main Handler handler,
- @Background DelayableExecutor bgExecutor) {
+ @Background DelayableExecutor bgExecutor,
+ @NonNull VibratorHelper vibrator) {
mContext = context;
mExecution = execution;
mUserManager = userManager;
@@ -677,6 +703,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
mWindowManager = windowManager;
mInteractionJankMonitor = jankMonitor;
mUdfpsEnrolledForUser = new SparseBooleanArray();
+ mSfpsEnrolledForUser = new SparseBooleanArray();
+ mVibratorHelper = vibrator;
mOrientationListener = new BiometricDisplayListener(
context,
@@ -866,6 +894,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
public void onBiometricAuthenticated(@Modality int modality) {
if (DEBUG) Log.d(TAG, "onBiometricAuthenticated: ");
+ vibrateSuccess(modality);
+
if (mCurrentDialog != null) {
mCurrentDialog.onAuthenticationSucceeded(modality);
} else {
@@ -889,6 +919,11 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
return mUdfpsProps;
}
+ @Nullable
+ public List<FingerprintSensorPropertiesInternal> getSfpsProps() {
+ return mSidefpsProps;
+ }
+
private String getErrorString(@Modality int modality, int error, int vendorCode) {
switch (modality) {
case TYPE_FACE:
@@ -913,6 +948,8 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
Log.d(TAG, String.format("onBiometricError(%d, %d, %d)", modality, error, vendorCode));
}
+ vibrateError(modality);
+
final boolean isLockout = (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT)
|| (error == BiometricConstants.BIOMETRIC_ERROR_LOCKOUT_PERMANENT);
@@ -1013,6 +1050,17 @@ public class AuthController implements CoreStartable, CommandQueue.Callbacks,
return mUdfpsEnrolledForUser.get(userId);
}
+ /**
+ * Whether the passed userId has enrolled SFPS.
+ */
+ public boolean isSfpsEnrolled(int userId) {
+ if (mSidefpsController == null) {
+ return false;
+ }
+
+ return mSfpsEnrolledForUser.get(userId);
+ }
+
private void showDialog(SomeArgs args, boolean skipAnimation, Bundle savedState) {
mCurrentDialogArgs = args;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
index f9e44a0c1724..85cb39849f1f 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialPatternView.java
@@ -20,6 +20,7 @@ import android.annotation.NonNull;
import android.content.Context;
import android.util.AttributeSet;
+import com.android.internal.annotations.VisibleForTesting;
import com.android.internal.widget.LockPatternChecker;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.LockPatternView;
@@ -34,7 +35,7 @@ import java.util.List;
*/
public class AuthCredentialPatternView extends AuthCredentialView {
- private LockPatternView mLockPatternView;
+ @VisibleForTesting LockPatternView mLockPatternView;
private class UnlockPatternListener implements LockPatternView.OnPatternListener {
@@ -93,9 +94,7 @@ public class AuthCredentialPatternView extends AuthCredentialView {
@Override
protected void onErrorTimeoutFinish() {
super.onErrorTimeoutFinish();
- // select to enable marquee unless a screen reader is enabled
- mLockPatternView.setEnabled(!mAccessibilityManager.isEnabled()
- || !mAccessibilityManager.isTouchExplorationEnabled());
+ mLockPatternView.setEnabled(true);
}
public AuthCredentialPatternView(Context context, AttributeSet attrs) {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
index 5958e6a436f1..157f14fb4c51 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/AuthCredentialView.java
@@ -47,6 +47,7 @@ import android.widget.LinearLayout;
import android.widget.TextView;
import androidx.annotation.StringRes;
+import androidx.annotation.VisibleForTesting;
import com.android.internal.widget.LockPatternUtils;
import com.android.internal.widget.VerifyCredentialResponse;
@@ -98,7 +99,7 @@ public abstract class AuthCredentialView extends LinearLayout {
protected int mUserId;
protected long mOperationId;
protected int mEffectiveUserId;
- protected ErrorTimer mErrorTimer;
+ @VisibleForTesting ErrorTimer mErrorTimer;
protected @Background DelayableExecutor mBackgroundExecutor;
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
index 96fe65f8fd40..65fcd760360c 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsController.java
@@ -60,7 +60,9 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.doze.DozeReceiver;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -119,6 +121,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final SystemUIDialogManager mDialogManager;
@NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@NonNull private final VibratorHelper mVibrator;
+ @NonNull private final FeatureFlags mFeatureFlags;
@NonNull private final FalsingManager mFalsingManager;
@NonNull private final PowerManager mPowerManager;
@NonNull private final AccessibilityManager mAccessibilityManager;
@@ -130,6 +133,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull private final LatencyTracker mLatencyTracker;
@VisibleForTesting @NonNull final BiometricDisplayListener mOrientationListener;
@NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
+ @NonNull private final BouncerInteractor mBouncerInteractor;
// Currently the UdfpsController supports a single UDFPS sensor. If devices have multiple
// sensors, this, in addition to a lot of the code here, will be updated.
@@ -212,7 +216,8 @@ public class UdfpsController implements DozeReceiver {
mUnlockedScreenOffAnimationController,
mUdfpsDisplayMode, requestId, reason, callback,
(view, event, fromUdfpsView) -> onTouch(requestId, event,
- fromUdfpsView), mActivityLaunchAnimator)));
+ fromUdfpsView), mActivityLaunchAnimator, mFeatureFlags,
+ mBouncerInteractor)));
}
@Override
@@ -590,6 +595,7 @@ public class UdfpsController implements DozeReceiver {
@NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
@NonNull DumpManager dumpManager,
@NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
+ @NonNull FeatureFlags featureFlags,
@NonNull FalsingManager falsingManager,
@NonNull PowerManager powerManager,
@NonNull AccessibilityManager accessibilityManager,
@@ -608,7 +614,8 @@ public class UdfpsController implements DozeReceiver {
@NonNull LatencyTracker latencyTracker,
@NonNull ActivityLaunchAnimator activityLaunchAnimator,
@NonNull Optional<AlternateUdfpsTouchProvider> alternateTouchProvider,
- @BiometricsBackground Executor biometricsExecutor) {
+ @BiometricsBackground Executor biometricsExecutor,
+ @NonNull BouncerInteractor bouncerInteractor) {
mContext = context;
mExecution = execution;
mVibrator = vibrator;
@@ -638,6 +645,8 @@ public class UdfpsController implements DozeReceiver {
mActivityLaunchAnimator = activityLaunchAnimator;
mAlternateTouchProvider = alternateTouchProvider.orElse(null);
mBiometricExecutor = biometricsExecutor;
+ mFeatureFlags = featureFlags;
+ mBouncerInteractor = bouncerInteractor;
mOrientationListener = new BiometricDisplayListener(
context,
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
index 7d0109686351..d70861ac5f19 100644
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsControllerOverlay.kt
@@ -48,6 +48,8 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.systemui.R
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -70,29 +72,31 @@ const val SETTING_REMOVE_ENROLLMENT_UI = "udfps_overlay_remove_enrollment_ui"
*/
@UiThread
class UdfpsControllerOverlay @JvmOverloads constructor(
- private val context: Context,
- fingerprintManager: FingerprintManager,
- private val inflater: LayoutInflater,
- private val windowManager: WindowManager,
- private val accessibilityManager: AccessibilityManager,
- private val statusBarStateController: StatusBarStateController,
- private val shadeExpansionStateManager: ShadeExpansionStateManager,
- private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
- private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
- private val dialogManager: SystemUIDialogManager,
- private val dumpManager: DumpManager,
- private val transitionController: LockscreenShadeTransitionController,
- private val configurationController: ConfigurationController,
- private val systemClock: SystemClock,
- private val keyguardStateController: KeyguardStateController,
- private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
- private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
- val requestId: Long,
- @ShowReason val requestReason: Int,
- private val controllerCallback: IUdfpsOverlayControllerCallback,
- private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
- private val activityLaunchAnimator: ActivityLaunchAnimator,
- private val isDebuggable: Boolean = Build.IS_DEBUGGABLE
+ private val context: Context,
+ fingerprintManager: FingerprintManager,
+ private val inflater: LayoutInflater,
+ private val windowManager: WindowManager,
+ private val accessibilityManager: AccessibilityManager,
+ private val statusBarStateController: StatusBarStateController,
+ private val shadeExpansionStateManager: ShadeExpansionStateManager,
+ private val statusBarKeyguardViewManager: StatusBarKeyguardViewManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ private val dialogManager: SystemUIDialogManager,
+ private val dumpManager: DumpManager,
+ private val transitionController: LockscreenShadeTransitionController,
+ private val configurationController: ConfigurationController,
+ private val systemClock: SystemClock,
+ private val keyguardStateController: KeyguardStateController,
+ private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
+ private var udfpsDisplayModeProvider: UdfpsDisplayModeProvider,
+ val requestId: Long,
+ @ShowReason val requestReason: Int,
+ private val controllerCallback: IUdfpsOverlayControllerCallback,
+ private val onTouch: (View, MotionEvent, Boolean) -> Boolean,
+ private val activityLaunchAnimator: ActivityLaunchAnimator,
+ private val featureFlags: FeatureFlags,
+ private val bouncerInteractor: BouncerInteractor,
+ private val isDebuggable: Boolean = Build.IS_DEBUGGABLE
) {
/** The view, when [isShowing], or null. */
var overlayView: UdfpsView? = null
@@ -246,7 +250,9 @@ class UdfpsControllerOverlay @JvmOverloads constructor(
unlockedScreenOffAnimationController,
dialogManager,
controller,
- activityLaunchAnimator
+ activityLaunchAnimator,
+ featureFlags,
+ bouncerInteractor
)
}
REASON_AUTH_BP -> {
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
deleted file mode 100644
index 4d7f89d7b727..000000000000
--- a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.java
+++ /dev/null
@@ -1,548 +0,0 @@
-/*
- * Copyright (C) 2021 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.android.systemui.biometrics;
-
-import static com.android.systemui.statusbar.StatusBarState.KEYGUARD;
-
-import android.animation.ValueAnimator;
-import android.annotation.NonNull;
-import android.content.res.Configuration;
-import android.util.MathUtils;
-import android.view.MotionEvent;
-
-import com.android.keyguard.BouncerPanelExpansionCalculator;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.R;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.animation.Interpolators;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionChangeEvent;
-import com.android.systemui.shade.ShadeExpansionListener;
-import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
-import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.notification.stack.StackStateAnimator;
-import com.android.systemui.statusbar.phone.KeyguardBouncer;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.time.SystemClock;
-
-import java.io.PrintWriter;
-
-/**
- * Class that coordinates non-HBM animations during keyguard authentication.
- */
-public class UdfpsKeyguardViewController extends UdfpsAnimationViewController<UdfpsKeyguardView> {
- public static final String TAG = "UdfpsKeyguardViewCtrl";
- @NonNull private final StatusBarKeyguardViewManager mKeyguardViewManager;
- @NonNull private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @NonNull private final LockscreenShadeTransitionController mLockScreenShadeTransitionController;
- @NonNull private final ConfigurationController mConfigurationController;
- @NonNull private final SystemClock mSystemClock;
- @NonNull private final KeyguardStateController mKeyguardStateController;
- @NonNull private final UdfpsController mUdfpsController;
- @NonNull private final UnlockedScreenOffAnimationController
- mUnlockedScreenOffAnimationController;
- @NonNull private final ActivityLaunchAnimator mActivityLaunchAnimator;
- private final ValueAnimator mUnlockedScreenOffDozeAnimator = ValueAnimator.ofFloat(0f, 1f);
-
- private boolean mShowingUdfpsBouncer;
- private boolean mUdfpsRequested;
- private float mQsExpansion;
- private boolean mFaceDetectRunning;
- private int mStatusBarState;
- private float mTransitionToFullShadeProgress;
- private float mLastDozeAmount;
- private long mLastUdfpsBouncerShowTime = -1;
- private float mPanelExpansionFraction;
- private boolean mLaunchTransitionFadingAway;
- private boolean mIsLaunchingActivity;
- private float mActivityLaunchProgress;
-
- /**
- * hidden amount of pin/pattern/password bouncer
- * {@link KeyguardBouncer#EXPANSION_VISIBLE} (0f) to
- * {@link KeyguardBouncer#EXPANSION_HIDDEN} (1f)
- */
- private float mInputBouncerHiddenAmount;
- private boolean mIsGenericBouncerShowing; // whether UDFPS bouncer or input bouncer is visible
-
- protected UdfpsKeyguardViewController(
- @NonNull UdfpsKeyguardView view,
- @NonNull StatusBarStateController statusBarStateController,
- @NonNull ShadeExpansionStateManager shadeExpansionStateManager,
- @NonNull StatusBarKeyguardViewManager statusBarKeyguardViewManager,
- @NonNull KeyguardUpdateMonitor keyguardUpdateMonitor,
- @NonNull DumpManager dumpManager,
- @NonNull LockscreenShadeTransitionController transitionController,
- @NonNull ConfigurationController configurationController,
- @NonNull SystemClock systemClock,
- @NonNull KeyguardStateController keyguardStateController,
- @NonNull UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
- @NonNull SystemUIDialogManager systemUIDialogManager,
- @NonNull UdfpsController udfpsController,
- @NonNull ActivityLaunchAnimator activityLaunchAnimator) {
- super(view, statusBarStateController, shadeExpansionStateManager, systemUIDialogManager,
- dumpManager);
- mKeyguardViewManager = statusBarKeyguardViewManager;
- mKeyguardUpdateMonitor = keyguardUpdateMonitor;
- mLockScreenShadeTransitionController = transitionController;
- mConfigurationController = configurationController;
- mSystemClock = systemClock;
- mKeyguardStateController = keyguardStateController;
- mUdfpsController = udfpsController;
- mUnlockedScreenOffAnimationController = unlockedScreenOffAnimationController;
- mActivityLaunchAnimator = activityLaunchAnimator;
-
- mUnlockedScreenOffDozeAnimator.setDuration(StackStateAnimator.ANIMATION_DURATION_STANDARD);
- mUnlockedScreenOffDozeAnimator.setInterpolator(Interpolators.ALPHA_IN);
- mUnlockedScreenOffDozeAnimator.addUpdateListener(
- new ValueAnimator.AnimatorUpdateListener() {
- @Override
- public void onAnimationUpdate(ValueAnimator animation) {
- mView.onDozeAmountChanged(
- animation.getAnimatedFraction(),
- (float) animation.getAnimatedValue(),
- UdfpsKeyguardView.ANIMATION_UNLOCKED_SCREEN_OFF);
- }
- });
- }
-
- @Override
- @NonNull protected String getTag() {
- return "UdfpsKeyguardViewController";
- }
-
- @Override
- public void onInit() {
- super.onInit();
- mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- }
-
- @Override
- protected void onViewAttached() {
- super.onViewAttached();
- final float dozeAmount = getStatusBarStateController().getDozeAmount();
- mLastDozeAmount = dozeAmount;
- mStateListener.onDozeAmountChanged(dozeAmount, dozeAmount);
- getStatusBarStateController().addCallback(mStateListener);
-
- mUdfpsRequested = false;
-
- mLaunchTransitionFadingAway = mKeyguardStateController.isLaunchTransitionFadingAway();
- mKeyguardStateController.addCallback(mKeyguardStateControllerCallback);
- mStatusBarState = getStatusBarStateController().getState();
- mQsExpansion = mKeyguardViewManager.getQsExpansion();
- updateGenericBouncerVisibility();
- mConfigurationController.addCallback(mConfigurationListener);
- getShadeExpansionStateManager().addExpansionListener(mShadeExpansionListener);
- updateScaleFactor();
- mView.updatePadding();
- updateAlpha();
- updatePauseAuth();
-
- mKeyguardViewManager.setAlternateAuthInterceptor(mAlternateAuthInterceptor);
- mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(this);
- mActivityLaunchAnimator.addListener(mActivityLaunchAnimatorListener);
- }
-
- @Override
- protected void onViewDetached() {
- super.onViewDetached();
- mFaceDetectRunning = false;
-
- mKeyguardStateController.removeCallback(mKeyguardStateControllerCallback);
- getStatusBarStateController().removeCallback(mStateListener);
- mKeyguardViewManager.removeAlternateAuthInterceptor(mAlternateAuthInterceptor);
- mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
- mConfigurationController.removeCallback(mConfigurationListener);
- getShadeExpansionStateManager().removeExpansionListener(mShadeExpansionListener);
- if (mLockScreenShadeTransitionController.getUdfpsKeyguardViewController() == this) {
- mLockScreenShadeTransitionController.setUdfpsKeyguardViewController(null);
- }
- mActivityLaunchAnimator.removeListener(mActivityLaunchAnimatorListener);
- }
-
- @Override
- public void dump(PrintWriter pw, String[] args) {
- super.dump(pw, args);
- pw.println("mShowingUdfpsBouncer=" + mShowingUdfpsBouncer);
- pw.println("mFaceDetectRunning=" + mFaceDetectRunning);
- pw.println("mStatusBarState=" + StatusBarState.toString(mStatusBarState));
- pw.println("mTransitionToFullShadeProgress=" + mTransitionToFullShadeProgress);
- pw.println("mQsExpansion=" + mQsExpansion);
- pw.println("mIsGenericBouncerShowing=" + mIsGenericBouncerShowing);
- pw.println("mInputBouncerHiddenAmount=" + mInputBouncerHiddenAmount);
- pw.println("mPanelExpansionFraction=" + mPanelExpansionFraction);
- pw.println("unpausedAlpha=" + mView.getUnpausedAlpha());
- pw.println("mUdfpsRequested=" + mUdfpsRequested);
- pw.println("mLaunchTransitionFadingAway=" + mLaunchTransitionFadingAway);
- pw.println("mLastDozeAmount=" + mLastDozeAmount);
-
- mView.dump(pw);
- }
-
- /**
- * Overrides non-bouncer show logic in shouldPauseAuth to still show icon.
- * @return whether the udfpsBouncer has been newly shown or hidden
- */
- private boolean showUdfpsBouncer(boolean show) {
- if (mShowingUdfpsBouncer == show) {
- return false;
- }
-
- boolean udfpsAffordanceWasNotShowing = shouldPauseAuth();
- mShowingUdfpsBouncer = show;
- if (mShowingUdfpsBouncer) {
- mLastUdfpsBouncerShowTime = mSystemClock.uptimeMillis();
- }
- if (mShowingUdfpsBouncer) {
- if (udfpsAffordanceWasNotShowing) {
- mView.animateInUdfpsBouncer(null);
- }
-
- if (mKeyguardStateController.isOccluded()) {
- mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true);
- }
-
- mView.announceForAccessibility(mView.getContext().getString(
- R.string.accessibility_fingerprint_bouncer));
- } else {
- mKeyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false);
- }
-
- updateGenericBouncerVisibility();
- updateAlpha();
- updatePauseAuth();
- return true;
- }
-
- /**
- * Returns true if the fingerprint manager is running but we want to temporarily pause
- * authentication. On the keyguard, we may want to show udfps when the shade
- * is expanded, so this can be overridden with the showBouncer method.
- */
- public boolean shouldPauseAuth() {
- if (mShowingUdfpsBouncer) {
- return false;
- }
-
- if (mUdfpsRequested && !getNotificationShadeVisible()
- && (!mIsGenericBouncerShowing
- || mInputBouncerHiddenAmount != KeyguardBouncer.EXPANSION_VISIBLE)
- && mKeyguardStateController.isShowing()) {
- return false;
- }
-
- if (mLaunchTransitionFadingAway) {
- return true;
- }
-
- // Only pause auth if we're not on the keyguard AND we're not transitioning to doze
- // (ie: dozeAmount = 0f). For the UnlockedScreenOffAnimation, the statusBarState is
- // delayed. However, we still animate in the UDFPS affordance with the
- // mUnlockedScreenOffDozeAnimator.
- if (mStatusBarState != KEYGUARD && mLastDozeAmount == 0f) {
- return true;
- }
-
- if (mInputBouncerHiddenAmount < .5f) {
- return true;
- }
-
- if (mView.getUnpausedAlpha() < (255 * .1)) {
- return true;
- }
-
- return false;
- }
-
- @Override
- public boolean listenForTouchesOutsideView() {
- return true;
- }
-
- @Override
- public void onTouchOutsideView() {
- maybeShowInputBouncer();
- }
-
- /**
- * If we were previously showing the udfps bouncer, hide it and instead show the regular
- * (pin/pattern/password) bouncer.
- *
- * Does nothing if we weren't previously showing the UDFPS bouncer.
- */
- private void maybeShowInputBouncer() {
- if (mShowingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
- mKeyguardViewManager.showBouncer(true);
- }
- }
-
- /**
- * Whether the udfps bouncer has shown for at least 200ms before allowing touches outside
- * of the udfps icon area to dismiss the udfps bouncer and show the pin/pattern/password
- * bouncer.
- */
- private boolean hasUdfpsBouncerShownWithMinTime() {
- return (mSystemClock.uptimeMillis() - mLastUdfpsBouncerShowTime) > 200;
- }
-
- /**
- * Set the progress we're currently transitioning to the full shade. 0.0f means we're not
- * transitioning yet, while 1.0f means we've fully dragged down.
- *
- * For example, start swiping down to expand the notification shade from the empty space in
- * the middle of the lock screen.
- */
- public void setTransitionToFullShadeProgress(float progress) {
- mTransitionToFullShadeProgress = progress;
- updateAlpha();
- }
-
- /**
- * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's
- * alpha is based on the doze amount.
- */
- @Override
- public void updateAlpha() {
- // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested,
- // then the keyguard is occluded by some application - so instead use the input bouncer
- // hidden amount to determine the fade.
- float expansion = mUdfpsRequested ? mInputBouncerHiddenAmount : mPanelExpansionFraction;
-
- int alpha = mShowingUdfpsBouncer ? 255
- : (int) MathUtils.constrain(
- MathUtils.map(.5f, .9f, 0f, 255f, expansion),
- 0f, 255f);
-
- if (!mShowingUdfpsBouncer) {
- // swipe from top of the lockscreen to expand full QS:
- alpha *= (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(mQsExpansion));
-
- // swipe from the middle (empty space) of lockscreen to expand the notification shade:
- alpha *= (1.0f - mTransitionToFullShadeProgress);
-
- // Fade out the icon if we are animating an activity launch over the lockscreen and the
- // activity didn't request the UDFPS.
- if (mIsLaunchingActivity && !mUdfpsRequested) {
- alpha *= (1.0f - mActivityLaunchProgress);
- }
-
- // Fade out alpha when a dialog is shown
- // Fade in alpha when a dialog is hidden
- alpha *= mView.getDialogSuggestedAlpha();
- }
- mView.setUnpausedAlpha(alpha);
- }
-
- /**
- * Updates mIsGenericBouncerShowing (whether any bouncer is showing) and updates the
- * mInputBouncerHiddenAmount to reflect whether the input bouncer is fully showing or not.
- */
- private void updateGenericBouncerVisibility() {
- mIsGenericBouncerShowing = mKeyguardViewManager.isBouncerShowing(); // includes altBouncer
- final boolean altBouncerShowing = mKeyguardViewManager.isShowingAlternateAuth();
- if (altBouncerShowing || !mKeyguardViewManager.bouncerIsOrWillBeShowing()) {
- mInputBouncerHiddenAmount = 1f;
- } else if (mIsGenericBouncerShowing) {
- // input bouncer is fully showing
- mInputBouncerHiddenAmount = 0f;
- }
- }
-
- /**
- * Update the scale factor based on the device's resolution.
- */
- private void updateScaleFactor() {
- if (mUdfpsController != null && mUdfpsController.mOverlayParams != null) {
- mView.setScaleFactor(mUdfpsController.mOverlayParams.getScaleFactor());
- }
- }
-
- private final StatusBarStateController.StateListener mStateListener =
- new StatusBarStateController.StateListener() {
- @Override
- public void onDozeAmountChanged(float linear, float eased) {
- if (mLastDozeAmount < linear) {
- showUdfpsBouncer(false);
- }
- mUnlockedScreenOffDozeAnimator.cancel();
- final boolean animatingFromUnlockedScreenOff =
- mUnlockedScreenOffAnimationController.isAnimationPlaying();
- if (animatingFromUnlockedScreenOff && linear != 0f) {
- // we manually animate the fade in of the UDFPS icon since the unlocked
- // screen off animation prevents the doze amounts to be incrementally eased in
- mUnlockedScreenOffDozeAnimator.start();
- } else {
- mView.onDozeAmountChanged(linear, eased,
- UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN);
- }
-
- mLastDozeAmount = linear;
- updatePauseAuth();
- }
-
- @Override
- public void onStateChanged(int statusBarState) {
- mStatusBarState = statusBarState;
- updateAlpha();
- updatePauseAuth();
- }
- };
-
- private final StatusBarKeyguardViewManager.AlternateAuthInterceptor mAlternateAuthInterceptor =
- new StatusBarKeyguardViewManager.AlternateAuthInterceptor() {
- @Override
- public boolean showAlternateAuthBouncer() {
- return showUdfpsBouncer(true);
- }
-
- @Override
- public boolean hideAlternateAuthBouncer() {
- return showUdfpsBouncer(false);
- }
-
- @Override
- public boolean isShowingAlternateAuthBouncer() {
- return mShowingUdfpsBouncer;
- }
-
- @Override
- public void requestUdfps(boolean request, int color) {
- mUdfpsRequested = request;
- mView.requestUdfps(request, color);
- updateAlpha();
- updatePauseAuth();
- }
-
- @Override
- public boolean isAnimating() {
- return false;
- }
-
- /**
- * Set the amount qs is expanded. Forxample, swipe down from the top of the
- * lock screen to start the full QS expansion.
- */
- @Override
- public void setQsExpansion(float qsExpansion) {
- mQsExpansion = qsExpansion;
- updateAlpha();
- updatePauseAuth();
- }
-
- @Override
- public boolean onTouch(MotionEvent event) {
- if (mTransitionToFullShadeProgress != 0) {
- return false;
- }
- return mUdfpsController.onTouch(event);
- }
-
- @Override
- public void setBouncerExpansionChanged(float expansion) {
- mInputBouncerHiddenAmount = expansion;
- updateAlpha();
- updatePauseAuth();
- }
-
- /**
- * Only called on primary auth bouncer changes, not on whether the UDFPS bouncer
- * visibility changes.
- */
- @Override
- public void onBouncerVisibilityChanged() {
- updateGenericBouncerVisibility();
- updateAlpha();
- updatePauseAuth();
- }
-
- @Override
- public void dump(PrintWriter pw) {
- pw.println(getTag());
- }
- };
-
- private final ConfigurationController.ConfigurationListener mConfigurationListener =
- new ConfigurationController.ConfigurationListener() {
- @Override
- public void onUiModeChanged() {
- mView.updateColor();
- }
-
- @Override
- public void onThemeChanged() {
- mView.updateColor();
- }
-
- @Override
- public void onConfigChanged(Configuration newConfig) {
- updateScaleFactor();
- mView.updatePadding();
- mView.updateColor();
- }
- };
-
- private final ShadeExpansionListener mShadeExpansionListener = new ShadeExpansionListener() {
- @Override
- public void onPanelExpansionChanged(ShadeExpansionChangeEvent event) {
- float fraction = event.getFraction();
- mPanelExpansionFraction =
- mKeyguardViewManager.isBouncerInTransit() ? BouncerPanelExpansionCalculator
- .aboutToShowBouncerProgress(fraction) : fraction;
- updateAlpha();
- updatePauseAuth();
- }
- };
-
- private final KeyguardStateController.Callback mKeyguardStateControllerCallback =
- new KeyguardStateController.Callback() {
- @Override
- public void onLaunchTransitionFadingAwayChanged() {
- mLaunchTransitionFadingAway =
- mKeyguardStateController.isLaunchTransitionFadingAway();
- updatePauseAuth();
- }
- };
-
- private final ActivityLaunchAnimator.Listener mActivityLaunchAnimatorListener =
- new ActivityLaunchAnimator.Listener() {
- @Override
- public void onLaunchAnimationStart() {
- mIsLaunchingActivity = true;
- mActivityLaunchProgress = 0f;
- updateAlpha();
- }
-
- @Override
- public void onLaunchAnimationEnd() {
- mIsLaunchingActivity = false;
- updateAlpha();
- }
-
- @Override
- public void onLaunchAnimationProgress(float linearProgress) {
- mActivityLaunchProgress = linearProgress;
- updateAlpha();
- }
- };
-}
diff --git a/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
new file mode 100644
index 000000000000..5bae2dc502d6
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/biometrics/UdfpsKeyguardViewController.kt
@@ -0,0 +1,550 @@
+/*
+ * Copyright (C) 2021 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.animation.ValueAnimator
+import android.content.res.Configuration
+import android.util.MathUtils
+import android.view.MotionEvent
+import androidx.annotation.VisibleForTesting
+import androidx.lifecycle.Lifecycle
+import androidx.lifecycle.repeatOnLifecycle
+import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
+import com.android.keyguard.KeyguardUpdateMonitor
+import com.android.systemui.R
+import com.android.systemui.animation.ActivityLaunchAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.flags.Flags
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.shade.ShadeExpansionListener
+import com.android.systemui.shade.ShadeExpansionStateManager
+import com.android.systemui.statusbar.LockscreenShadeTransitionController
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.notification.stack.StackStateAnimator
+import com.android.systemui.statusbar.phone.KeyguardBouncer
+import com.android.systemui.statusbar.phone.KeyguardBouncer.BouncerExpansionCallback
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.AlternateAuthInterceptor
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager.KeyguardViewManagerCallback
+import com.android.systemui.statusbar.phone.SystemUIDialogManager
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController
+import com.android.systemui.statusbar.policy.ConfigurationController
+import com.android.systemui.statusbar.policy.KeyguardStateController
+import com.android.systemui.util.time.SystemClock
+import java.io.PrintWriter
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.Job
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+/** Class that coordinates non-HBM animations during keyguard authentication. */
+open class UdfpsKeyguardViewController
+constructor(
+ private val view: UdfpsKeyguardView,
+ statusBarStateController: StatusBarStateController,
+ shadeExpansionStateManager: ShadeExpansionStateManager,
+ private val keyguardViewManager: StatusBarKeyguardViewManager,
+ private val keyguardUpdateMonitor: KeyguardUpdateMonitor,
+ dumpManager: DumpManager,
+ private val lockScreenShadeTransitionController: LockscreenShadeTransitionController,
+ private val configurationController: ConfigurationController,
+ private val systemClock: SystemClock,
+ private val keyguardStateController: KeyguardStateController,
+ private val unlockedScreenOffAnimationController: UnlockedScreenOffAnimationController,
+ systemUIDialogManager: SystemUIDialogManager,
+ private val udfpsController: UdfpsController,
+ private val activityLaunchAnimator: ActivityLaunchAnimator,
+ featureFlags: FeatureFlags,
+ private val bouncerInteractor: BouncerInteractor
+) :
+ UdfpsAnimationViewController<UdfpsKeyguardView>(
+ view,
+ statusBarStateController,
+ shadeExpansionStateManager,
+ systemUIDialogManager,
+ dumpManager
+ ) {
+ private val isModernBouncerEnabled: Boolean = featureFlags.isEnabled(Flags.MODERN_BOUNCER)
+ private var showingUdfpsBouncer = false
+ private var udfpsRequested = false
+ private var qsExpansion = 0f
+ private var faceDetectRunning = false
+ private var statusBarState = 0
+ private var transitionToFullShadeProgress = 0f
+ private var lastDozeAmount = 0f
+ private var lastUdfpsBouncerShowTime: Long = -1
+ private var panelExpansionFraction = 0f
+ private var launchTransitionFadingAway = false
+ private var isLaunchingActivity = false
+ private var activityLaunchProgress = 0f
+ private val unlockedScreenOffDozeAnimator =
+ ValueAnimator.ofFloat(0f, 1f).apply {
+ duration = StackStateAnimator.ANIMATION_DURATION_STANDARD.toLong()
+ interpolator = Interpolators.ALPHA_IN
+ addUpdateListener { animation ->
+ view.onDozeAmountChanged(
+ animation.animatedFraction,
+ animation.animatedValue as Float,
+ UdfpsKeyguardView.ANIMATION_UNLOCKED_SCREEN_OFF
+ )
+ }
+ }
+ /**
+ * Hidden amount of input (pin/pattern/password) bouncer. This is used
+ * [KeyguardBouncer.EXPANSION_VISIBLE] (0f) to [KeyguardBouncer.EXPANSION_HIDDEN] (1f). Only
+ * used for the non-modernBouncer.
+ */
+ private var inputBouncerHiddenAmount = KeyguardBouncer.EXPANSION_HIDDEN
+ private var inputBouncerExpansion = 0f // only used for modernBouncer
+
+ private val stateListener: StatusBarStateController.StateListener =
+ object : StatusBarStateController.StateListener {
+ override fun onDozeAmountChanged(linear: Float, eased: Float) {
+ if (lastDozeAmount < linear) {
+ showUdfpsBouncer(false)
+ }
+ unlockedScreenOffDozeAnimator.cancel()
+ val animatingFromUnlockedScreenOff =
+ unlockedScreenOffAnimationController.isAnimationPlaying()
+ if (animatingFromUnlockedScreenOff && linear != 0f) {
+ // we manually animate the fade in of the UDFPS icon since the unlocked
+ // screen off animation prevents the doze amounts to be incrementally eased in
+ unlockedScreenOffDozeAnimator.start()
+ } else {
+ view.onDozeAmountChanged(
+ linear,
+ eased,
+ UdfpsKeyguardView.ANIMATION_BETWEEN_AOD_AND_LOCKSCREEN
+ )
+ }
+ lastDozeAmount = linear
+ updatePauseAuth()
+ }
+
+ override fun onStateChanged(statusBarState: Int) {
+ this@UdfpsKeyguardViewController.statusBarState = statusBarState
+ updateAlpha()
+ updatePauseAuth()
+ }
+ }
+
+ private val bouncerExpansionCallback: BouncerExpansionCallback =
+ object : BouncerExpansionCallback {
+ override fun onExpansionChanged(expansion: Float) {
+ inputBouncerHiddenAmount = expansion
+ updateAlpha()
+ updatePauseAuth()
+ }
+
+ override fun onVisibilityChanged(isVisible: Boolean) {
+ updateBouncerHiddenAmount()
+ updateAlpha()
+ updatePauseAuth()
+ }
+ }
+
+ private val configurationListener: ConfigurationController.ConfigurationListener =
+ object : ConfigurationController.ConfigurationListener {
+ override fun onUiModeChanged() {
+ view.updateColor()
+ }
+
+ override fun onThemeChanged() {
+ view.updateColor()
+ }
+
+ override fun onConfigChanged(newConfig: Configuration) {
+ updateScaleFactor()
+ view.updatePadding()
+ view.updateColor()
+ }
+ }
+
+ private val shadeExpansionListener = ShadeExpansionListener { (fraction) ->
+ panelExpansionFraction =
+ if (keyguardViewManager.isBouncerInTransit) {
+ aboutToShowBouncerProgress(fraction)
+ } else {
+ fraction
+ }
+ updateAlpha()
+ updatePauseAuth()
+ }
+
+ private val keyguardStateControllerCallback: KeyguardStateController.Callback =
+ object : KeyguardStateController.Callback {
+ override fun onLaunchTransitionFadingAwayChanged() {
+ launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
+ updatePauseAuth()
+ }
+ }
+
+ private val activityLaunchAnimatorListener: ActivityLaunchAnimator.Listener =
+ object : ActivityLaunchAnimator.Listener {
+ override fun onLaunchAnimationStart() {
+ isLaunchingActivity = true
+ activityLaunchProgress = 0f
+ updateAlpha()
+ }
+
+ override fun onLaunchAnimationEnd() {
+ isLaunchingActivity = false
+ updateAlpha()
+ }
+
+ override fun onLaunchAnimationProgress(linearProgress: Float) {
+ activityLaunchProgress = linearProgress
+ updateAlpha()
+ }
+ }
+
+ private val statusBarKeyguardViewManagerCallback: KeyguardViewManagerCallback =
+ object : KeyguardViewManagerCallback {
+ override fun onQSExpansionChanged(qsExpansion: Float) {
+ this@UdfpsKeyguardViewController.qsExpansion = qsExpansion
+ updateAlpha()
+ updatePauseAuth()
+ }
+
+ /**
+ * Forward touches to the UdfpsController. This allows the touch to start from outside
+ * the sensor area and then slide their finger into the sensor area.
+ */
+ override fun onTouch(event: MotionEvent) {
+ // Don't forward touches if the shade has already started expanding.
+ if (transitionToFullShadeProgress != 0f) {
+ return
+ }
+ udfpsController.onTouch(event)
+ }
+ }
+
+ private val alternateAuthInterceptor: AlternateAuthInterceptor =
+ object : AlternateAuthInterceptor {
+ override fun showAlternateAuthBouncer(): Boolean {
+ return showUdfpsBouncer(true)
+ }
+
+ override fun hideAlternateAuthBouncer(): Boolean {
+ return showUdfpsBouncer(false)
+ }
+
+ override fun isShowingAlternateAuthBouncer(): Boolean {
+ return showingUdfpsBouncer
+ }
+
+ override fun requestUdfps(request: Boolean, color: Int) {
+ udfpsRequested = request
+ view.requestUdfps(request, color)
+ updateAlpha()
+ updatePauseAuth()
+ }
+
+ override fun dump(pw: PrintWriter) {
+ pw.println(tag)
+ }
+ }
+
+ override val tag: String
+ get() = TAG
+
+ override fun onInit() {
+ super.onInit()
+ keyguardViewManager.setAlternateAuthInterceptor(alternateAuthInterceptor)
+ }
+
+ init {
+ if (isModernBouncerEnabled) {
+ view.repeatWhenAttached {
+ // repeatOnLifecycle CREATED (as opposed to STARTED) because the Bouncer expansion
+ // can make the view not visible; and we still want to listen for events
+ // that may make the view visible again.
+ repeatOnLifecycle(Lifecycle.State.CREATED) { listenForBouncerExpansion(this) }
+ }
+ }
+ }
+
+ @VisibleForTesting
+ internal suspend fun listenForBouncerExpansion(scope: CoroutineScope): Job {
+ return scope.launch {
+ bouncerInteractor.bouncerExpansion.collect { bouncerExpansion: Float ->
+ inputBouncerExpansion = bouncerExpansion
+ updateAlpha()
+ updatePauseAuth()
+ }
+ }
+ }
+
+ public override fun onViewAttached() {
+ super.onViewAttached()
+ val dozeAmount = statusBarStateController.dozeAmount
+ lastDozeAmount = dozeAmount
+ stateListener.onDozeAmountChanged(dozeAmount, dozeAmount)
+ statusBarStateController.addCallback(stateListener)
+ udfpsRequested = false
+ launchTransitionFadingAway = keyguardStateController.isLaunchTransitionFadingAway
+ keyguardStateController.addCallback(keyguardStateControllerCallback)
+ statusBarState = statusBarStateController.state
+ qsExpansion = keyguardViewManager.qsExpansion
+ keyguardViewManager.addCallback(statusBarKeyguardViewManagerCallback)
+ if (!isModernBouncerEnabled) {
+ val bouncer = keyguardViewManager.bouncer
+ bouncer?.expansion?.let {
+ bouncerExpansionCallback.onExpansionChanged(it)
+ bouncer.addBouncerExpansionCallback(bouncerExpansionCallback)
+ }
+ updateBouncerHiddenAmount()
+ }
+ configurationController.addCallback(configurationListener)
+ shadeExpansionStateManager.addExpansionListener(shadeExpansionListener)
+ updateScaleFactor()
+ view.updatePadding()
+ updateAlpha()
+ updatePauseAuth()
+ keyguardViewManager.setAlternateAuthInterceptor(alternateAuthInterceptor)
+ lockScreenShadeTransitionController.udfpsKeyguardViewController = this
+ activityLaunchAnimator.addListener(activityLaunchAnimatorListener)
+ }
+
+ override fun onViewDetached() {
+ super.onViewDetached()
+ faceDetectRunning = false
+ keyguardStateController.removeCallback(keyguardStateControllerCallback)
+ statusBarStateController.removeCallback(stateListener)
+ keyguardViewManager.removeAlternateAuthInterceptor(alternateAuthInterceptor)
+ keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false)
+ configurationController.removeCallback(configurationListener)
+ shadeExpansionStateManager.removeExpansionListener(shadeExpansionListener)
+ if (lockScreenShadeTransitionController.udfpsKeyguardViewController === this) {
+ lockScreenShadeTransitionController.udfpsKeyguardViewController = null
+ }
+ activityLaunchAnimator.removeListener(activityLaunchAnimatorListener)
+ keyguardViewManager.removeCallback(statusBarKeyguardViewManagerCallback)
+ if (!isModernBouncerEnabled) {
+ keyguardViewManager.bouncer?.removeBouncerExpansionCallback(bouncerExpansionCallback)
+ }
+ }
+
+ override fun dump(pw: PrintWriter, args: Array<String>) {
+ super.dump(pw, args)
+ pw.println("isModernBouncerEnabled=$isModernBouncerEnabled")
+ pw.println("showingUdfpsAltBouncer=$showingUdfpsBouncer")
+ pw.println("faceDetectRunning=$faceDetectRunning")
+ pw.println("statusBarState=" + StatusBarState.toString(statusBarState))
+ pw.println("transitionToFullShadeProgress=$transitionToFullShadeProgress")
+ pw.println("qsExpansion=$qsExpansion")
+ pw.println("panelExpansionFraction=$panelExpansionFraction")
+ pw.println("unpausedAlpha=" + view.unpausedAlpha)
+ pw.println("udfpsRequestedByApp=$udfpsRequested")
+ pw.println("launchTransitionFadingAway=$launchTransitionFadingAway")
+ pw.println("lastDozeAmount=$lastDozeAmount")
+ if (isModernBouncerEnabled) {
+ pw.println("inputBouncerExpansion=$inputBouncerExpansion")
+ } else {
+ pw.println("inputBouncerHiddenAmount=$inputBouncerHiddenAmount")
+ }
+ view.dump(pw)
+ }
+
+ /**
+ * Overrides non-bouncer show logic in shouldPauseAuth to still show icon.
+ * @return whether the udfpsBouncer has been newly shown or hidden
+ */
+ private fun showUdfpsBouncer(show: Boolean): Boolean {
+ if (showingUdfpsBouncer == show) {
+ return false
+ }
+ val udfpsAffordanceWasNotShowing = shouldPauseAuth()
+ showingUdfpsBouncer = show
+ if (showingUdfpsBouncer) {
+ lastUdfpsBouncerShowTime = systemClock.uptimeMillis()
+ }
+ if (showingUdfpsBouncer) {
+ if (udfpsAffordanceWasNotShowing) {
+ view.animateInUdfpsBouncer(null)
+ }
+ if (keyguardStateController.isOccluded) {
+ keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(true)
+ }
+ view.announceForAccessibility(
+ view.context.getString(R.string.accessibility_fingerprint_bouncer)
+ )
+ } else {
+ keyguardUpdateMonitor.requestFaceAuthOnOccludingApp(false)
+ }
+ updateBouncerHiddenAmount()
+ updateAlpha()
+ updatePauseAuth()
+ return true
+ }
+
+ /**
+ * Returns true if the fingerprint manager is running but we want to temporarily pause
+ * authentication. On the keyguard, we may want to show udfps when the shade is expanded, so
+ * this can be overridden with the showBouncer method.
+ */
+ override fun shouldPauseAuth(): Boolean {
+ if (showingUdfpsBouncer) {
+ return false
+ }
+ if (
+ udfpsRequested &&
+ !notificationShadeVisible &&
+ !isInputBouncerFullyVisible() &&
+ keyguardStateController.isShowing
+ ) {
+ return false
+ }
+ if (launchTransitionFadingAway) {
+ return true
+ }
+
+ // Only pause auth if we're not on the keyguard AND we're not transitioning to doze
+ // (ie: dozeAmount = 0f). For the UnlockedScreenOffAnimation, the statusBarState is
+ // delayed. However, we still animate in the UDFPS affordance with the
+ // mUnlockedScreenOffDozeAnimator.
+ if (statusBarState != StatusBarState.KEYGUARD && lastDozeAmount == 0f) {
+ return true
+ }
+ if (isBouncerExpansionGreaterThan(.5f)) {
+ return true
+ }
+ return view.unpausedAlpha < 255 * .1
+ }
+
+ fun isBouncerExpansionGreaterThan(bouncerExpansionThreshold: Float): Boolean {
+ return if (isModernBouncerEnabled) {
+ inputBouncerExpansion >= bouncerExpansionThreshold
+ } else {
+ inputBouncerHiddenAmount < bouncerExpansionThreshold
+ }
+ }
+
+ fun isInputBouncerFullyVisible(): Boolean {
+ return if (isModernBouncerEnabled) {
+ inputBouncerExpansion == 1f
+ } else {
+ keyguardViewManager.isBouncerShowing && !keyguardViewManager.isShowingAlternateAuth
+ }
+ }
+
+ override fun listenForTouchesOutsideView(): Boolean {
+ return true
+ }
+
+ override fun onTouchOutsideView() {
+ maybeShowInputBouncer()
+ }
+
+ /**
+ * If we were previously showing the udfps bouncer, hide it and instead show the regular
+ * (pin/pattern/password) bouncer.
+ *
+ * Does nothing if we weren't previously showing the UDFPS bouncer.
+ */
+ private fun maybeShowInputBouncer() {
+ if (showingUdfpsBouncer && hasUdfpsBouncerShownWithMinTime()) {
+ keyguardViewManager.showBouncer(true)
+ }
+ }
+
+ /**
+ * Whether the udfps bouncer has shown for at least 200ms before allowing touches outside of the
+ * udfps icon area to dismiss the udfps bouncer and show the pin/pattern/password bouncer.
+ */
+ private fun hasUdfpsBouncerShownWithMinTime(): Boolean {
+ return systemClock.uptimeMillis() - lastUdfpsBouncerShowTime > 200
+ }
+
+ /**
+ * Set the progress we're currently transitioning to the full shade. 0.0f means we're not
+ * transitioning yet, while 1.0f means we've fully dragged down. For example, start swiping down
+ * to expand the notification shade from the empty space in the middle of the lock screen.
+ */
+ fun setTransitionToFullShadeProgress(progress: Float) {
+ transitionToFullShadeProgress = progress
+ updateAlpha()
+ }
+
+ /**
+ * Update alpha for the UDFPS lock screen affordance. The AoD UDFPS visual affordance's alpha is
+ * based on the doze amount.
+ */
+ override fun updateAlpha() {
+ // Fade icon on transitions to showing the status bar or bouncer, but if mUdfpsRequested,
+ // then the keyguard is occluded by some application - so instead use the input bouncer
+ // hidden amount to determine the fade.
+ val expansion = if (udfpsRequested) getInputBouncerHiddenAmt() else panelExpansionFraction
+ var alpha: Int =
+ if (showingUdfpsBouncer) 255
+ else MathUtils.constrain(MathUtils.map(.5f, .9f, 0f, 255f, expansion), 0f, 255f).toInt()
+ if (!showingUdfpsBouncer) {
+ // swipe from top of the lockscreen to expand full QS:
+ alpha =
+ (alpha * (1.0f - Interpolators.EMPHASIZED_DECELERATE.getInterpolation(qsExpansion)))
+ .toInt()
+
+ // swipe from the middle (empty space) of lockscreen to expand the notification shade:
+ alpha = (alpha * (1.0f - transitionToFullShadeProgress)).toInt()
+
+ // Fade out the icon if we are animating an activity launch over the lockscreen and the
+ // activity didn't request the UDFPS.
+ if (isLaunchingActivity && !udfpsRequested) {
+ alpha = (alpha * (1.0f - activityLaunchProgress)).toInt()
+ }
+
+ // Fade out alpha when a dialog is shown
+ // Fade in alpha when a dialog is hidden
+ alpha = (alpha * view.dialogSuggestedAlpha).toInt()
+ }
+ view.unpausedAlpha = alpha
+ }
+
+ private fun getInputBouncerHiddenAmt(): Float {
+ return if (isModernBouncerEnabled) {
+ 1f - inputBouncerExpansion
+ } else {
+ inputBouncerHiddenAmount
+ }
+ }
+
+ /** Update the scale factor based on the device's resolution. */
+ private fun updateScaleFactor() {
+ udfpsController.mOverlayParams?.scaleFactor?.let { view.setScaleFactor(it) }
+ }
+
+ private fun updateBouncerHiddenAmount() {
+ if (isModernBouncerEnabled) {
+ return
+ }
+ val altBouncerShowing = keyguardViewManager.isShowingAlternateAuth
+ if (altBouncerShowing || !keyguardViewManager.bouncerIsOrWillBeShowing()) {
+ inputBouncerHiddenAmount = 1f
+ } else if (keyguardViewManager.isBouncerShowing) {
+ // input bouncer is fully showing
+ inputBouncerHiddenAmount = 0f
+ }
+ }
+
+ companion object {
+ const val TAG = "UdfpsKeyguardViewController"
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
index c5d598236c20..28378c93501b 100644
--- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
+++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt
@@ -335,7 +335,7 @@ object Flags {
val CHOOSER_UNBUNDLED = UnreleasedFlag(1500, teamfood = true)
// 1700 - clipboard
- @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700)
+ @JvmField val CLIPBOARD_OVERLAY_REFACTOR = UnreleasedFlag(1700, true)
@JvmField val CLIPBOARD_REMOTE_BEHAVIOR = UnreleasedFlag(1701)
// 1800 - shade container
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
index 99ae85d7a548..80c6130955c5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/BouncerView.kt
@@ -18,6 +18,7 @@ package com.android.systemui.keyguard.data
import android.view.KeyEvent
import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.plugins.ActivityStarter
import java.lang.ref.WeakReference
import javax.inject.Inject
@@ -45,4 +46,9 @@ interface BouncerViewDelegate {
fun dispatchBackKeyEventPreIme(): Boolean
fun showNextSecurityScreenOrFinish(): Boolean
fun resume()
+ fun setDismissAction(
+ onDismissAction: ActivityStarter.OnDismissAction?,
+ cancelAction: Runnable?,
+ )
+ fun willDismissWithActions(): Boolean
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
index 543389e0a7cd..0046256c677b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardBouncerRepository.kt
@@ -21,10 +21,9 @@ import com.android.keyguard.KeyguardUpdateMonitor
import com.android.keyguard.KeyguardUpdateMonitorCallback
import com.android.keyguard.ViewMediatorCallback
import com.android.systemui.dagger.SysUISingleton
-import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
-import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_HIDDEN
+import com.android.systemui.statusbar.phone.KeyguardBouncer
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow
@@ -41,9 +40,15 @@ constructor(
/** Determines if we want to instantaneously show the bouncer instead of translating. */
private val _isScrimmed = MutableStateFlow(false)
val isScrimmed = _isScrimmed.asStateFlow()
- /** Set amount of how much of the bouncer is showing on the screen */
- private val _expansionAmount = MutableStateFlow(EXPANSION_HIDDEN)
- val expansionAmount = _expansionAmount.asStateFlow()
+ /**
+ * Set how much of the panel is showing on the screen.
+ * ```
+ * 0f = panel fully hidden = bouncer fully showing
+ * 1f = panel fully showing = bouncer fully hidden
+ * ```
+ */
+ private val _panelExpansionAmount = MutableStateFlow(KeyguardBouncer.EXPANSION_HIDDEN)
+ val panelExpansionAmount = _panelExpansionAmount.asStateFlow()
private val _isVisible = MutableStateFlow(false)
val isVisible = _isVisible.asStateFlow()
private val _show = MutableStateFlow<KeyguardBouncerModel?>(null)
@@ -54,8 +59,6 @@ constructor(
val hide = _hide.asStateFlow()
private val _startingToHide = MutableStateFlow(false)
val startingToHide = _startingToHide.asStateFlow()
- private val _onDismissAction = MutableStateFlow<BouncerCallbackActionsModel?>(null)
- val onDismissAction = _onDismissAction.asStateFlow()
private val _disappearAnimation = MutableStateFlow<Runnable?>(null)
val startingDisappearAnimation = _disappearAnimation.asStateFlow()
private val _keyguardPosition = MutableStateFlow(0f)
@@ -96,8 +99,8 @@ constructor(
_isScrimmed.value = isScrimmed
}
- fun setExpansion(expansion: Float) {
- _expansionAmount.value = expansion
+ fun setPanelExpansion(panelExpansion: Float) {
+ _panelExpansionAmount.value = panelExpansion
}
fun setVisible(isVisible: Boolean) {
@@ -120,10 +123,6 @@ constructor(
_startingToHide.value = startingToHide
}
- fun setOnDismissAction(bouncerCallbackActionsModel: BouncerCallbackActionsModel?) {
- _onDismissAction.value = bouncerCallbackActionsModel
- }
-
fun setStartDisappearAnimation(runnable: Runnable?) {
_disappearAnimation.value = runnable
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
index 6baaf5f10024..c867c6e9229b 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/data/repository/KeyguardRepository.kt
@@ -23,9 +23,12 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.doze.DozeHost
import com.android.systemui.keyguard.WakefulnessLifecycle
import com.android.systemui.keyguard.WakefulnessLifecycle.Wakefulness
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
+import com.android.systemui.statusbar.phone.BiometricUnlockController.WakeAndUnlockMode
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
import kotlinx.coroutines.channels.awaitClose
@@ -65,6 +68,9 @@ interface KeyguardRepository {
*/
val isKeyguardShowing: Flow<Boolean>
+ /** Observable for whether the bouncer is showing. */
+ val isBouncerShowing: Flow<Boolean>
+
/**
* Observable for whether we are in doze state.
*
@@ -95,6 +101,9 @@ interface KeyguardRepository {
/** Observable for device wake/sleep state */
val wakefulnessState: Flow<WakefulnessModel>
+ /** Observable for biometric unlock modes */
+ val biometricUnlockState: Flow<BiometricUnlockModel>
+
/**
* Returns `true` if the keyguard is showing; `false` otherwise.
*
@@ -125,6 +134,7 @@ constructor(
private val keyguardStateController: KeyguardStateController,
dozeHost: DozeHost,
wakefulnessLifecycle: WakefulnessLifecycle,
+ biometricUnlockController: BiometricUnlockController,
) : KeyguardRepository {
private val _animateBottomAreaDozingTransitions = MutableStateFlow(false)
override val animateBottomAreaDozingTransitions =
@@ -159,6 +169,29 @@ constructor(
awaitClose { keyguardStateController.removeCallback(callback) }
}
+ override val isBouncerShowing: Flow<Boolean> = conflatedCallbackFlow {
+ val callback =
+ object : KeyguardStateController.Callback {
+ override fun onBouncerShowingChanged() {
+ trySendWithFailureLogging(
+ keyguardStateController.isBouncerShowing,
+ TAG,
+ "updated isBouncerShowing"
+ )
+ }
+ }
+
+ keyguardStateController.addCallback(callback)
+ // Adding the callback does not send an initial update.
+ trySendWithFailureLogging(
+ keyguardStateController.isBouncerShowing,
+ TAG,
+ "initial isBouncerShowing"
+ )
+
+ awaitClose { keyguardStateController.removeCallback(callback) }
+ }
+
override val isDozing: Flow<Boolean> =
conflatedCallbackFlow {
val callback =
@@ -248,6 +281,24 @@ constructor(
awaitClose { wakefulnessLifecycle.removeObserver(callback) }
}
+ override val biometricUnlockState: Flow<BiometricUnlockModel> = conflatedCallbackFlow {
+ val callback =
+ object : BiometricUnlockController.BiometricModeListener {
+ override fun onModeChanged(@WakeAndUnlockMode mode: Int) {
+ trySendWithFailureLogging(biometricModeIntToObject(mode), TAG, "biometric mode")
+ }
+ }
+
+ biometricUnlockController.addBiometricModeListener(callback)
+ trySendWithFailureLogging(
+ biometricModeIntToObject(biometricUnlockController.getMode()),
+ TAG,
+ "initial biometric mode"
+ )
+
+ awaitClose { biometricUnlockController.removeBiometricModeListener(callback) }
+ }
+
override fun setAnimateDozingTransitions(animate: Boolean) {
_animateBottomAreaDozingTransitions.value = animate
}
@@ -279,6 +330,20 @@ constructor(
}
}
+ private fun biometricModeIntToObject(@WakeAndUnlockMode value: Int): BiometricUnlockModel {
+ return when (value) {
+ 0 -> BiometricUnlockModel.NONE
+ 1 -> BiometricUnlockModel.WAKE_AND_UNLOCK
+ 2 -> BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+ 3 -> BiometricUnlockModel.SHOW_BOUNCER
+ 4 -> BiometricUnlockModel.ONLY_WAKE
+ 5 -> BiometricUnlockModel.UNLOCK_COLLAPSING
+ 6 -> BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+ 7 -> BiometricUnlockModel.DISMISS_BOUNCER
+ else -> throw IllegalArgumentException("Invalid BiometricUnlockModel value: $value")
+ }
+ }
+
companion object {
private const val TAG = "KeyguardRepositoryImpl"
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt
new file mode 100644
index 000000000000..7e01db34319f
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/AodToGoneTransitionInteractor.kt
@@ -0,0 +1,81 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+
+package com.android.systemui.keyguard.domain.interactor
+
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
+import com.android.systemui.dagger.SysUISingleton
+import com.android.systemui.dagger.qualifiers.Application
+import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.WAKE_AND_UNLOCK
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING
+import com.android.systemui.keyguard.shared.model.KeyguardState
+import com.android.systemui.keyguard.shared.model.TransitionInfo
+import com.android.systemui.util.kotlin.sample
+import javax.inject.Inject
+import kotlinx.coroutines.CoroutineScope
+import kotlinx.coroutines.flow.collect
+import kotlinx.coroutines.launch
+
+@SysUISingleton
+class AodToGoneTransitionInteractor
+@Inject
+constructor(
+ @Application private val scope: CoroutineScope,
+ private val keyguardInteractor: KeyguardInteractor,
+ private val keyguardTransitionRepository: KeyguardTransitionRepository,
+ private val keyguardTransitionInteractor: KeyguardTransitionInteractor,
+) : TransitionInteractor("AOD->GONE") {
+
+ private val wakeAndUnlockModes =
+ setOf(WAKE_AND_UNLOCK, WAKE_AND_UNLOCK_FROM_DREAM, WAKE_AND_UNLOCK_PULSING)
+
+ override fun start() {
+ scope.launch {
+ keyguardInteractor.biometricUnlockState
+ .sample(keyguardTransitionInteractor.finishedKeyguardState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (biometricUnlockState, keyguardState) = pair
+ if (
+ keyguardState == KeyguardState.AOD &&
+ wakeAndUnlockModes.contains(biometricUnlockState)
+ ) {
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ name,
+ KeyguardState.AOD,
+ KeyguardState.GONE,
+ getAnimator(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(TRANSITION_DURATION_MS)
+ }
+ }
+
+ companion object {
+ private const val TRANSITION_DURATION_MS = 500L
+ }
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt
index 2af9318d92ec..dbb0352c2187 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractor.kt
@@ -30,7 +30,6 @@ import com.android.systemui.dagger.qualifiers.Main
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
-import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.plugins.ActivityStarter
@@ -40,6 +39,7 @@ import com.android.systemui.statusbar.phone.KeyguardBypassController
import com.android.systemui.statusbar.policy.KeyguardStateController
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
+import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
@@ -77,7 +77,7 @@ constructor(
KeyguardBouncerModel(
promptReason = repository.bouncerPromptReason ?: 0,
errorMessage = repository.bouncerErrorMessage,
- expansionAmount = repository.expansionAmount.value
+ expansionAmount = repository.panelExpansionAmount.value
)
)
repository.setShowingSoon(false)
@@ -90,14 +90,22 @@ constructor(
val startingToHide: Flow<Unit> = repository.startingToHide.filter { it }.map {}
val isVisible: Flow<Boolean> = repository.isVisible
val isBackButtonEnabled: Flow<Boolean> = repository.isBackButtonEnabled.filterNotNull()
- val expansionAmount: Flow<Float> = repository.expansionAmount
val showMessage: Flow<BouncerShowMessageModel> = repository.showMessage.filterNotNull()
val startingDisappearAnimation: Flow<Runnable> =
repository.startingDisappearAnimation.filterNotNull()
- val onDismissAction: Flow<BouncerCallbackActionsModel> =
- repository.onDismissAction.filterNotNull()
val resourceUpdateRequests: Flow<Boolean> = repository.resourceUpdateRequests.filter { it }
val keyguardPosition: Flow<Float> = repository.keyguardPosition
+ val panelExpansionAmount: Flow<Float> = repository.panelExpansionAmount
+ /** 0f = bouncer fully hidden. 1f = bouncer fully visible. */
+ val bouncerExpansion: Flow<Float> = //
+ combine(repository.panelExpansionAmount, repository.isVisible) { expansionAmount, isVisible
+ ->
+ if (isVisible) {
+ 1f - expansionAmount
+ } else {
+ 0f
+ }
+ }
// TODO(b/243685699): Move isScrimmed logic to data layer.
// TODO(b/243695312): Encapsulate all of the show logic for the bouncer.
@@ -128,7 +136,7 @@ constructor(
Trace.beginSection("KeyguardBouncer#show")
repository.setScrimmed(isScrimmed)
if (isScrimmed) {
- setExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
+ setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
}
if (resumeBouncer) {
@@ -149,7 +157,6 @@ constructor(
}
keyguardStateController.notifyBouncerShowing(true)
callbackInteractor.dispatchStartingToShow()
-
Trace.endSection()
}
@@ -168,7 +175,6 @@ constructor(
keyguardStateController.notifyBouncerShowing(false /* showing */)
cancelShowRunnable()
repository.setShowingSoon(false)
- repository.setOnDismissAction(null)
repository.setVisible(false)
repository.setHide(true)
repository.setShow(null)
@@ -176,14 +182,17 @@ constructor(
}
/**
- * Sets the panel expansion which is calculated further upstream. Expansion is from 0f to 1f
- * where 0f => showing and 1f => hiding
+ * Sets the panel expansion which is calculated further upstream. Panel expansion is from 0f
+ * (panel fully hidden) to 1f (panel fully showing). As the panel shows (from 0f => 1f), the
+ * bouncer hides and as the panel becomes hidden (1f => 0f), the bouncer starts to show.
+ * Therefore, a panel expansion of 1f represents the bouncer fully hidden and a panel expansion
+ * of 0f represents the bouncer fully showing.
*/
- fun setExpansion(expansion: Float) {
- val oldExpansion = repository.expansionAmount.value
+ fun setPanelExpansion(expansion: Float) {
+ val oldExpansion = repository.panelExpansionAmount.value
val expansionChanged = oldExpansion != expansion
if (repository.startingDisappearAnimation.value == null) {
- repository.setExpansion(expansion)
+ repository.setPanelExpansion(expansion)
}
if (
@@ -227,7 +236,7 @@ constructor(
onDismissAction: ActivityStarter.OnDismissAction?,
cancelAction: Runnable?
) {
- repository.setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction))
+ bouncerView.delegate?.setDismissAction(onDismissAction, cancelAction)
}
/** Update the resources of the views. */
@@ -282,7 +291,7 @@ constructor(
/** Returns whether bouncer is fully showing. */
fun isFullyShowing(): Boolean {
return (repository.showingSoon.value || repository.isVisible.value) &&
- repository.expansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE &&
+ repository.panelExpansionAmount.value == KeyguardBouncer.EXPANSION_VISIBLE &&
repository.startingDisappearAnimation.value == null
}
@@ -294,8 +303,8 @@ constructor(
/** If bouncer expansion is between 0f and 1f non-inclusive. */
fun isInTransit(): Boolean {
return repository.showingSoon.value ||
- repository.expansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN &&
- repository.expansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE
+ repository.panelExpansionAmount.value != KeyguardBouncer.EXPANSION_HIDDEN &&
+ repository.panelExpansionAmount.value != KeyguardBouncer.EXPANSION_VISIBLE
}
/** Return whether bouncer is animating away. */
@@ -305,7 +314,7 @@ constructor(
/** Return whether bouncer will dismiss with actions */
fun willDismissWithAction(): Boolean {
- return repository.onDismissAction.value?.onDismissAction != null
+ return bouncerView.delegate?.willDismissWithActions() == true
}
/** Returns whether the bouncer should be full screen. */
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
index 03c6a789326e..614ff8d930d8 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardInteractor.kt
@@ -19,6 +19,8 @@ package com.android.systemui.keyguard.domain.interactor
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardRepository
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
+import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import javax.inject.Inject
import kotlinx.coroutines.flow.Flow
@@ -39,10 +41,19 @@ constructor(
val dozeAmount: Flow<Float> = repository.dozeAmount
/** Whether the system is in doze mode. */
val isDozing: Flow<Boolean> = repository.isDozing
- /** Whether the keyguard is showing to not. */
+ /** Whether the keyguard is showing or not. */
val isKeyguardShowing: Flow<Boolean> = repository.isKeyguardShowing
+ /** Whether the bouncer is showing or not. */
+ val isBouncerShowing: Flow<Boolean> = repository.isBouncerShowing
/** The device wake/sleep state */
val wakefulnessState: Flow<WakefulnessModel> = repository.wakefulnessState
+ /** Observable for the [StatusBarState] */
+ val statusBarState: Flow<StatusBarState> = repository.statusBarState
+ /**
+ * Observable for [BiometricUnlockModel] when biometrics like face or any fingerprint (rear,
+ * side, under display) is used to unlock the device.
+ */
+ val biometricUnlockState: Flow<BiometricUnlockModel> = repository.biometricUnlockState
fun isKeyguardShowing(): Boolean {
return repository.isKeyguardShowing()
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
index 83d94325b9d4..57fb4a114700 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionAuditLogger.kt
@@ -41,11 +41,13 @@ constructor(
}
scope.launch {
- interactor.finishedKeyguardState.collect { logger.i("Finished transition to", it) }
+ interactor.finishedKeyguardTransitionStep.collect {
+ logger.i("Finished transition", it)
+ }
}
scope.launch {
- interactor.startedKeyguardState.collect { logger.i("Started transition to", it) }
+ interactor.startedKeyguardTransitionStep.collect { logger.i("Started transition", it) }
}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
index d5ea77b8e729..a7c6d4450336 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionCoreStartable.kt
@@ -41,6 +41,7 @@ constructor(
is AodLockscreenTransitionInteractor -> Log.d(TAG, "Started $it")
is GoneAodTransitionInteractor -> Log.d(TAG, "Started $it")
is LockscreenGoneTransitionInteractor -> Log.d(TAG, "Started $it")
+ is AodToGoneTransitionInteractor -> Log.d(TAG, "Started $it")
}
it.start()
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
index dffd097a77c5..749183e241e5 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractor.kt
@@ -21,7 +21,6 @@ import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.KeyguardState.AOD
-import com.android.systemui.keyguard.shared.model.KeyguardState.GONE
import com.android.systemui.keyguard.shared.model.KeyguardState.LOCKSCREEN
import com.android.systemui.keyguard.shared.model.TransitionState
import com.android.systemui.keyguard.shared.model.TransitionStep
@@ -44,8 +43,9 @@ constructor(
/** LOCKSCREEN->AOD transition information. */
val lockscreenToAodTransition: Flow<TransitionStep> = repository.transition(LOCKSCREEN, AOD)
- /** GONE->AOD information. */
- val goneToAodTransition: Flow<TransitionStep> = repository.transition(GONE, AOD)
+ /** (any)->AOD transition information */
+ val anyStateToAodTransition: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.to == KeyguardState.AOD }
/**
* AOD<->LOCKSCREEN transition information, mapped to dozeAmount range of AOD (1f) <->
@@ -57,15 +57,15 @@ constructor(
lockscreenToAodTransition,
)
+ /* The last [TransitionStep] with a [TransitionState] of FINISHED */
+ val finishedKeyguardTransitionStep: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.transitionState == TransitionState.FINISHED }
+
/* The last completed [KeyguardState] transition */
val finishedKeyguardState: Flow<KeyguardState> =
- repository.transitions
- .filter { step -> step.transitionState == TransitionState.FINISHED }
- .map { step -> step.to }
+ finishedKeyguardTransitionStep.map { step -> step.to }
- /* The last started [KeyguardState] transition */
- val startedKeyguardState: Flow<KeyguardState> =
- repository.transitions
- .filter { step -> step.transitionState == TransitionState.STARTED }
- .map { step -> step.to }
+ /* The last [TransitionStep] with a [TransitionState] of STARTED */
+ val startedKeyguardTransitionStep: Flow<TransitionStep> =
+ repository.transitions.filter { step -> step.transitionState == TransitionState.STARTED }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
index 761f3fd9d9f9..fd4814d2bc94 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/LockscreenBouncerTransitionInteractor.kt
@@ -16,14 +16,16 @@
package com.android.systemui.keyguard.domain.interactor
+import android.animation.ValueAnimator
+import com.android.systemui.animation.Interpolators
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.dagger.qualifiers.Application
-import com.android.systemui.keyguard.data.repository.KeyguardRepository
import com.android.systemui.keyguard.data.repository.KeyguardTransitionRepository
import com.android.systemui.keyguard.shared.model.KeyguardState
import com.android.systemui.keyguard.shared.model.StatusBarState.SHADE_LOCKED
import com.android.systemui.keyguard.shared.model.TransitionInfo
import com.android.systemui.keyguard.shared.model.TransitionState
+import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.shade.data.repository.ShadeRepository
import com.android.systemui.util.kotlin.sample
import java.util.UUID
@@ -38,7 +40,7 @@ class LockscreenBouncerTransitionInteractor
@Inject
constructor(
@Application private val scope: CoroutineScope,
- private val keyguardRepository: KeyguardRepository,
+ private val keyguardInteractor: KeyguardInteractor,
private val shadeRepository: ShadeRepository,
private val keyguardTransitionRepository: KeyguardTransitionRepository,
private val keyguardTransitionInteractor: KeyguardTransitionInteractor
@@ -47,12 +49,47 @@ constructor(
private var transitionId: UUID? = null
override fun start() {
+ listenForDraggingUpToBouncer()
+ listenForBouncerHiding()
+ }
+
+ private fun listenForBouncerHiding() {
+ scope.launch {
+ keyguardInteractor.isBouncerShowing
+ .sample(keyguardInteractor.wakefulnessState, { a, b -> Pair(a, b) })
+ .collect { pair ->
+ val (isBouncerShowing, wakefulnessState) = pair
+ if (!isBouncerShowing) {
+ val to =
+ if (
+ wakefulnessState == WakefulnessModel.STARTING_TO_SLEEP ||
+ wakefulnessState == WakefulnessModel.ASLEEP
+ ) {
+ KeyguardState.AOD
+ } else {
+ KeyguardState.LOCKSCREEN
+ }
+ keyguardTransitionRepository.startTransition(
+ TransitionInfo(
+ ownerName = name,
+ from = KeyguardState.BOUNCER,
+ to = to,
+ animator = getAnimator(),
+ )
+ )
+ }
+ }
+ }
+ }
+
+ /* Starts transitions when manually dragging up the bouncer from the lockscreen. */
+ private fun listenForDraggingUpToBouncer() {
scope.launch {
shadeRepository.shadeModel
.sample(
combine(
keyguardTransitionInteractor.finishedKeyguardState,
- keyguardRepository.statusBarState,
+ keyguardInteractor.statusBarState,
) { keyguardState, statusBarState ->
Pair(keyguardState, statusBarState)
},
@@ -100,4 +137,15 @@ constructor(
}
}
}
+
+ private fun getAnimator(): ValueAnimator {
+ return ValueAnimator().apply {
+ setInterpolator(Interpolators.LINEAR)
+ setDuration(TRANSITION_DURATION_MS)
+ }
+ }
+
+ companion object {
+ private const val TRANSITION_DURATION_MS = 300L
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
index 728bafae2a4c..37f33afbf53e 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/domain/interactor/StartKeyguardTransitionModule.kt
@@ -42,6 +42,8 @@ abstract class StartKeyguardTransitionModule {
@Binds @IntoSet abstract fun goneAod(impl: GoneAodTransitionInteractor): TransitionInteractor
+ @Binds @IntoSet abstract fun aodGone(impl: AodToGoneTransitionInteractor): TransitionInteractor
+
@Binds
@IntoSet
abstract fun lockscreenGone(impl: LockscreenGoneTransitionInteractor): TransitionInteractor
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt
new file mode 100644
index 000000000000..db709b476c5d
--- /dev/null
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/BiometricUnlockModel.kt
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License
+ */
+package com.android.systemui.keyguard.shared.model
+
+/** Model device wakefulness states. */
+enum class BiometricUnlockModel {
+ /** Mode in which we don't need to wake up the device when we authenticate. */
+ NONE,
+ /**
+ * Mode in which we wake up the device, and directly dismiss Keyguard. Active when we acquire a
+ * fingerprint while the screen is off and the device was sleeping.
+ */
+ WAKE_AND_UNLOCK,
+ /**
+ * Mode in which we wake the device up, and fade out the Keyguard contents because they were
+ * already visible while pulsing in doze mode.
+ */
+ WAKE_AND_UNLOCK_PULSING,
+ /**
+ * Mode in which we wake up the device, but play the normal dismiss animation. Active when we
+ * acquire a fingerprint pulsing in doze mode.
+ */
+ SHOW_BOUNCER,
+ /**
+ * Mode in which we only wake up the device, and keyguard was not showing when we authenticated.
+ */
+ ONLY_WAKE,
+ /**
+ * Mode in which fingerprint unlocks the device or passive auth (ie face auth) unlocks the
+ * device while being requested when keyguard is occluded or showing.
+ */
+ UNLOCK_COLLAPSING,
+ /** When bouncer is visible and will be dismissed. */
+ DISMISS_BOUNCER,
+ /** Mode in which fingerprint wakes and unlocks the device from a dream. */
+ WAKE_AND_UNLOCK_FROM_DREAM,
+}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
index 0ca358210813..732a6f7b887a 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/shared/model/TransitionStep.kt
@@ -21,10 +21,11 @@ data class TransitionStep(
val to: KeyguardState = KeyguardState.NONE,
val value: Float = 0f, // constrained [0.0, 1.0]
val transitionState: TransitionState = TransitionState.FINISHED,
+ val ownerName: String = "",
) {
constructor(
info: TransitionInfo,
value: Float,
transitionState: TransitionState,
- ) : this(info.from, info.to, value, transitionState)
+ ) : this(info.from, info.to, value, transitionState, info.ownerName)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
index df260148751c..a22958b74bb9 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/binder/KeyguardBouncerViewBinder.kt
@@ -29,6 +29,7 @@ import com.android.keyguard.dagger.KeyguardBouncerComponent
import com.android.systemui.keyguard.data.BouncerViewDelegate
import com.android.systemui.keyguard.ui.viewmodel.KeyguardBouncerViewModel
import com.android.systemui.lifecycle.repeatWhenAttached
+import com.android.systemui.plugins.ActivityStarter
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.collect
@@ -75,6 +76,17 @@ object KeyguardBouncerViewBinder {
hostViewController.showPrimarySecurityScreen()
hostViewController.onResume()
}
+
+ override fun setDismissAction(
+ onDismissAction: ActivityStarter.OnDismissAction?,
+ cancelAction: Runnable?
+ ) {
+ hostViewController.setOnDismissAction(onDismissAction, cancelAction)
+ }
+
+ override fun willDismissWithActions(): Boolean {
+ return hostViewController.hasDismissActions()
+ }
}
view.repeatWhenAttached {
repeatOnLifecycle(Lifecycle.State.STARTED) {
@@ -122,15 +134,6 @@ object KeyguardBouncerViewBinder {
}
launch {
- viewModel.setDismissAction.collect {
- hostViewController.setOnDismissAction(
- it.onDismissAction,
- it.cancelAction
- )
- }
- }
-
- launch {
viewModel.startDisappearAnimation.collect {
hostViewController.startDisappearAnimation(it)
}
diff --git a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
index 9ad52117bfc6..9a9284371074 100644
--- a/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
+++ b/packages/SystemUI/src/com/android/systemui/keyguard/ui/viewmodel/KeyguardBouncerViewModel.kt
@@ -20,7 +20,6 @@ import android.view.View
import com.android.systemui.keyguard.data.BouncerView
import com.android.systemui.keyguard.data.BouncerViewDelegate
import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
-import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.statusbar.phone.KeyguardBouncer.EXPANSION_VISIBLE
@@ -38,7 +37,7 @@ constructor(
private val interactor: BouncerInteractor,
) {
/** Observe on bouncer expansion amount. */
- val bouncerExpansionAmount: Flow<Float> = interactor.expansionAmount
+ val bouncerExpansionAmount: Flow<Float> = interactor.panelExpansionAmount
/** Observe on bouncer visibility. */
val isBouncerVisible: Flow<Boolean> = interactor.isVisible
@@ -63,9 +62,6 @@ constructor(
/** Observe whether bouncer is starting to hide. */
val startingToHide: Flow<Unit> = interactor.startingToHide
- /** Observe whether we want to set the dismiss action to the bouncer. */
- val setDismissAction: Flow<BouncerCallbackActionsModel> = interactor.onDismissAction
-
/** Observe whether we want to start the disappear animation. */
val startDisappearAnimation: Flow<Runnable> = interactor.startingDisappearAnimation
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
index c089511a7ce9..85d15dca12cb 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/NavigationBar.java
@@ -85,7 +85,11 @@ import android.view.InsetsVisibilities;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
+import android.view.SurfaceControl;
+import android.view.SurfaceControl.Transaction;
import android.view.View;
+import android.view.ViewRootImpl;
+import android.view.ViewRootImpl.SurfaceChangedCallback;
import android.view.ViewTreeObserver;
import android.view.ViewTreeObserver.InternalInsetsInfo;
import android.view.ViewTreeObserver.OnComputeInternalInsetsListener;
@@ -354,15 +358,6 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
@Override
- public void onQuickStepStarted() {
- // Use navbar dragging as a signal to hide the rotate button
- mView.getRotationButtonController().setRotateSuggestionButtonState(false);
-
- // Hide the notifications panel when quick step starts
- mShadeController.collapsePanel(true /* animate */);
- }
-
- @Override
public void onPrioritizedRotation(@Surface.Rotation int rotation) {
mStartingQuickSwitchRotation = rotation;
if (rotation == -1) {
@@ -475,6 +470,24 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
};
+ private final ViewRootImpl.SurfaceChangedCallback mSurfaceChangedCallback =
+ new SurfaceChangedCallback() {
+ @Override
+ public void surfaceCreated(Transaction t) {
+ notifyNavigationBarSurface();
+ }
+
+ @Override
+ public void surfaceDestroyed() {
+ notifyNavigationBarSurface();
+ }
+
+ @Override
+ public void surfaceReplaced(Transaction t) {
+ notifyNavigationBarSurface();
+ }
+ };
+
@Inject
NavigationBar(
NavigationBarView navigationBarView,
@@ -680,7 +693,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
final Display display = mView.getDisplay();
mView.setComponents(mRecentsOptional);
if (mCentralSurfacesOptionalLazy.get().isPresent()) {
- mView.setComponents(mCentralSurfacesOptionalLazy.get().get().getPanelController());
+ mView.setComponents(
+ mCentralSurfacesOptionalLazy.get().get().getNotificationPanelViewController());
}
mView.setDisabledFlags(mDisabledFlags1, mSysUiFlagsContainer);
mView.setOnVerticalChangedListener(this::onVerticalChanged);
@@ -701,6 +715,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mView.getViewTreeObserver().addOnComputeInternalInsetsListener(
mOnComputeInternalInsetsListener);
+ mView.getViewRootImpl().addSurfaceChangedCallback(mSurfaceChangedCallback);
+ notifyNavigationBarSurface();
mNavBarHelper.registerNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
@@ -779,6 +795,10 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
mHandler.removeCallbacks(mEnableLayoutTransitions);
mNavBarHelper.removeNavTaskStateUpdater(mNavbarTaskbarStateUpdater);
mPipOptional.ifPresent(mView::removePipExclusionBoundsChangeListener);
+ ViewRootImpl viewRoot = mView.getViewRootImpl();
+ if (viewRoot != null) {
+ viewRoot.removeSurfaceChangedCallback(mSurfaceChangedCallback);
+ }
mFrame = null;
mOrientationHandle = null;
}
@@ -932,6 +952,12 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
}
+ private void notifyNavigationBarSurface() {
+ ViewRootImpl viewRoot = mView.getViewRootImpl();
+ SurfaceControl surface = viewRoot != null ? viewRoot.getSurfaceControl() : null;
+ mOverviewProxyService.onNavigationBarSurfaceChanged(surface);
+ }
+
private int deltaRotation(int oldRotation, int newRotation) {
int delta = newRotation - oldRotation;
if (delta < 0) delta += 4;
@@ -1257,8 +1283,8 @@ public class NavigationBar extends ViewController<NavigationBarView> implements
}
private void onVerticalChanged(boolean isVertical) {
- mCentralSurfacesOptionalLazy.get().ifPresent(
- statusBar -> statusBar.setQsScrimEnabled(!isVertical));
+ mCentralSurfacesOptionalLazy.get().ifPresent(statusBar ->
+ statusBar.getNotificationPanelViewController().setQsScrimEnabled(!isVertical));
}
private boolean onNavigationTouch(View v, MotionEvent event) {
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
index 622f5a279a5f..83c2a5de5c6e 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/buttons/KeyButtonView.java
@@ -412,10 +412,6 @@ public class KeyButtonView extends ImageView implements ButtonInterface {
logSomePresses(action, flags);
if (mCode == KeyEvent.KEYCODE_BACK && flags != KeyEvent.FLAG_LONG_PRESS) {
Log.i(TAG, "Back button event: " + KeyEvent.actionToString(action));
- if (action == MotionEvent.ACTION_UP) {
- mOverviewProxyService.notifyBackAction((flags & KeyEvent.FLAG_CANCELED) == 0,
- -1, -1, true /* isButton */, false /* gestureSwipeLeft */);
- }
}
final int repeatCount = (flags & KeyEvent.FLAG_LONG_PRESS) != 0 ? 1 : 0;
final KeyEvent ev = new KeyEvent(mDownTime, when, action, mCode, repeatCount,
diff --git a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
index 709467ffd3b5..c319a827da44 100644
--- a/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
+++ b/packages/SystemUI/src/com/android/systemui/navigationbar/gestural/EdgeBackGestureHandler.java
@@ -287,8 +287,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mBackAnimation.setTriggerBack(true);
}
- mOverviewProxyService.notifyBackAction(true, (int) mDownPoint.x,
- (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
logGesture(mInRejectedExclusion
? SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED_REJECTED
: SysUiStatsLog.BACK_GESTURE__TYPE__COMPLETED);
@@ -300,8 +298,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
mBackAnimation.setTriggerBack(false);
}
logGesture(SysUiStatsLog.BACK_GESTURE__TYPE__INCOMPLETE);
- mOverviewProxyService.notifyBackAction(false, (int) mDownPoint.x,
- (int) mDownPoint.y, false /* isButton */, !mIsOnLeftEdge);
}
@Override
@@ -785,9 +781,6 @@ public class EdgeBackGestureHandler extends CurrentUserTracker
if (mExcludeRegion.contains(x, y)) {
if (withinRange) {
- // Log as exclusion only if it is in acceptable range in the first place.
- mOverviewProxyService.notifyBackAction(
- false /* completed */, -1, -1, false /* isButton */, !mIsOnLeftEdge);
// We don't have the end point for logging purposes.
mEndPoint.x = -1;
mEndPoint.y = -1;
diff --git a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
index e9a6c25c0e6d..1f92b12c1a83 100644
--- a/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/qs/tileimpl/QSIconViewImpl.java
@@ -140,7 +140,7 @@ public class QSIconViewImpl extends QSIconView {
iv.setTag(R.id.qs_icon_tag, icon);
iv.setTag(R.id.qs_slash_tag, state.slash);
iv.setPadding(0, padding, 0, padding);
- if (d instanceof Animatable2) {
+ if (shouldAnimate && d instanceof Animatable2) {
Animatable2 a = (Animatable2) d;
a.start();
if (state.isTransient) {
diff --git a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
index 66be00d8de66..46c4f410d078 100644
--- a/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
+++ b/packages/SystemUI/src/com/android/systemui/recents/OverviewProxyService.java
@@ -64,6 +64,7 @@ import android.view.KeyCharacterMap;
import android.view.KeyEvent;
import android.view.MotionEvent;
import android.view.Surface;
+import android.view.SurfaceControl;
import android.view.accessibility.AccessibilityManager;
import android.view.inputmethod.InputMethodManager;
@@ -149,6 +150,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
private final UiEventLogger mUiEventLogger;
private Region mActiveNavBarRegion;
+ private SurfaceControl mNavigationBarSurface;
private IOverviewProxy mOverviewProxy;
private int mConnectionBackoffAttempts;
@@ -190,7 +192,8 @@ public class OverviewProxyService extends CurrentUserTracker implements
// TODO move this logic to message queue
mCentralSurfacesOptionalLazy.get().ifPresent(centralSurfaces -> {
if (event.getActionMasked() == ACTION_DOWN) {
- centralSurfaces.getPanelController().startExpandLatencyTracking();
+ centralSurfaces.getNotificationPanelViewController()
+ .startExpandLatencyTracking();
}
mHandler.post(() -> {
int action = event.getActionMasked();
@@ -217,17 +220,15 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
- public void onBackPressed() throws RemoteException {
+ public void onBackPressed() {
verifyCallerAndClearCallingIdentityPostMain("onBackPressed", () -> {
sendEvent(KeyEvent.ACTION_DOWN, KeyEvent.KEYCODE_BACK);
sendEvent(KeyEvent.ACTION_UP, KeyEvent.KEYCODE_BACK);
-
- notifyBackAction(true, -1, -1, true, false);
});
}
@Override
- public void onImeSwitcherPressed() throws RemoteException {
+ public void onImeSwitcherPressed() {
// TODO(b/204901476) We're intentionally using DEFAULT_DISPLAY for now since
// Launcher/Taskbar isn't display aware.
mContext.getSystemService(InputMethodManager.class)
@@ -316,12 +317,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
@Override
- public void notifySwipeUpGestureStarted() {
- verifyCallerAndClearCallingIdentityPostMain("notifySwipeUpGestureStarted", () ->
- notifySwipeUpGestureStartedInternal());
- }
-
- @Override
public void notifyPrioritizedRotation(@Surface.Rotation int rotation) {
verifyCallerAndClearCallingIdentityPostMain("notifyPrioritizedRotation", () ->
notifyPrioritizedRotationInternal(rotation));
@@ -443,6 +438,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
Log.e(TAG_OPS, "Failed to call onInitialize()", e);
}
dispatchNavButtonBounds();
+ dispatchNavigationBarSurface();
// Force-update the systemui state flags
updateSystemUiStateFlags();
@@ -597,11 +593,18 @@ public class OverviewProxyService extends CurrentUserTracker implements
.commitUpdate(mContext.getDisplayId());
}
- public void notifyBackAction(boolean completed, int downX, int downY, boolean isButton,
- boolean gestureSwipeLeft) {
+ /**
+ * Called when the navigation bar surface is created or changed
+ */
+ public void onNavigationBarSurfaceChanged(SurfaceControl navbarSurface) {
+ mNavigationBarSurface = navbarSurface;
+ dispatchNavigationBarSurface();
+ }
+
+ private void dispatchNavigationBarSurface() {
try {
if (mOverviewProxy != null) {
- mOverviewProxy.onBackAction(completed, downX, downY, isButton, gestureSwipeLeft);
+ mOverviewProxy.onNavigationBarSurface(mNavigationBarSurface);
}
} catch (RemoteException e) {
Log.e(TAG_OPS, "Failed to notify back action", e);
@@ -614,7 +617,7 @@ public class OverviewProxyService extends CurrentUserTracker implements
final NavigationBarView navBarView =
mNavBarControllerLazy.get().getNavigationBarView(mContext.getDisplayId());
final NotificationPanelViewController panelController =
- mCentralSurfacesOptionalLazy.get().get().getPanelController();
+ mCentralSurfacesOptionalLazy.get().get().getNotificationPanelViewController();
if (SysUiState.DEBUG) {
Log.d(TAG_OPS, "Updating sysui state flags: navBarFragment=" + navBarFragment
+ " navBarView=" + navBarView + " panelController=" + panelController);
@@ -800,24 +803,12 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
- public void notifyQuickStepStarted() {
- for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
- mConnectionCallbacks.get(i).onQuickStepStarted();
- }
- }
-
private void notifyPrioritizedRotationInternal(@Surface.Rotation int rotation) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onPrioritizedRotation(rotation);
}
}
- public void notifyQuickScrubStarted() {
- for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
- mConnectionCallbacks.get(i).onQuickScrubStarted();
- }
- }
-
private void notifyAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {
for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
mConnectionCallbacks.get(i).onAssistantProgress(progress);
@@ -836,12 +827,6 @@ public class OverviewProxyService extends CurrentUserTracker implements
}
}
- private void notifySwipeUpGestureStartedInternal() {
- for (int i = mConnectionCallbacks.size() - 1; i >= 0; --i) {
- mConnectionCallbacks.get(i).onSwipeUpGestureStarted();
- }
- }
-
public void notifyAssistantVisibilityChanged(float visibility) {
try {
if (mOverviewProxy != null) {
@@ -1005,23 +990,20 @@ public class OverviewProxyService extends CurrentUserTracker implements
pw.print(" mWindowCornerRadius="); pw.println(mWindowCornerRadius);
pw.print(" mSupportsRoundedCornersOnWindows="); pw.println(mSupportsRoundedCornersOnWindows);
pw.print(" mActiveNavBarRegion="); pw.println(mActiveNavBarRegion);
+ pw.print(" mNavigationBarSurface="); pw.println(mNavigationBarSurface);
pw.print(" mNavBarMode="); pw.println(mNavBarMode);
mSysUiState.dump(pw, args);
}
public interface OverviewProxyListener {
default void onConnectionChanged(boolean isConnected) {}
- default void onQuickStepStarted() {}
- default void onSwipeUpGestureStarted() {}
default void onPrioritizedRotation(@Surface.Rotation int rotation) {}
default void onOverviewShown(boolean fromHome) {}
- default void onQuickScrubStarted() {}
/** Notify the recents app (overview) is started by 3-button navigation. */
default void onToggleRecentApps() {}
default void onHomeRotationEnabled(boolean enabled) {}
default void onTaskbarStatusUpdated(boolean visible, boolean stashed) {}
default void onTaskbarAutohideSuspend(boolean suspend) {}
- default void onSystemUiStateChanged(int sysuiStateFlags) {}
default void onAssistantProgress(@FloatRange(from = 0.0, to = 1.0) float progress) {}
default void onAssistantGestureCompletion(float velocity) {}
default void startAssistant(Bundle bundle) {}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
index e00f5ebad571..92f5c851f208 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java
@@ -531,7 +531,7 @@ public final class NotificationPanelViewController {
private final NavigationBarController mNavigationBarController;
private final int mDisplayId;
- private KeyguardIndicationController mKeyguardIndicationController;
+ private final KeyguardIndicationController mKeyguardIndicationController;
private int mHeadsUpInset;
private boolean mHeadsUpPinnedMode;
private boolean mAllowExpandForSmallExpansion;
@@ -743,6 +743,7 @@ public final class NotificationPanelViewController {
SysUiState sysUiState,
Provider<KeyguardBottomAreaViewController> keyguardBottomAreaViewControllerProvider,
KeyguardUnlockAnimationController keyguardUnlockAnimationController,
+ KeyguardIndicationController keyguardIndicationController,
NotificationListContainer notificationListContainer,
NotificationStackSizeCalculator notificationStackSizeCalculator,
UnlockedScreenOffAnimationController unlockedScreenOffAnimationController,
@@ -779,6 +780,7 @@ public final class NotificationPanelViewController {
mResources = mView.getResources();
mKeyguardStateController = keyguardStateController;
+ mKeyguardIndicationController = keyguardIndicationController;
mStatusBarStateController = (SysuiStatusBarStateController) statusBarStateController;
mNotificationShadeWindowController = notificationShadeWindowController;
FlingAnimationUtils.Builder fauBuilder = flingAnimationUtilsBuilder.get();
@@ -1020,7 +1022,7 @@ public final class NotificationPanelViewController {
mNotificationStackScrollLayoutController.setOnEmptySpaceClickListener(
mOnEmptySpaceClickListener);
addTrackingHeadsUpListener(mNotificationStackScrollLayoutController::setTrackingHeadsUp);
- mKeyguardBottomArea = mView.findViewById(R.id.keyguard_bottom_area);
+ setKeyguardBottomArea(mView.findViewById(R.id.keyguard_bottom_area));
initBottomArea();
@@ -1264,7 +1266,7 @@ public final class NotificationPanelViewController {
int index = mView.indexOfChild(mKeyguardBottomArea);
mView.removeView(mKeyguardBottomArea);
KeyguardBottomAreaView oldBottomArea = mKeyguardBottomArea;
- mKeyguardBottomArea = mKeyguardBottomAreaViewControllerProvider.get().getView();
+ setKeyguardBottomArea(mKeyguardBottomAreaViewControllerProvider.get().getView());
mKeyguardBottomArea.initFrom(oldBottomArea);
mView.addView(mKeyguardBottomArea, index);
initBottomArea();
@@ -1343,8 +1345,8 @@ public final class NotificationPanelViewController {
return mHintAnimationRunning || mUnlockedScreenOffAnimationController.isAnimationPlaying();
}
- public void setKeyguardIndicationController(KeyguardIndicationController indicationController) {
- mKeyguardIndicationController = indicationController;
+ private void setKeyguardBottomArea(KeyguardBottomAreaView keyguardBottomArea) {
+ mKeyguardBottomArea = keyguardBottomArea;
mKeyguardIndicationController.setIndicationArea(mKeyguardBottomArea);
}
@@ -3880,6 +3882,7 @@ public final class NotificationPanelViewController {
public void setHeadsUpManager(HeadsUpManagerPhone headsUpManager) {
mHeadsUpManager = headsUpManager;
+ mHeadsUpManager.addListener(mOnHeadsUpChangedListener);
mHeadsUpTouchHelper = new HeadsUpTouchHelper(headsUpManager,
mNotificationStackScrollLayoutController.getHeadsUpCallback(),
NotificationPanelViewController.this);
@@ -4361,10 +4364,6 @@ public final class NotificationPanelViewController {
mView.getViewTreeObserver().removeOnGlobalLayoutListener(listener);
}
- public ShadeHeadsUpChangedListener getOnHeadsUpChangedListener() {
- return mOnHeadsUpChangedListener;
- }
-
public void setHeaderDebugInfo(String text) {
if (DEBUG_DRAWABLE) mHeaderDebugInfo = text;
}
@@ -4644,7 +4643,7 @@ public final class NotificationPanelViewController {
mUpdateFlingVelocity = vel;
}
} else if (!mCentralSurfaces.isBouncerShowing()
- && !mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()
+ && !mStatusBarKeyguardViewManager.isShowingAlternateAuth()
&& !mKeyguardStateController.isKeyguardGoingAway()) {
onEmptySpaceClick();
onTrackingStopped(true);
@@ -5682,6 +5681,7 @@ public final class NotificationPanelViewController {
/** @see ViewGroup#onInterceptTouchEvent(MotionEvent) */
public boolean onInterceptTouchEvent(MotionEvent event) {
+ mShadeLog.logMotionEvent(event, "NPVC onInterceptTouchEvent");
if (SPEW_LOGCAT) {
Log.v(TAG,
"NPVC onInterceptTouchEvent (" + event.getId() + "): (" + event.getX()
@@ -5694,6 +5694,8 @@ public final class NotificationPanelViewController {
// Do not let touches go to shade or QS if the bouncer is visible,
// but still let user swipe down to expand the panel, dismissing the bouncer.
if (mCentralSurfaces.isBouncerShowing()) {
+ mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
+ + "bouncer is showing");
return true;
}
if (mCommandQueue.panelsEnabled()
@@ -5701,15 +5703,21 @@ public final class NotificationPanelViewController {
&& mHeadsUpTouchHelper.onInterceptTouchEvent(event)) {
mMetricsLogger.count(COUNTER_PANEL_OPEN, 1);
mMetricsLogger.count(COUNTER_PANEL_OPEN_PEEK, 1);
+ mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
+ + "HeadsUpTouchHelper");
return true;
}
if (!shouldQuickSettingsIntercept(mDownX, mDownY, 0)
&& mPulseExpansionHandler.onInterceptTouchEvent(event)) {
+ mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
+ + "PulseExpansionHandler");
return true;
}
if (!isFullyCollapsed() && onQsIntercept(event)) {
debugLog("onQsIntercept true");
+ mShadeLog.v("NotificationPanelViewController MotionEvent intercepted: "
+ + "QsIntercept");
return true;
}
if (mInstantExpanding || !mNotificationsDragEnabled || mTouchDisabled || (mMotionAborted
@@ -5740,6 +5748,9 @@ public final class NotificationPanelViewController {
if (mAnimatingOnDown && mClosing && !mHintAnimationRunning) {
cancelHeightAnimator();
mTouchSlopExceeded = true;
+ mShadeLog.v("NotificationPanelViewController MotionEvent intercepted:"
+ + " mAnimatingOnDown: true, mClosing: true, mHintAnimationRunning:"
+ + " false");
return true;
}
mInitialExpandY = y;
@@ -5784,6 +5795,8 @@ public final class NotificationPanelViewController {
&& hAbs > Math.abs(x - mInitialExpandX)) {
cancelHeightAnimator();
startExpandMotion(x, y, true /* startTracking */, mExpandedHeight);
+ mShadeLog.v("NotificationPanelViewController MotionEvent"
+ + " intercepted: startExpandMotion");
return true;
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
index 65bd58d0d801..1e63b2dd134f 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java
@@ -284,7 +284,7 @@ public class NotificationShadeWindowViewController {
return true;
}
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
// capture all touches if the alt auth bouncer is showing
return true;
}
@@ -322,7 +322,7 @@ public class NotificationShadeWindowViewController {
handled = !mService.isPulsing();
}
- if (mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()) {
+ if (mStatusBarKeyguardViewManager.isShowingAlternateAuth()) {
// eat the touch
handled = true;
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
index 084b7dc3a646..bf622c941abb 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/PulsingGestureListener.kt
@@ -52,6 +52,7 @@ class PulsingGestureListener @Inject constructor(
private val centralSurfaces: CentralSurfaces,
private val ambientDisplayConfiguration: AmbientDisplayConfiguration,
private val statusBarStateController: StatusBarStateController,
+ private val shadeLogger: ShadeLogger,
tunerService: TunerService,
dumpManager: DumpManager
) : GestureDetector.SimpleOnGestureListener(), Dumpable {
@@ -77,18 +78,23 @@ class PulsingGestureListener @Inject constructor(
}
override fun onSingleTapUp(e: MotionEvent): Boolean {
- if (statusBarStateController.isDozing &&
- singleTapEnabled &&
- !dockManager.isDocked &&
- !falsingManager.isProximityNear &&
- !falsingManager.isFalseTap(LOW_PENALTY)
- ) {
- centralSurfaces.wakeUpIfDozing(
+ val isNotDocked = !dockManager.isDocked
+ shadeLogger.logSingleTapUp(statusBarStateController.isDozing, singleTapEnabled, isNotDocked)
+ if (statusBarStateController.isDozing && singleTapEnabled && isNotDocked) {
+ val proximityIsNotNear = !falsingManager.isProximityNear
+ val isNotAFalseTap = !falsingManager.isFalseTap(LOW_PENALTY)
+ shadeLogger.logSingleTapUpFalsingState(proximityIsNotNear, isNotAFalseTap)
+ if (proximityIsNotNear && isNotAFalseTap) {
+ shadeLogger.d("Single tap handled, requesting centralSurfaces.wakeUpIfDozing")
+ centralSurfaces.wakeUpIfDozing(
SystemClock.uptimeMillis(),
notificationShadeWindowView,
- "PULSING_SINGLE_TAP")
+ "PULSING_SINGLE_TAP"
+ )
+ }
return true
}
+ shadeLogger.d("onSingleTapUp event ignored")
return false
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
index f389dd970fbc..eaf7faecd5b3 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeControllerImpl.java
@@ -224,6 +224,6 @@ public class ShadeControllerImpl implements ShadeController {
}
private NotificationPanelViewController getNotificationPanelViewController() {
- return getCentralSurfaces().getPanelController();
+ return getCentralSurfaces().getNotificationPanelViewController();
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
index 7f1bba350af1..761530101061 100644
--- a/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/shade/ShadeLogger.kt
@@ -16,6 +16,10 @@ class ShadeLogger @Inject constructor(@ShadeLog private val buffer: LogBuffer) {
buffer.log(TAG, LogLevel.VERBOSE, msg)
}
+ fun d(@CompileTimeConstant msg: String) {
+ buffer.log(TAG, LogLevel.DEBUG, msg)
+ }
+
private inline fun log(
logLevel: LogLevel,
initializer: LogMessage.() -> Unit,
@@ -123,4 +127,25 @@ class ShadeLogger @Inject constructor(@ShadeLog private val buffer: LogBuffer) {
"animatingQs=$long1"
})
}
+
+ fun logSingleTapUp(isDozing: Boolean, singleTapEnabled: Boolean, isNotDocked: Boolean) {
+ log(LogLevel.DEBUG, {
+ bool1 = isDozing
+ bool2 = singleTapEnabled
+ bool3 = isNotDocked
+ }, {
+ "PulsingGestureListener#onSingleTapUp all of this must true for single " +
+ "tap to be detected: isDozing: $bool1, singleTapEnabled: $bool2, isNotDocked: $bool3"
+ })
+ }
+
+ fun logSingleTapUpFalsingState(proximityIsNotNear: Boolean, isNotFalseTap: Boolean) {
+ log(LogLevel.DEBUG, {
+ bool1 = proximityIsNotNear
+ bool2 = isNotFalseTap
+ }, {
+ "PulsingGestureListener#onSingleTapUp all of this must true for single " +
+ "tap to be detected: proximityIsNotNear: $bool1, isNotFalseTap: $bool2"
+ })
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
index 87ef92a28d5d..408293cffd99 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/NotificationShelf.java
@@ -496,9 +496,6 @@ public class NotificationShelf extends ActivatableNotificationView implements
return;
}
- final float smallCornerRadius =
- getResources().getDimension(R.dimen.notification_corner_radius_small)
- / getResources().getDimension(R.dimen.notification_corner_radius);
final float viewEnd = viewStart + anv.getActualHeight();
final float cornerAnimationDistance = mCornerAnimationDistance
* mAmbientState.getExpansionFraction();
@@ -509,7 +506,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
final float changeFraction = MathUtils.saturate(
(viewEnd - cornerAnimationTop) / cornerAnimationDistance);
anv.requestBottomRoundness(
- anv.isLastInSection() ? 1f : changeFraction,
+ /* value = */ anv.isLastInSection() ? 1f : changeFraction,
/* animate = */ false,
SourceType.OnScroll);
@@ -517,7 +514,7 @@ public class NotificationShelf extends ActivatableNotificationView implements
// Fast scroll skips frames and leaves corners with unfinished rounding.
// Reset top and bottom corners outside of animation bounds.
anv.requestBottomRoundness(
- anv.isLastInSection() ? 1f : smallCornerRadius,
+ /* value = */ anv.isLastInSection() ? 1f : 0f,
/* animate = */ false,
SourceType.OnScroll);
}
@@ -527,16 +524,16 @@ public class NotificationShelf extends ActivatableNotificationView implements
final float changeFraction = MathUtils.saturate(
(viewStart - cornerAnimationTop) / cornerAnimationDistance);
anv.requestTopRoundness(
- anv.isFirstInSection() ? 1f : changeFraction,
- false,
+ /* value = */ anv.isFirstInSection() ? 1f : changeFraction,
+ /* animate = */ false,
SourceType.OnScroll);
} else if (viewStart < cornerAnimationTop) {
// Fast scroll skips frames and leaves corners with unfinished rounding.
// Reset top and bottom corners outside of animation bounds.
anv.requestTopRoundness(
- anv.isFirstInSection() ? 1f : smallCornerRadius,
- false,
+ /* value = */ anv.isFirstInSection() ? 1f : 0f,
+ /* animate = */ false,
SourceType.OnScroll);
}
}
@@ -976,6 +973,16 @@ public class NotificationShelf extends ActivatableNotificationView implements
mIndexOfFirstViewInShelf = mHostLayoutController.indexOfChild(firstViewInShelf);
}
+ /**
+ * This method resets the OnScroll roundness of a view to 0f
+ *
+ * Note: This should be the only class that handles roundness {@code SourceType.OnScroll}
+ */
+ public static void resetOnScrollRoundness(ExpandableView expandableView) {
+ expandableView.requestTopRoundness(0f, false, SourceType.OnScroll);
+ expandableView.requestBottomRoundness(0f, false, SourceType.OnScroll);
+ }
+
public class ShelfState extends ExpandableViewState {
private boolean hasItemsInStableShelf;
private ExpandableView firstViewInShelf;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
index 258f9fc5449f..c070fccf9808 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/VibratorHelper.java
@@ -19,6 +19,7 @@ package com.android.systemui.statusbar;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.media.AudioAttributes;
+import android.os.Process;
import android.os.VibrationAttributes;
import android.os.VibrationEffect;
import android.os.Vibrator;
@@ -33,6 +34,7 @@ import java.util.concurrent.Executor;
import javax.inject.Inject;
/**
+ *
*/
@SysUISingleton
public class VibratorHelper {
@@ -40,9 +42,18 @@ public class VibratorHelper {
private final Vibrator mVibrator;
public static final VibrationAttributes TOUCH_VIBRATION_ATTRIBUTES =
VibrationAttributes.createForUsage(VibrationAttributes.USAGE_TOUCH);
+
+ private static final VibrationEffect BIOMETRIC_SUCCESS_VIBRATION_EFFECT =
+ VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
+ private static final VibrationEffect BIOMETRIC_ERROR_VIBRATION_EFFECT =
+ VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
+ private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
+ VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
+
private final Executor mExecutor;
/**
+ *
*/
@Inject
public VibratorHelper(@Nullable Vibrator vibrator, @Background Executor executor) {
@@ -109,4 +120,23 @@ public class VibratorHelper {
}
mExecutor.execute(mVibrator::cancel);
}
+
+ /**
+ * Perform vibration when biometric authentication success
+ */
+ public void vibrateAuthSuccess(String reason) {
+ vibrate(Process.myUid(),
+ "com.android.systemui",
+ BIOMETRIC_SUCCESS_VIBRATION_EFFECT, reason,
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
+ }
+
+ /**
+ * Perform vibration when biometric authentication error
+ */
+ public void vibrateAuthError(String reason) {
+ vibrate(Process.myUid(), "com.android.systemui",
+ BIOMETRIC_ERROR_VIBRATION_EFFECT, reason,
+ HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
+ }
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
index 26f0ad9eca87..0554fb5b3689 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainer.java
@@ -44,6 +44,7 @@ import com.android.internal.widget.NotificationExpandButton;
import com.android.systemui.R;
import com.android.systemui.statusbar.CrossFadeHelper;
import com.android.systemui.statusbar.NotificationGroupingUtil;
+import com.android.systemui.statusbar.NotificationShelf;
import com.android.systemui.statusbar.notification.FeedbackIcon;
import com.android.systemui.statusbar.notification.NotificationFadeAware;
import com.android.systemui.statusbar.notification.NotificationUtils;
@@ -308,6 +309,11 @@ public class NotificationChildrenContainer extends ViewGroup
row.setContentTransformationAmount(0, false /* isLastChild */);
row.setNotificationFaded(mContainingNotificationIsFaded);
+
+ // This is a workaround, the NotificationShelf should be the owner of `OnScroll` roundness.
+ // Here we should reset the `OnScroll` roundness only on top-level rows.
+ NotificationShelf.resetOnScrollRoundness(row);
+
// It doesn't make sense to keep old animations around, lets cancel them!
ExpandableViewState viewState = row.getViewState();
if (viewState != null) {
@@ -1377,8 +1383,12 @@ public class NotificationChildrenContainer extends ViewGroup
if (child.getVisibility() == View.GONE) {
continue;
}
+ child.requestTopRoundness(
+ /* value = */ 0f,
+ /* animate = */ isShown(),
+ SourceType.DefaultValue);
child.requestBottomRoundness(
- last ? getBottomRoundness() : 0f,
+ /* value = */ last ? getBottomRoundness() : 0f,
/* animate = */ isShown(),
SourceType.DefaultValue);
last = false;
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
index c4ef28e889ca..2c3330e12229 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLayout.java
@@ -114,6 +114,8 @@ import com.android.systemui.util.Assert;
import com.android.systemui.util.DumpUtilsKt;
import com.android.systemui.util.LargeScreenUtils;
+import com.google.errorprone.annotations.CompileTimeConstant;
+
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.util.ArrayList;
@@ -3693,6 +3695,8 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
@ShadeViewRefactor(RefactorComponent.INPUT)
void handleEmptySpaceClick(MotionEvent ev) {
+ logEmptySpaceClick(ev, isBelowLastNotification(mInitialTouchX, mInitialTouchY),
+ mStatusBarState, mTouchIsClick);
switch (ev.getActionMasked()) {
case MotionEvent.ACTION_MOVE:
final float touchSlop = getTouchSlop(ev);
@@ -3704,10 +3708,32 @@ public class NotificationStackScrollLayout extends ViewGroup implements Dumpable
case MotionEvent.ACTION_UP:
if (mStatusBarState != StatusBarState.KEYGUARD && mTouchIsClick &&
isBelowLastNotification(mInitialTouchX, mInitialTouchY)) {
+ debugLog("handleEmptySpaceClick: touch event propagated further");
mOnEmptySpaceClickListener.onEmptySpaceClicked(mInitialTouchX, mInitialTouchY);
}
break;
+ default:
+ debugLog("handleEmptySpaceClick: MotionEvent ignored");
+ }
+ }
+
+ private void debugLog(@CompileTimeConstant String s) {
+ if (mLogger == null) {
+ return;
+ }
+ mLogger.d(s);
+ }
+
+ private void logEmptySpaceClick(MotionEvent ev, boolean isTouchBelowLastNotification,
+ int statusBarState, boolean touchIsClick) {
+ if (mLogger == null) {
+ return;
}
+ mLogger.logEmptySpaceClick(
+ isTouchBelowLastNotification,
+ statusBarState,
+ touchIsClick,
+ MotionEvent.actionToString(ev.getActionMasked()));
}
@ShadeViewRefactor(RefactorComponent.INPUT)
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
index 4c52db7f8732..64dd6dcd3008 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/notification/stack/NotificationStackScrollLogger.kt
@@ -2,6 +2,7 @@ package com.android.systemui.statusbar.notification.stack
import com.android.systemui.log.dagger.NotificationHeadsUpLog
import com.android.systemui.plugins.log.LogBuffer
+import com.android.systemui.plugins.log.LogLevel.DEBUG
import com.android.systemui.plugins.log.LogLevel.INFO
import com.android.systemui.statusbar.notification.collection.NotificationEntry
import com.android.systemui.statusbar.notification.logKey
@@ -10,6 +11,7 @@ import com.android.systemui.statusbar.notification.stack.NotificationStackScroll
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_DISAPPEAR_CLICK
import com.android.systemui.statusbar.notification.stack.NotificationStackScrollLayout.AnimationEvent.ANIMATION_TYPE_HEADS_UP_OTHER
+import com.google.errorprone.annotations.CompileTimeConstant
import javax.inject.Inject
class NotificationStackScrollLogger @Inject constructor(
@@ -56,6 +58,25 @@ class NotificationStackScrollLogger @Inject constructor(
"key: $str1 expected: $bool1 actual: $bool2"
})
}
+
+ fun d(@CompileTimeConstant msg: String) = buffer.log(TAG, DEBUG, msg)
+
+ fun logEmptySpaceClick(
+ isBelowLastNotification: Boolean,
+ statusBarState: Int,
+ touchIsClick: Boolean,
+ motionEventDesc: String
+ ) {
+ buffer.log(TAG, DEBUG, {
+ int1 = statusBarState
+ bool1 = touchIsClick
+ bool2 = isBelowLastNotification
+ str1 = motionEventDesc
+ }, {
+ "handleEmptySpaceClick: statusBarState: $int1 isTouchAClick: $bool1 " +
+ "isTouchBelowNotification: $bool2 motionEvent: $str1"
+ })
+ }
}
private const val TAG = "NotificationStackScroll" \ No newline at end of file
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
index a2798f47be65..6e68079706f4 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/BiometricUnlockController.java
@@ -27,11 +27,8 @@ import android.hardware.fingerprint.FingerprintManager;
import android.metrics.LogMaker;
import android.os.Handler;
import android.os.PowerManager;
-import android.os.Process;
import android.os.SystemClock;
import android.os.Trace;
-import android.os.VibrationAttributes;
-import android.os.VibrationEffect;
import android.util.Log;
import androidx.annotation.Nullable;
@@ -70,8 +67,10 @@ import com.android.systemui.statusbar.policy.KeyguardStateController;
import java.io.PrintWriter;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
+import java.util.HashSet;
import java.util.Map;
import java.util.Optional;
+import java.util.Set;
import javax.inject.Inject;
@@ -87,12 +86,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private static final String BIOMETRIC_WAKE_LOCK_NAME = "wake-and-unlock:wakelock";
private static final UiEventLogger UI_EVENT_LOGGER = new UiEventLoggerImpl();
private static final int UDFPS_ATTEMPTS_BEFORE_SHOW_BOUNCER = 3;
- private static final VibrationEffect SUCCESS_VIBRATION_EFFECT =
- VibrationEffect.get(VibrationEffect.EFFECT_CLICK);
- private static final VibrationEffect ERROR_VIBRATION_EFFECT =
- VibrationEffect.get(VibrationEffect.EFFECT_DOUBLE_CLICK);
- private static final VibrationAttributes HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES =
- VibrationAttributes.createForUsage(VibrationAttributes.USAGE_HARDWARE_FEEDBACK);
@IntDef(prefix = { "MODE_" }, value = {
MODE_NONE,
@@ -167,7 +160,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private final NotificationShadeWindowController mNotificationShadeWindowController;
private final SessionTracker mSessionTracker;
private final int mConsecutiveFpFailureThreshold;
- private final boolean mShouldVibrate;
private int mMode;
private BiometricSourceType mBiometricType;
private KeyguardViewController mKeyguardViewController;
@@ -177,7 +169,7 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
private PendingAuthenticated mPendingAuthenticated = null;
private boolean mHasScreenTurnedOnSinceAuthenticating;
private boolean mFadedAwayAfterWakeAndUnlock;
- private BiometricModeListener mBiometricModeListener;
+ private Set<BiometricModeListener> mBiometricModeListeners = new HashSet<>();
private final MetricsLogger mMetricsLogger;
private final AuthController mAuthController;
@@ -307,8 +299,6 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mHandler = handler;
mConsecutiveFpFailureThreshold = resources.getInteger(
R.integer.fp_consecutive_failure_time_ms);
- mShouldVibrate = !(resources.getBoolean(
- com.android.internal.R.bool.system_server_plays_face_haptics));
mKeyguardBypassController = keyguardBypassController;
mKeyguardBypassController.setUnlockController(this);
mMetricsLogger = metricsLogger;
@@ -326,9 +316,14 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mKeyguardViewController = keyguardViewController;
}
- /** Sets a {@link BiometricModeListener}. */
- public void setBiometricModeListener(BiometricModeListener biometricModeListener) {
- mBiometricModeListener = biometricModeListener;
+ /** Adds a {@link BiometricModeListener}. */
+ public void addBiometricModeListener(BiometricModeListener listener) {
+ mBiometricModeListeners.add(listener);
+ }
+
+ /** Removes a {@link BiometricModeListener}. */
+ public void removeBiometricModeListener(BiometricModeListener listener) {
+ mBiometricModeListeners.remove(listener);
}
private final Runnable mReleaseBiometricWakeLockRunnable = new Runnable() {
@@ -423,10 +418,10 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
public void startWakeAndUnlock(BiometricSourceType biometricSourceType,
boolean isStrongBiometric) {
int mode = calculateMode(biometricSourceType, isStrongBiometric);
- if (BiometricSourceType.FACE == biometricSourceType && (mode == MODE_WAKE_AND_UNLOCK
+ if (mode == MODE_WAKE_AND_UNLOCK
|| mode == MODE_WAKE_AND_UNLOCK_PULSING || mode == MODE_UNLOCK_COLLAPSING
- || mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER)) {
- vibrateSuccess();
+ || mode == MODE_WAKE_AND_UNLOCK_FROM_DREAM || mode == MODE_DISMISS_BOUNCER) {
+ vibrateSuccess(biometricSourceType);
}
startWakeAndUnlock(mode);
}
@@ -511,15 +506,12 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
break;
}
onModeChanged(mMode);
- if (mBiometricModeListener != null) {
- mBiometricModeListener.notifyBiometricAuthModeChanged();
- }
Trace.endSection();
}
private void onModeChanged(@WakeAndUnlockMode int mode) {
- if (mBiometricModeListener != null) {
- mBiometricModeListener.onModeChanged(mode);
+ for (BiometricModeListener listener : mBiometricModeListeners) {
+ listener.onModeChanged(mode);
}
}
@@ -659,10 +651,11 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
}
// Suppress all face auth errors if fingerprint can be used to authenticate
- if (biometricSourceType == BiometricSourceType.FACE
+ if ((biometricSourceType == BiometricSourceType.FACE
&& !mUpdateMonitor.getCachedIsUnlockWithFingerprintPossible(
- KeyguardUpdateMonitor.getCurrentUser())) {
- vibrateError();
+ KeyguardUpdateMonitor.getCurrentUser()))
+ || (biometricSourceType == BiometricSourceType.FINGERPRINT)) {
+ vibrateError(biometricSourceType);
}
cleanup();
@@ -688,24 +681,15 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
cleanup();
}
- private void vibrateSuccess() {
- if (mShouldVibrate) {
- mVibratorHelper.vibrate(Process.myUid(),
- "com.android.systemui",
- SUCCESS_VIBRATION_EFFECT,
- getClass().getSimpleName() + "::success",
- HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
- }
+ //these haptics are for device-entry only
+ private void vibrateSuccess(BiometricSourceType type) {
+ mVibratorHelper.vibrateAuthSuccess(
+ getClass().getSimpleName() + ", type =" + type + "device-entry::success");
}
- private void vibrateError() {
- if (mShouldVibrate) {
- mVibratorHelper.vibrate(Process.myUid(),
- "com.android.systemui",
- ERROR_VIBRATION_EFFECT,
- getClass().getSimpleName() + "::error",
- HARDWARE_FEEDBACK_VIBRATION_ATTRIBUTES);
- }
+ private void vibrateError(BiometricSourceType type) {
+ mVibratorHelper.vibrateAuthError(
+ getClass().getSimpleName() + ", type =" + type + "device-entry::error");
}
private void cleanup() {
@@ -734,9 +718,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
mMode = MODE_NONE;
mBiometricType = null;
mNotificationShadeWindowController.setForceDozeBrightness(false);
- if (mBiometricModeListener != null) {
- mBiometricModeListener.onResetMode();
- mBiometricModeListener.notifyBiometricAuthModeChanged();
+ for (BiometricModeListener listener : mBiometricModeListeners) {
+ listener.onResetMode();
}
mNumConsecutiveFpFailures = 0;
mLastFpFailureUptimeMillis = 0;
@@ -845,10 +828,8 @@ public class BiometricUnlockController extends KeyguardUpdateMonitorCallback imp
/** An interface to interact with the {@link BiometricUnlockController}. */
public interface BiometricModeListener {
/** Called when {@code mMode} is reset to {@link #MODE_NONE}. */
- void onResetMode();
+ default void onResetMode() {}
/** Called when {@code mMode} has changed in {@link #startWakeAndUnlock(int)}. */
- void onModeChanged(@WakeAndUnlockMode int mode);
- /** Called after processing {@link #onModeChanged(int)}. */
- void notifyBiometricAuthModeChanged();
+ default void onModeChanged(@WakeAndUnlockMode int mode) {}
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
index 75b444fbd297..dc370821dc95 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfaces.java
@@ -196,8 +196,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void collapsePanelOnMainThread();
- void collapsePanelWithDuration(int duration);
-
void togglePanel();
void start();
@@ -305,9 +303,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void checkBarModes();
- // Called by NavigationBarFragment
- void setQsScrimEnabled(boolean scrimEnabled);
-
void updateBubblesVisibility();
void setInteracting(int barWindow, boolean interacting);
@@ -379,8 +374,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void showKeyguardImpl();
- boolean isInLaunchTransition();
-
void fadeKeyguardAfterLaunchTransition(Runnable beforeFading,
Runnable endRunnable, Runnable cancelRunnable);
@@ -437,8 +430,6 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
void showPinningEscapeToast();
- KeyguardBottomAreaView getKeyguardBottomAreaView();
-
void setBouncerShowing(boolean bouncerShowing);
void setBouncerShowingOverDream(boolean bouncerShowingOverDream);
@@ -505,12 +496,8 @@ public interface CentralSurfaces extends Dumpable, ActivityStarter, LifecycleOwn
boolean isBouncerShowingOverDream();
- void onBouncerPreHideAnimation();
-
boolean isKeyguardSecure();
- NotificationPanelViewController getPanelController();
-
NotificationGutsManager getGutsManager();
void updateNotificationPanelTouchState();
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
index 47a12a8b57a3..15cc086586da 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/CentralSurfacesImpl.java
@@ -483,7 +483,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
private final CentralSurfacesComponent.Factory mCentralSurfacesComponentFactory;
private final PluginManager mPluginManager;
- private final com.android.systemui.shade.ShadeController mShadeController;
+ private final ShadeController mShadeController;
private final InitController mInitController;
private final PluginDependencyProvider mPluginDependencyProvider;
@@ -496,9 +496,9 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
private final StatusBarSignalPolicy mStatusBarSignalPolicy;
private final StatusBarHideIconsForBouncerManager mStatusBarHideIconsForBouncerManager;
- // expanded notifications
- // the sliding/resizing panel within the notification window
- protected NotificationPanelViewController mNotificationPanelViewController;
+ /** Controller for the Shade. */
+ @VisibleForTesting
+ NotificationPanelViewController mNotificationPanelViewController;
// settings
private QSPanelController mQSPanelController;
@@ -1169,7 +1169,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
initializer.initializeStatusBar(mCentralSurfacesComponent);
mStatusBarTouchableRegionManager.setup(this, mNotificationShadeWindowView);
- mHeadsUpManager.addListener(mNotificationPanelViewController.getOnHeadsUpChangedListener());
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
createNavigationBar(result);
@@ -1178,9 +1177,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mLockscreenWallpaper = mLockscreenWallpaperLazy.get();
}
- mNotificationPanelViewController.setKeyguardIndicationController(
- mKeyguardIndicationController);
-
mAmbientIndicationContainer = mNotificationShadeWindowView.findViewById(
R.id.ambient_indication_container);
@@ -1531,11 +1527,12 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
protected void startKeyguard() {
Trace.beginSection("CentralSurfaces#startKeyguard");
mBiometricUnlockController = mBiometricUnlockControllerLazy.get();
- mBiometricUnlockController.setBiometricModeListener(
+ mBiometricUnlockController.addBiometricModeListener(
new BiometricUnlockController.BiometricModeListener() {
@Override
public void onResetMode() {
setWakeAndUnlocking(false);
+ notifyBiometricAuthModeChanged();
}
@Override
@@ -1546,11 +1543,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
case BiometricUnlockController.MODE_WAKE_AND_UNLOCK:
setWakeAndUnlocking(true);
}
- }
-
- @Override
- public void notifyBiometricAuthModeChanged() {
- CentralSurfacesImpl.this.notifyBiometricAuthModeChanged();
+ notifyBiometricAuthModeChanged();
}
private void setWakeAndUnlocking(boolean wakeAndUnlocking) {
@@ -2021,8 +2014,7 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
void makeExpandedInvisible() {
- if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible
- + " mExpandedVisible=" + mExpandedVisible);
+ if (SPEW) Log.d(TAG, "makeExpandedInvisible: mExpandedVisible=" + mExpandedVisible);
if (!mExpandedVisible || mNotificationShadeWindowView == null) {
return;
@@ -2198,12 +2190,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNoAnimationOnNextBarModeChange = false;
}
- // Called by NavigationBarFragment
- @Override
- public void setQsScrimEnabled(boolean scrimEnabled) {
- mNotificationPanelViewController.setQsScrimEnabled(scrimEnabled);
- }
-
/** Temporarily hides Bubbles if the status bar is hidden. */
@Override
public void updateBubblesVisibility() {
@@ -2579,14 +2565,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
CommandQueue.FLAG_EXCLUDE_RECENTS_PANEL,
true /* force */, true /* delayed*/);
} else {
-
// Do it after DismissAction has been processed to conserve the needed
// ordering.
mMainExecutor.execute(mShadeController::runPostCollapseRunnables);
}
- } else if (CentralSurfacesImpl.this.isInLaunchTransition()
- && mNotificationPanelViewController.isLaunchTransitionFinished()) {
-
+ } else if (mNotificationPanelViewController.isLaunchTransitionFinished()) {
// We are not dismissing the shade, but the launch transition is already
// finished,
// so nobody will call readyForKeyguardDone anymore. Post it such that
@@ -3008,11 +2991,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mPresenter.updateMediaMetaData(true /* metaDataChanged */, true);
}
- @Override
- public boolean isInLaunchTransition() {
- return mNotificationPanelViewController.isLaunchTransitionFinished();
- }
-
/**
* Fades the content of the keyguard away after the launch transition is done.
*
@@ -3392,12 +3370,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
}
}
- /** Collapse the panel. The collapsing will be animated for the given {@code duration}. */
- @Override
- public void collapsePanelWithDuration(int duration) {
- mNotificationPanelViewController.collapseWithDuration(duration);
- }
-
/**
* Updates the light reveal effect to reflect the reason we're waking or sleeping (for example,
* from the power button).
@@ -3487,12 +3459,6 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
mNavigationBarController.showPinningEscapeToast(mDisplayId);
}
- //TODO(b/254875405): this should be removed.
- @Override
- public KeyguardBottomAreaView getKeyguardBottomAreaView() {
- return mNotificationPanelViewController.getKeyguardBottomAreaView();
- }
-
protected ViewRootImpl getViewRootImpl() {
NotificationShadeWindowView nswv = getNotificationShadeWindowView();
if (nswv != null) return nswv.getViewRootImpl();
@@ -4195,23 +4161,11 @@ public class CentralSurfacesImpl implements CoreStartable, CentralSurfaces {
return mBouncerShowingOverDream;
}
- /**
- * When {@link KeyguardBouncer} starts to be dismissed, playing its animation.
- */
- @Override
- public void onBouncerPreHideAnimation() {
- mNotificationPanelViewController.startBouncerPreHideAnimation();
-
- }
-
@Override
public boolean isKeyguardSecure() {
return mStatusBarKeyguardViewManager.isSecure();
}
- @Override
- public NotificationPanelViewController getPanelController() {
- return mNotificationPanelViewController;
- }
+
// End Extra BaseStatusBarMethods.
@Override
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
index 9bb4132490d4..b2a9509a03b7 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/KeyguardBouncer.java
@@ -64,6 +64,11 @@ public class KeyguardBouncer {
private static final String TAG = "KeyguardBouncer";
static final long BOUNCER_FACE_DELAY = 1200;
public static final float ALPHA_EXPANSION_THRESHOLD = 0.95f;
+ /**
+ * Values for the bouncer expansion represented as the panel expansion.
+ * Panel expansion 1f = panel fully showing = bouncer fully hidden
+ * Panel expansion 0f = panel fully hiding = bouncer fully showing
+ */
public static final float EXPANSION_HIDDEN = 1f;
public static final float EXPANSION_VISIBLE = 0f;
@@ -143,6 +148,14 @@ public class KeyguardBouncer {
}
/**
+ * Get the KeyguardBouncer expansion
+ * @return 1=HIDDEN, 0=SHOWING, in between 0 and 1 means the bouncer is in transition.
+ */
+ public float getExpansion() {
+ return mExpansion;
+ }
+
+ /**
* Enable/disable only the back button
*/
public void setBackButtonEnabled(boolean enabled) {
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 ccb5d8800ddb..a00e75642f55 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarKeyguardViewManager.java
@@ -86,8 +86,10 @@ import com.android.systemui.unfold.SysUIUnfoldComponent;
import java.io.PrintWriter;
import java.util.ArrayList;
+import java.util.HashSet;
import java.util.Objects;
import java.util.Optional;
+import java.util.Set;
import javax.inject.Inject;
@@ -166,13 +168,9 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public void onExpansionChanged(float expansion) {
- if (mAlternateAuthInterceptor != null) {
- mAlternateAuthInterceptor.setBouncerExpansionChanged(expansion);
- }
if (mBouncerAnimating) {
mCentralSurfaces.setBouncerHiddenFraction(expansion);
}
- updateStates();
}
@Override
@@ -184,9 +182,6 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (!isVisible) {
mCentralSurfaces.setBouncerHiddenFraction(KeyguardBouncer.EXPANSION_HIDDEN);
}
- if (mAlternateAuthInterceptor != null) {
- mAlternateAuthInterceptor.onBouncerVisibilityChanged();
- }
/* Register predictive back callback when keyguard becomes visible, and unregister
when it's hidden. */
@@ -252,6 +247,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
private int mLastBiometricMode;
private boolean mLastScreenOffAnimationPlaying;
private float mQsExpansion;
+ final Set<KeyguardViewManagerCallback> mCallbacks = new HashSet<>();
private boolean mIsModernBouncerEnabled;
private OnDismissAction mAfterKeyguardGoneAction;
@@ -465,7 +461,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mBouncer != null) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else {
- mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
}
} else if (mStatusBarStateController.getState() == StatusBarState.SHADE_LOCKED) {
// Don't expand to the bouncer. Instead transition back to the lock screen (see
@@ -475,17 +471,17 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mBouncer != null) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
} else {
- mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
+ mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE);
}
} else if (mKeyguardStateController.isShowing() && !hideBouncerOverDream) {
if (!isWakeAndUnlocking()
&& !(mBiometricUnlockController.getMode() == MODE_DISMISS_BOUNCER)
- && !mCentralSurfaces.isInLaunchTransition()
+ && !mNotificationPanelViewController.isLaunchTransitionFinished()
&& !isUnlockCollapsing()) {
if (mBouncer != null) {
mBouncer.setExpansion(fraction);
} else {
- mBouncerInteractor.setExpansion(fraction);
+ mBouncerInteractor.setPanelExpansion(fraction);
}
}
if (fraction != KeyguardBouncer.EXPANSION_HIDDEN && tracking
@@ -504,7 +500,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (mBouncer != null) {
mBouncer.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
} else {
- mBouncerInteractor.setExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
+ mBouncerInteractor.setPanelExpansion(KeyguardBouncer.EXPANSION_HIDDEN);
}
} else if (mPulsing && fraction == KeyguardBouncer.EXPANSION_VISIBLE) {
// Panel expanded while pulsing but didn't translate the bouncer (because we are
@@ -849,7 +845,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
if (isShowing && isOccluding) {
SysUiStatsLog.write(SysUiStatsLog.KEYGUARD_STATE_CHANGED,
SysUiStatsLog.KEYGUARD_STATE_CHANGED__STATE__OCCLUDED);
- if (mCentralSurfaces.isInLaunchTransition()) {
+ if (mNotificationPanelViewController.isLaunchTransitionFinished()) {
final Runnable endRunnable = new Runnable() {
@Override
public void run() {
@@ -903,7 +899,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
} else {
mBouncerInteractor.startDisappearAnimation(finishRunnable);
}
- mCentralSurfaces.onBouncerPreHideAnimation();
+ mNotificationPanelViewController.startBouncerPreHideAnimation();
// We update the state (which will show the keyguard) only if an animation will run on
// the keyguard. If there is no animation, we wait before updating the state so that we
@@ -935,7 +931,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
long uptimeMillis = SystemClock.uptimeMillis();
long delay = Math.max(0, startTime + HIDE_TIMING_CORRECTION_MS - uptimeMillis);
- if (mCentralSurfaces.isInLaunchTransition()
+ if (mNotificationPanelViewController.isLaunchTransitionFinished()
|| mKeyguardStateController.isFlingingToDismissKeyguard()) {
final boolean wasFlingingToDismissKeyguard =
mKeyguardStateController.isFlingingToDismissKeyguard();
@@ -1313,7 +1309,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
@Override
public boolean shouldDisableWindowAnimationsForUnlock() {
- return mCentralSurfaces.isInLaunchTransition();
+ return mNotificationPanelViewController.isLaunchTransitionFinished();
}
@Override
@@ -1356,7 +1352,7 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
mBouncerInteractor.notifyKeyguardAuthenticated(strongAuth);
}
- if (mAlternateAuthInterceptor != null && isShowingAlternateAuthOrAnimating()) {
+ if (mAlternateAuthInterceptor != null && isShowingAlternateAuth()) {
resetAlternateAuth(false);
executeAfterKeyguardGoneAction();
}
@@ -1442,6 +1438,10 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
pw.println(" mPendingWakeupAction: " + mPendingWakeupAction);
pw.println(" isBouncerShowing(): " + isBouncerShowing());
pw.println(" bouncerIsOrWillBeShowing(): " + bouncerIsOrWillBeShowing());
+ pw.println(" Registered KeyguardViewManagerCallbacks:");
+ for (KeyguardViewManagerCallback callback : mCallbacks) {
+ pw.println(" " + callback);
+ }
if (mBouncer != null) {
mBouncer.dump(pw);
@@ -1466,6 +1466,20 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
}
/**
+ * Add a callback to listen for changes
+ */
+ public void addCallback(KeyguardViewManagerCallback callback) {
+ mCallbacks.add(callback);
+ }
+
+ /**
+ * Removes callback to stop receiving updates
+ */
+ public void removeCallback(KeyguardViewManagerCallback callback) {
+ mCallbacks.remove(callback);
+ }
+
+ /**
* Whether qs is currently expanded.
*/
public float getQsExpansion() {
@@ -1477,8 +1491,8 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
*/
public void setQsExpansion(float qsExpansion) {
mQsExpansion = qsExpansion;
- if (mAlternateAuthInterceptor != null) {
- mAlternateAuthInterceptor.setQsExpansion(qsExpansion);
+ for (KeyguardViewManagerCallback callback : mCallbacks) {
+ callback.onQSExpansionChanged(mQsExpansion);
}
}
@@ -1492,21 +1506,13 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
&& mAlternateAuthInterceptor.isShowingAlternateAuthBouncer();
}
- public boolean isShowingAlternateAuthOrAnimating() {
- return mAlternateAuthInterceptor != null
- && (mAlternateAuthInterceptor.isShowingAlternateAuthBouncer()
- || mAlternateAuthInterceptor.isAnimating());
- }
-
/**
- * Forward touches to any alternate authentication affordances.
+ * Forward touches to callbacks.
*/
- public boolean onTouch(MotionEvent event) {
- if (mAlternateAuthInterceptor == null) {
- return false;
+ public void onTouch(MotionEvent event) {
+ for (KeyguardViewManagerCallback callback: mCallbacks) {
+ callback.onTouch(event);
}
-
- return mAlternateAuthInterceptor.onTouch(event);
}
/** Update keyguard position based on a tapped X coordinate. */
@@ -1640,45 +1646,33 @@ public class StatusBarKeyguardViewManager implements RemoteInputController.Callb
boolean isShowingAlternateAuthBouncer();
/**
- * print information for the alternate auth interceptor registered
- */
- void dump(PrintWriter pw);
-
- /**
- * @return true if the new auth method bouncer is currently animating in or out.
- */
- boolean isAnimating();
-
- /**
- * How much QS is fully expanded where 0f is not showing and 1f is fully expanded.
- */
- void setQsExpansion(float qsExpansion);
-
- /**
- * Forward potential touches to authentication interceptor
- * @return true if event was handled
+ * Use when an app occluding the keyguard would like to give the user ability to
+ * unlock the device using udfps.
+ *
+ * @param color of the udfps icon. should have proper contrast with its background. only
+ * used if requestUdfps = true
*/
- boolean onTouch(MotionEvent event);
+ void requestUdfps(boolean requestUdfps, int color);
/**
- * Update pin/pattern/password bouncer expansion amount where 0 is visible and 1 is fully
- * hidden
+ * print information for the alternate auth interceptor registered
*/
- void setBouncerExpansionChanged(float expansion);
+ void dump(PrintWriter pw);
+ }
+ /**
+ * Callback for KeyguardViewManager state changes.
+ */
+ public interface KeyguardViewManagerCallback {
/**
- * called when the bouncer view visibility has changed.
+ * Set the amount qs is expanded. For example, swipe down from the top of the
+ * lock screen to start the full QS expansion.
*/
- void onBouncerVisibilityChanged();
+ default void onQSExpansionChanged(float qsExpansion) { }
/**
- * Use when an app occluding the keyguard would like to give the user ability to
- * unlock the device using udfps.
- *
- * @param color of the udfps icon. should have proper contrast with its background. only
- * used if requestUdfps = true
+ * Forward touch events to callbacks
*/
- void requestUdfps(boolean requestUdfps, int color);
-
+ default void onTouch(MotionEvent event) { }
}
}
diff --git a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
index ee948c04df38..b1642d6addb6 100644
--- a/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
+++ b/packages/SystemUI/src/com/android/systemui/statusbar/phone/StatusBarLaunchAnimatorController.kt
@@ -31,7 +31,7 @@ class StatusBarLaunchAnimatorController(
delegate.onLaunchAnimationStart(isExpandingFullyAbove)
centralSurfaces.notificationPanelViewController.setIsLaunchAnimationRunning(true)
if (!isExpandingFullyAbove) {
- centralSurfaces.collapsePanelWithDuration(
+ centralSurfaces.notificationPanelViewController.collapseWithDuration(
ActivityLaunchAnimator.TIMINGS.totalDuration.toInt())
}
}
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
index aca60c033bac..131cf7d33e3a 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardListenQueueTest.kt
@@ -72,6 +72,7 @@ private fun fingerprintModel(user: Int) = KeyguardFingerprintListenModel(
keyguardOccluded = false,
occludingAppRequestingFp = false,
primaryUser = false,
+ shouldListenSfpsState = false,
shouldListenForFingerprintAssistant = false,
switchingUser = false,
udfps = false,
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
index 52f8ef8b7ebc..0a2b3d8498c4 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardSecurityContainerControllerTest.java
@@ -194,6 +194,15 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
}
@Test
+ public void onInitConfiguresViewMode() {
+ mKeyguardSecurityContainerController.onInit();
+ verify(mView).initMode(eq(MODE_DEFAULT), eq(mGlobalSettings), eq(mFalsingManager),
+ eq(mUserSwitcherController),
+ any(KeyguardSecurityContainer.UserSwitcherViewMode.UserSwitcherCallback.class),
+ eq(mFalsingA11yDelegate));
+ }
+
+ @Test
public void showSecurityScreen_canInflateAllModes() {
SecurityMode[] modes = SecurityMode.values();
for (SecurityMode mode : modes) {
diff --git a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
index 5104f84f3bc6..a8284d29197d 100644
--- a/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
+++ b/packages/SystemUI/tests/src/com/android/keyguard/KeyguardUpdateMonitorTest.java
@@ -20,6 +20,7 @@ import static android.app.StatusBarManager.SESSION_KEYGUARD;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ACQUIRED_START;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT;
import static android.hardware.biometrics.BiometricFingerprintConstants.FINGERPRINT_ERROR_LOCKOUT_PERMANENT;
+import static android.hardware.fingerprint.FingerprintSensorProperties.TYPE_POWER_BUTTON;
import static android.telephony.SubscriptionManager.DATA_ROAMING_DISABLE;
import static android.telephony.SubscriptionManager.NAME_SOURCE_CARRIER_ID;
@@ -38,6 +39,7 @@ import static org.mockito.ArgumentMatchers.anyString;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.clearInvocations;
+import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.spy;
@@ -53,6 +55,7 @@ import android.app.trust.IStrongAuthTracker;
import android.app.trust.TrustManager;
import android.content.BroadcastReceiver;
import android.content.ComponentName;
+import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
@@ -60,18 +63,21 @@ import android.content.pm.PackageManager;
import android.content.pm.ResolveInfo;
import android.content.pm.ServiceInfo;
import android.content.pm.UserInfo;
+import android.database.ContentObserver;
import android.hardware.SensorPrivacyManager;
import android.hardware.biometrics.BiometricConstants;
import android.hardware.biometrics.BiometricManager;
import android.hardware.biometrics.BiometricSourceType;
import android.hardware.biometrics.ComponentInfoInternal;
import android.hardware.biometrics.IBiometricEnabledOnKeyguardCallback;
+import android.hardware.biometrics.SensorProperties;
import android.hardware.face.FaceManager;
import android.hardware.face.FaceSensorProperties;
import android.hardware.face.FaceSensorPropertiesInternal;
import android.hardware.fingerprint.FingerprintManager;
import android.hardware.fingerprint.FingerprintSensorProperties;
import android.hardware.fingerprint.FingerprintSensorPropertiesInternal;
+import android.net.Uri;
import android.nfc.NfcAdapter;
import android.os.Bundle;
import android.os.CancellationSignal;
@@ -110,6 +116,7 @@ import com.android.systemui.statusbar.StatusBarState;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.telephony.TelephonyListenerManager;
import com.android.systemui.util.settings.GlobalSettings;
+import com.android.systemui.util.settings.SecureSettings;
import org.junit.After;
import org.junit.Assert;
@@ -181,6 +188,8 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Mock
private BroadcastDispatcher mBroadcastDispatcher;
@Mock
+ private SecureSettings mSecureSettings;
+ @Mock
private TelephonyManager mTelephonyManager;
@Mock
private SensorPrivacyManager mSensorPrivacyManager;
@@ -214,6 +223,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
private GlobalSettings mGlobalSettings;
private FaceWakeUpTriggersConfig mFaceWakeUpTriggersConfig;
+
private final int mCurrentUserId = 100;
private final UserInfo mCurrentUserInfo = new UserInfo(mCurrentUserId, "Test user", 0);
@@ -223,6 +233,9 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
@Captor
private ArgumentCaptor<FaceManager.AuthenticationCallback> mAuthenticationCallbackCaptor;
+ @Mock
+ private Uri mURI;
+
// Direct executor
private final Executor mBackgroundExecutor = Runnable::run;
private final Executor mMainExecutor = Runnable::run;
@@ -305,6 +318,15 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
mTestableLooper = TestableLooper.get(this);
allowTestableLooperAsMainThread();
+
+ when(mSecureSettings.getUriFor(anyString())).thenReturn(mURI);
+
+ final ContentResolver contentResolver = mContext.getContentResolver();
+ ExtendedMockito.spyOn(contentResolver);
+ doNothing().when(contentResolver)
+ .registerContentObserver(any(Uri.class), anyBoolean(), any(ContentObserver.class),
+ anyInt());
+
mKeyguardUpdateMonitor = new TestableKeyguardUpdateMonitor(mContext);
verify(mBiometricManager)
@@ -1136,6 +1158,64 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
}
@Test
+ public void testStartsListeningForSfps_whenKeyguardIsVisible_ifRequireScreenOnToAuthEnabled()
+ throws RemoteException {
+ // SFPS supported and enrolled
+ final ArrayList<FingerprintSensorPropertiesInternal> props = new ArrayList<>();
+ props.add(newFingerprintSensorPropertiesInternal(TYPE_POWER_BUTTON));
+ when(mAuthController.getSfpsProps()).thenReturn(props);
+ when(mAuthController.isSfpsEnrolled(anyInt())).thenReturn(true);
+
+ // WHEN require screen on to auth is disabled, and keyguard is not awake
+ when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).thenReturn(0);
+ mKeyguardUpdateMonitor.updateSfpsRequireScreenOnToAuthPref();
+
+ mContext.getOrCreateTestableResources().addOverride(
+ com.android.internal.R.bool.config_requireScreenOnToAuthEnabled, true);
+
+ // Preconditions for sfps auth to run
+ keyguardNotGoingAway();
+ currentUserIsPrimary();
+ currentUserDoesNotHaveTrust();
+ biometricsNotDisabledThroughDevicePolicyManager();
+ biometricsEnabledForCurrentUser();
+ userNotCurrentlySwitching();
+
+ statusBarShadeIsLocked();
+ mTestableLooper.processAllMessages();
+
+ // THEN we should listen for sfps when screen off, because require screen on is disabled
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
+
+ // WHEN require screen on to auth is enabled, and keyguard is not awake
+ when(mSecureSettings.getIntForUser(anyString(), anyInt(), anyInt())).thenReturn(1);
+ mKeyguardUpdateMonitor.updateSfpsRequireScreenOnToAuthPref();
+
+ // THEN we shouldn't listen for sfps when screen off, because require screen on is enabled
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isFalse();
+
+ // Device now awake & keyguard is now interactive
+ deviceNotGoingToSleep();
+ deviceIsInteractive();
+ keyguardIsVisible();
+
+ // THEN we should listen for sfps when screen on, and require screen on is enabled
+ assertThat(mKeyguardUpdateMonitor.shouldListenForFingerprint(false)).isTrue();
+ }
+
+
+ private FingerprintSensorPropertiesInternal newFingerprintSensorPropertiesInternal(
+ @FingerprintSensorProperties.SensorType int sensorType) {
+ return new FingerprintSensorPropertiesInternal(
+ 0 /* sensorId */,
+ SensorProperties.STRENGTH_STRONG,
+ 1 /* maxEnrollmentsPerUser */,
+ new ArrayList<ComponentInfoInternal>(),
+ sensorType,
+ true /* resetLockoutRequiresHardwareAuthToken */);
+ }
+
+ @Test
public void testShouldNotListenForUdfps_whenTrustEnabled() {
// GIVEN a "we should listen for udfps" state
mStatusBarStateListener.onStateChanged(StatusBarState.KEYGUARD);
@@ -1804,7 +1884,7 @@ public class KeyguardUpdateMonitorTest extends SysuiTestCase {
protected TestableKeyguardUpdateMonitor(Context context) {
super(context,
TestableLooper.get(KeyguardUpdateMonitorTest.this).getLooper(),
- mBroadcastDispatcher, mDumpManager,
+ mBroadcastDispatcher, mSecureSettings, mDumpManager,
mBackgroundExecutor, mMainExecutor,
mStatusBarStateController, mLockPatternUtils,
mAuthController, mTelephonyListenerManager,
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
index e8c760c3e140..d1107c612977 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthContainerViewTest.kt
@@ -35,6 +35,8 @@ import android.view.WindowInsets
import android.view.WindowManager
import android.widget.ScrollView
import androidx.test.filters.SmallTest
+import com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn
+import com.android.dx.mockito.inline.extended.ExtendedMockito.spyOn
import com.android.internal.jank.InteractionJankMonitor
import com.android.internal.widget.LockPatternUtils
import com.android.systemui.R
@@ -159,6 +161,35 @@ class AuthContainerViewTest : SysuiTestCase() {
}
@Test
+ fun testDismissesOnFocusLoss_hidesKeyboardWhenVisible() {
+ val container = initializeFingerprintContainer(
+ authenticators = BiometricManager.Authenticators.DEVICE_CREDENTIAL
+ )
+ waitForIdleSync()
+
+ val requestID = authContainer?.requestId ?: 0L
+
+ // Simulate keyboard was shown on the credential view
+ val windowInsetsController = container.windowInsetsController
+ spyOn(windowInsetsController)
+ spyOn(container.rootWindowInsets)
+ doReturn(true).`when`(container.rootWindowInsets).isVisible(WindowInsets.Type.ime())
+
+ container.onWindowFocusChanged(false)
+ waitForIdleSync()
+
+ // Expect hiding IME request will be invoked when dismissing the view
+ verify(windowInsetsController)?.hide(WindowInsets.Type.ime())
+
+ verify(callback).onDismissed(
+ eq(AuthDialogCallback.DISMISSED_USER_CANCELED),
+ eq<ByteArray?>(null), /* credentialAttestation */
+ eq(requestID)
+ )
+ assertThat(container.parent).isNull()
+ }
+
+ @Test
fun testActionAuthenticated_sendsDismissedAuthenticated() {
val container = initializeFingerprintContainer()
container.mBiometricCallback.onAction(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
index a02dfa3935d7..a275c8d33751 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthControllerTest.java
@@ -87,6 +87,7 @@ import com.android.systemui.SysuiTestCase;
import com.android.systemui.keyguard.WakefulnessLifecycle;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.statusbar.CommandQueue;
+import com.android.systemui.statusbar.VibratorHelper;
import com.android.systemui.util.concurrency.DelayableExecutor;
import com.android.systemui.util.concurrency.Execution;
import com.android.systemui.util.concurrency.FakeExecution;
@@ -173,6 +174,9 @@ public class AuthControllerTest extends SysuiTestCase {
private DelayableExecutor mBackgroundExecutor;
private TestableAuthController mAuthController;
+ @Mock
+ private VibratorHelper mVibratorHelper;
+
@Before
public void setup() throws RemoteException {
mContextSpy = spy(mContext);
@@ -221,7 +225,8 @@ public class AuthControllerTest extends SysuiTestCase {
mAuthController = new TestableAuthController(mContextSpy, mExecution, mCommandQueue,
mActivityTaskManager, mWindowManager, mFingerprintManager, mFaceManager,
- () -> mUdfpsController, () -> mSidefpsController, mStatusBarStateController);
+ () -> mUdfpsController, () -> mSidefpsController, mStatusBarStateController,
+ mVibratorHelper);
mAuthController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -246,11 +251,13 @@ public class AuthControllerTest extends SysuiTestCase {
// This test is sensitive to prior FingerprintManager interactions.
reset(mFingerprintManager);
+ when(mVibratorHelper.hasVibrator()).thenReturn(true);
+
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
mFaceManager, () -> mUdfpsController, () -> mSidefpsController,
- mStatusBarStateController);
+ mStatusBarStateController, mVibratorHelper);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -270,11 +277,13 @@ public class AuthControllerTest extends SysuiTestCase {
// This test is sensitive to prior FingerprintManager interactions.
reset(mFingerprintManager);
+ when(mVibratorHelper.hasVibrator()).thenReturn(true);
+
// This test requires an uninitialized AuthController.
AuthController authController = new TestableAuthController(mContextSpy, mExecution,
mCommandQueue, mActivityTaskManager, mWindowManager, mFingerprintManager,
mFaceManager, () -> mUdfpsController, () -> mSidefpsController,
- mStatusBarStateController);
+ mStatusBarStateController, mVibratorHelper);
authController.start();
verify(mFingerprintManager).addAuthenticatorsRegisteredCallback(
@@ -928,12 +937,13 @@ public class AuthControllerTest extends SysuiTestCase {
FaceManager faceManager,
Provider<UdfpsController> udfpsControllerFactory,
Provider<SidefpsController> sidefpsControllerFactory,
- StatusBarStateController statusBarStateController) {
+ StatusBarStateController statusBarStateController,
+ VibratorHelper vibratorHelper) {
super(context, execution, commandQueue, activityTaskManager, windowManager,
fingerprintManager, faceManager, udfpsControllerFactory,
sidefpsControllerFactory, mDisplayManager, mWakefulnessLifecycle,
mUserManager, mLockPatternUtils, statusBarStateController,
- mInteractionJankMonitor, mHandler, mBackgroundExecutor);
+ mInteractionJankMonitor, mHandler, mBackgroundExecutor, vibratorHelper);
}
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthCredentialViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthCredentialViewTest.kt
new file mode 100644
index 000000000000..fbda08fab799
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/AuthCredentialViewTest.kt
@@ -0,0 +1,163 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.app.admin.DevicePolicyManager.PASSWORD_QUALITY_SOMETHING
+import android.hardware.biometrics.BiometricManager
+import android.hardware.biometrics.PromptInfo
+import android.hardware.face.FaceSensorPropertiesInternal
+import android.hardware.fingerprint.FingerprintSensorPropertiesInternal
+import android.os.Handler
+import android.os.IBinder
+import android.os.UserManager
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import android.testing.ViewUtils
+import androidx.test.filters.SmallTest
+import com.android.internal.jank.InteractionJankMonitor
+import com.android.internal.widget.LockPatternUtils
+import com.android.internal.widget.LockPatternView
+import com.android.internal.widget.VerifyCredentialResponse
+import com.android.systemui.SysuiTestCase
+import com.android.systemui.biometrics.AuthContainerView.BiometricCallback
+import com.android.systemui.biometrics.AuthCredentialView.ErrorTimer
+import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.util.concurrency.FakeExecutor
+import com.android.systemui.util.time.FakeSystemClock
+import com.google.common.truth.Truth.assertThat
+import org.junit.After
+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.spy
+import org.mockito.Mockito.verify
+import org.mockito.Mockito.`when`
+import org.mockito.junit.MockitoJUnit
+
+@RunWith(AndroidTestingRunner::class)
+@TestableLooper.RunWithLooper(setAsMainLooper = true)
+@SmallTest
+class AuthCredentialViewTest : SysuiTestCase() {
+ @JvmField @Rule var mockitoRule = MockitoJUnit.rule()
+
+ @Mock lateinit var callback: AuthDialogCallback
+ @Mock lateinit var lockPatternUtils: LockPatternUtils
+ @Mock lateinit var userManager: UserManager
+ @Mock lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock lateinit var windowToken: IBinder
+ @Mock lateinit var interactionJankMonitor: InteractionJankMonitor
+
+ private var authContainer: TestAuthContainerView? = null
+ private var authCredentialView: AuthCredentialPatternView? = null
+ private var lockPatternView: LockPatternView? = null
+ private var biometricCallback: BiometricCallback? = null
+ private var errorTimer: ErrorTimer? = null
+
+ @After
+ fun tearDown() {
+ if (authContainer?.isAttachedToWindow == true) {
+ ViewUtils.detachView(authContainer)
+ }
+ }
+
+ @Test
+ fun testAuthCredentialPatternView_onErrorTimeoutFinish_setPatternEnabled() {
+ `when`(lockPatternUtils.getCredentialTypeForUser(anyInt()))
+ .thenReturn(LockPatternUtils.CREDENTIAL_TYPE_PATTERN)
+ `when`(lockPatternUtils.getKeyguardStoredPasswordQuality(anyInt()))
+ .thenReturn(PASSWORD_QUALITY_SOMETHING)
+ val errorResponse: VerifyCredentialResponse = VerifyCredentialResponse.fromError()
+
+ assertThat(initializeFingerprintContainer()).isNotNull()
+ authContainer?.animateToCredentialUI()
+ waitForIdleSync()
+
+ authCredentialView = spy(authContainer?.mCredentialView as AuthCredentialPatternView)
+ authCredentialView?.onCredentialVerified(errorResponse, 5000)
+ errorTimer = authCredentialView?.mErrorTimer
+ errorTimer?.onFinish()
+ waitForIdleSync()
+
+ verify(authCredentialView)?.onErrorTimeoutFinish()
+
+ lockPatternView = authCredentialView?.mLockPatternView
+ assertThat(lockPatternView?.isEnabled).isTrue()
+ }
+
+ private fun initializeFingerprintContainer(
+ authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
+ addToView: Boolean = true
+ ) =
+ initializeContainer(
+ TestAuthContainerView(
+ authenticators = authenticators,
+ fingerprintProps = fingerprintSensorPropertiesInternal()
+ ),
+ addToView
+ )
+
+ private fun initializeContainer(
+ view: TestAuthContainerView,
+ addToView: Boolean
+ ): TestAuthContainerView {
+ authContainer = view
+ if (addToView) {
+ authContainer!!.addToView()
+ biometricCallback = authContainer?.mBiometricCallback
+ }
+ return authContainer!!
+ }
+
+ private inner class TestAuthContainerView(
+ authenticators: Int = BiometricManager.Authenticators.BIOMETRIC_WEAK,
+ fingerprintProps: List<FingerprintSensorPropertiesInternal> = listOf(),
+ faceProps: List<FaceSensorPropertiesInternal> = listOf()
+ ) :
+ AuthContainerView(
+ Config().apply {
+ mContext = context
+ mCallback = callback
+ mSensorIds =
+ (fingerprintProps.map { it.sensorId } + faceProps.map { it.sensorId })
+ .toIntArray()
+ mSkipAnimation = true
+ mPromptInfo = PromptInfo().apply { this.authenticators = authenticators }
+ },
+ fingerprintProps,
+ faceProps,
+ wakefulnessLifecycle,
+ userManager,
+ lockPatternUtils,
+ interactionJankMonitor,
+ Handler(TestableLooper.get(this).looper),
+ FakeExecutor(FakeSystemClock())
+ ) {
+ override fun postOnAnimation(runnable: Runnable) {
+ runnable.run()
+ }
+ }
+
+ override fun waitForIdleSync() = TestableLooper.get(this).processAllMessages()
+
+ private fun AuthContainerView.addToView() {
+ ViewUtils.attachView(this)
+ waitForIdleSync()
+ assertThat(isAttachedToWindow).isTrue()
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
index c85334db9499..90948ff3b769 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerOverlayTest.kt
@@ -42,6 +42,8 @@ import com.android.systemui.R
import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ActivityLaunchAnimator
import com.android.systemui.dump.DumpManager
+import com.android.systemui.flags.FeatureFlags
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
import com.android.systemui.plugins.statusbar.StatusBarStateController
import com.android.systemui.shade.ShadeExpansionStateManager
import com.android.systemui.statusbar.LockscreenShadeTransitionController
@@ -103,6 +105,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
@Mock private lateinit var udfpsView: UdfpsView
@Mock private lateinit var udfpsEnrollView: UdfpsEnrollView
@Mock private lateinit var activityLaunchAnimator: ActivityLaunchAnimator
+ @Mock private lateinit var featureFlags: FeatureFlags
+ @Mock private lateinit var bouncerInteractor: BouncerInteractor
@Captor private lateinit var layoutParamsCaptor: ArgumentCaptor<WindowManager.LayoutParams>
private val onTouch = { _: View, _: MotionEvent, _: Boolean -> true }
@@ -136,7 +140,8 @@ class UdfpsControllerOverlayTest : SysuiTestCase() {
keyguardUpdateMonitor, dialogManager, dumpManager, transitionController,
configurationController, systemClock, keyguardStateController,
unlockedScreenOffAnimationController, udfpsDisplayMode, REQUEST_ID, reason,
- controllerCallback, onTouch, activityLaunchAnimator, isDebuggable
+ controllerCallback, onTouch, activityLaunchAnimator, featureFlags,
+ bouncerInteractor, isDebuggable
)
block()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
index 28e13b8e81ab..be39c0de22a1 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsControllerTest.java
@@ -69,7 +69,9 @@ import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.animation.ActivityLaunchAnimator;
import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.keyguard.ScreenLifecycle;
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
import com.android.systemui.plugins.FalsingManager;
import com.android.systemui.plugins.statusbar.StatusBarStateController;
import com.android.systemui.shade.ShadeExpansionStateManager;
@@ -171,6 +173,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private FakeExecutor mFgExecutor;
@Mock
private UdfpsDisplayMode mUdfpsDisplayMode;
+ @Mock
+ private FeatureFlags mFeatureFlags;
// Stuff for configuring mocks
@Mock
@@ -191,6 +195,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
private ActivityLaunchAnimator mActivityLaunchAnimator;
@Mock
private AlternateUdfpsTouchProvider mAlternateTouchProvider;
+ @Mock
+ private BouncerInteractor mBouncerInteractor;
// Capture listeners so that they can be used to send events
@Captor private ArgumentCaptor<IUdfpsOverlayController> mOverlayCaptor;
@@ -252,6 +258,7 @@ public class UdfpsControllerTest extends SysuiTestCase {
mStatusBarKeyguardViewManager,
mDumpManager,
mKeyguardUpdateMonitor,
+ mFeatureFlags,
mFalsingManager,
mPowerManager,
mAccessibilityManager,
@@ -270,7 +277,8 @@ public class UdfpsControllerTest extends SysuiTestCase {
mLatencyTracker,
mActivityLaunchAnimator,
Optional.of(mAlternateTouchProvider),
- mBiometricsExecutor);
+ mBiometricsExecutor,
+ mBouncerInteractor);
verify(mFingerprintManager).setUdfpsOverlayController(mOverlayCaptor.capture());
mOverlayController = mOverlayCaptor.getValue();
verify(mScreenLifecycle).addObserver(mScreenObserverCaptor.capture());
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
new file mode 100644
index 000000000000..e5c7a42c06a6
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerBaseTest.java
@@ -0,0 +1,172 @@
+/*
+ * Copyright (C) 2020 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics;
+
+import static org.mockito.ArgumentMatchers.anyInt;
+import static org.mockito.Mockito.times;
+import static org.mockito.Mockito.verify;
+import static org.mockito.Mockito.when;
+
+import android.content.Context;
+
+import com.android.keyguard.KeyguardUpdateMonitor;
+import com.android.systemui.SysuiTestCase;
+import com.android.systemui.animation.ActivityLaunchAnimator;
+import com.android.systemui.dump.DumpManager;
+import com.android.systemui.flags.FakeFeatureFlags;
+import com.android.systemui.flags.Flags;
+import com.android.systemui.keyguard.KeyguardViewMediator;
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor;
+import com.android.systemui.plugins.statusbar.StatusBarStateController;
+import com.android.systemui.shade.ShadeExpansionChangeEvent;
+import com.android.systemui.shade.ShadeExpansionListener;
+import com.android.systemui.shade.ShadeExpansionStateManager;
+import com.android.systemui.statusbar.LockscreenShadeTransitionController;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
+import com.android.systemui.statusbar.phone.SystemUIDialogManager;
+import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
+import com.android.systemui.statusbar.policy.ConfigurationController;
+import com.android.systemui.statusbar.policy.KeyguardStateController;
+import com.android.systemui.util.concurrency.DelayableExecutor;
+import com.android.systemui.util.time.FakeSystemClock;
+
+import org.junit.Before;
+import org.mockito.ArgumentCaptor;
+import org.mockito.Captor;
+import org.mockito.Mock;
+import org.mockito.MockitoAnnotations;
+
+import java.util.List;
+
+public class UdfpsKeyguardViewControllerBaseTest extends SysuiTestCase {
+ // Dependencies
+ protected @Mock UdfpsKeyguardView mView;
+ protected @Mock Context mResourceContext;
+ protected @Mock StatusBarStateController mStatusBarStateController;
+ protected @Mock ShadeExpansionStateManager mShadeExpansionStateManager;
+ protected @Mock StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
+ protected @Mock LockscreenShadeTransitionController mLockscreenShadeTransitionController;
+ protected @Mock DumpManager mDumpManager;
+ protected @Mock DelayableExecutor mExecutor;
+ protected @Mock KeyguardUpdateMonitor mKeyguardUpdateMonitor;
+ protected @Mock KeyguardStateController mKeyguardStateController;
+ protected @Mock KeyguardViewMediator mKeyguardViewMediator;
+ protected @Mock ConfigurationController mConfigurationController;
+ protected @Mock UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
+ protected @Mock SystemUIDialogManager mDialogManager;
+ protected @Mock UdfpsController mUdfpsController;
+ protected @Mock ActivityLaunchAnimator mActivityLaunchAnimator;
+ protected @Mock KeyguardBouncer mBouncer;
+ protected @Mock BouncerInteractor mBouncerInteractor;
+
+ protected FakeFeatureFlags mFeatureFlags = new FakeFeatureFlags();
+ protected FakeSystemClock mSystemClock = new FakeSystemClock();
+
+ protected UdfpsKeyguardViewController mController;
+
+ // Capture listeners so that they can be used to send events
+ private @Captor ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
+ protected StatusBarStateController.StateListener mStatusBarStateListener;
+
+ private @Captor ArgumentCaptor<ShadeExpansionListener> mExpansionListenerCaptor;
+ protected List<ShadeExpansionListener> mExpansionListeners;
+
+ private @Captor ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor>
+ mAltAuthInterceptorCaptor;
+ protected StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor;
+
+ private @Captor ArgumentCaptor<KeyguardStateController.Callback>
+ mKeyguardStateControllerCallbackCaptor;
+ protected KeyguardStateController.Callback mKeyguardStateControllerCallback;
+
+ @Before
+ public void setUp() {
+ MockitoAnnotations.initMocks(this);
+ when(mView.getContext()).thenReturn(mResourceContext);
+ when(mResourceContext.getString(anyInt())).thenReturn("test string");
+ when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false);
+ when(mView.getUnpausedAlpha()).thenReturn(255);
+ mController = createUdfpsKeyguardViewController();
+ }
+
+ protected void sendStatusBarStateChanged(int statusBarState) {
+ mStatusBarStateListener.onStateChanged(statusBarState);
+ }
+
+ protected void captureStatusBarStateListeners() {
+ verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture());
+ mStatusBarStateListener = mStateListenerCaptor.getValue();
+ }
+
+ protected void captureStatusBarExpansionListeners() {
+ verify(mShadeExpansionStateManager, times(2))
+ .addExpansionListener(mExpansionListenerCaptor.capture());
+ // first (index=0) is from super class, UdfpsAnimationViewController.
+ // second (index=1) is from UdfpsKeyguardViewController
+ mExpansionListeners = mExpansionListenerCaptor.getAllValues();
+ }
+
+ protected void updateStatusBarExpansion(float fraction, boolean expanded) {
+ ShadeExpansionChangeEvent event =
+ new ShadeExpansionChangeEvent(
+ fraction, expanded, /* tracking= */ false, /* dragDownPxAmount= */ 0f);
+ for (ShadeExpansionListener listener : mExpansionListeners) {
+ listener.onPanelExpansionChanged(event);
+ }
+ }
+
+ protected void captureAltAuthInterceptor() {
+ verify(mStatusBarKeyguardViewManager).setAlternateAuthInterceptor(
+ mAltAuthInterceptorCaptor.capture());
+ mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue();
+ }
+
+ protected void captureKeyguardStateControllerCallback() {
+ verify(mKeyguardStateController).addCallback(
+ mKeyguardStateControllerCallbackCaptor.capture());
+ mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue();
+ }
+
+ public UdfpsKeyguardViewController createUdfpsKeyguardViewController() {
+ return createUdfpsKeyguardViewController(false);
+ }
+
+ protected UdfpsKeyguardViewController createUdfpsKeyguardViewController(
+ boolean useModernBouncer) {
+ mFeatureFlags.set(Flags.MODERN_BOUNCER, useModernBouncer);
+ when(mStatusBarKeyguardViewManager.getBouncer()).thenReturn(
+ useModernBouncer ? null : mBouncer);
+ return new UdfpsKeyguardViewController(
+ mView,
+ mStatusBarStateController,
+ mShadeExpansionStateManager,
+ mStatusBarKeyguardViewManager,
+ mKeyguardUpdateMonitor,
+ mDumpManager,
+ mLockscreenShadeTransitionController,
+ mConfigurationController,
+ mSystemClock,
+ mKeyguardStateController,
+ mUnlockedScreenOffAnimationController,
+ mDialogManager,
+ mUdfpsController,
+ mActivityLaunchAnimator,
+ mFeatureFlags,
+ mBouncerInteractor);
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
index c0f9c82fb131..55b61948ee45 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerTest.java
@@ -25,125 +25,52 @@ import static org.mockito.Mockito.atLeastOnce;
import static org.mockito.Mockito.eq;
import static org.mockito.Mockito.never;
import static org.mockito.Mockito.reset;
-import static org.mockito.Mockito.times;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
-import android.content.Context;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper.RunWithLooper;
import androidx.test.filters.SmallTest;
-import com.android.keyguard.KeyguardUpdateMonitor;
-import com.android.systemui.SysuiTestCase;
-import com.android.systemui.animation.ActivityLaunchAnimator;
-import com.android.systemui.dump.DumpManager;
-import com.android.systemui.keyguard.KeyguardViewMediator;
-import com.android.systemui.plugins.statusbar.StatusBarStateController;
-import com.android.systemui.shade.ShadeExpansionChangeEvent;
import com.android.systemui.shade.ShadeExpansionListener;
-import com.android.systemui.shade.ShadeExpansionStateManager;
-import com.android.systemui.statusbar.LockscreenShadeTransitionController;
import com.android.systemui.statusbar.StatusBarState;
-import com.android.systemui.statusbar.phone.StatusBarKeyguardViewManager;
-import com.android.systemui.statusbar.phone.SystemUIDialogManager;
-import com.android.systemui.statusbar.phone.UnlockedScreenOffAnimationController;
-import com.android.systemui.statusbar.policy.ConfigurationController;
-import com.android.systemui.statusbar.policy.KeyguardStateController;
-import com.android.systemui.util.concurrency.DelayableExecutor;
-import com.android.systemui.util.time.FakeSystemClock;
-
-import org.junit.Before;
+import com.android.systemui.statusbar.phone.KeyguardBouncer;
+
import org.junit.Test;
import org.junit.runner.RunWith;
import org.mockito.ArgumentCaptor;
import org.mockito.Captor;
-import org.mockito.Mock;
-import org.mockito.MockitoAnnotations;
-
-import java.util.List;
@SmallTest
@RunWith(AndroidTestingRunner.class)
@RunWithLooper
-public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
- // Dependencies
- @Mock
- private UdfpsKeyguardView mView;
- @Mock
- private Context mResourceContext;
- @Mock
- private StatusBarStateController mStatusBarStateController;
- @Mock
- private ShadeExpansionStateManager mShadeExpansionStateManager;
- @Mock
- private StatusBarKeyguardViewManager mStatusBarKeyguardViewManager;
- @Mock
- private LockscreenShadeTransitionController mLockscreenShadeTransitionController;
- @Mock
- private DumpManager mDumpManager;
- @Mock
- private DelayableExecutor mExecutor;
- @Mock
- private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
- @Mock
- private KeyguardStateController mKeyguardStateController;
- @Mock
- private KeyguardViewMediator mKeyguardViewMediator;
- @Mock
- private ConfigurationController mConfigurationController;
- @Mock
- private UnlockedScreenOffAnimationController mUnlockedScreenOffAnimationController;
- @Mock
- private SystemUIDialogManager mDialogManager;
- @Mock
- private UdfpsController mUdfpsController;
- @Mock
- private ActivityLaunchAnimator mActivityLaunchAnimator;
- private FakeSystemClock mSystemClock = new FakeSystemClock();
-
- private UdfpsKeyguardViewController mController;
-
- // Capture listeners so that they can be used to send events
- @Captor private ArgumentCaptor<StatusBarStateController.StateListener> mStateListenerCaptor;
- private StatusBarStateController.StateListener mStatusBarStateListener;
-
- @Captor private ArgumentCaptor<ShadeExpansionListener> mExpansionListenerCaptor;
- private List<ShadeExpansionListener> mExpansionListeners;
-
- @Captor private ArgumentCaptor<StatusBarKeyguardViewManager.AlternateAuthInterceptor>
- mAltAuthInterceptorCaptor;
- private StatusBarKeyguardViewManager.AlternateAuthInterceptor mAltAuthInterceptor;
-
- @Captor private ArgumentCaptor<KeyguardStateController.Callback>
- mKeyguardStateControllerCallbackCaptor;
- private KeyguardStateController.Callback mKeyguardStateControllerCallback;
-
- @Before
- public void setUp() {
- MockitoAnnotations.initMocks(this);
- when(mView.getContext()).thenReturn(mResourceContext);
- when(mResourceContext.getString(anyInt())).thenReturn("test string");
- when(mKeyguardViewMediator.isAnimatingScreenOff()).thenReturn(false);
- when(mView.getUnpausedAlpha()).thenReturn(255);
- mController = new UdfpsKeyguardViewController(
- mView,
- mStatusBarStateController,
- mShadeExpansionStateManager,
- mStatusBarKeyguardViewManager,
- mKeyguardUpdateMonitor,
- mDumpManager,
- mLockscreenShadeTransitionController,
- mConfigurationController,
- mSystemClock,
- mKeyguardStateController,
- mUnlockedScreenOffAnimationController,
- mDialogManager,
- mUdfpsController,
- mActivityLaunchAnimator);
+public class UdfpsKeyguardViewControllerTest extends UdfpsKeyguardViewControllerBaseTest {
+ private @Captor ArgumentCaptor<KeyguardBouncer.BouncerExpansionCallback>
+ mBouncerExpansionCallbackCaptor;
+ private KeyguardBouncer.BouncerExpansionCallback mBouncerExpansionCallback;
+
+ @Override
+ public UdfpsKeyguardViewController createUdfpsKeyguardViewController() {
+ return createUdfpsKeyguardViewController(/* useModernBouncer */ false);
+ }
+
+ @Test
+ public void testShouldPauseAuth_bouncerShowing() {
+ mController.onViewAttached();
+ captureStatusBarStateListeners();
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD);
+
+ captureBouncerExpansionCallback();
+ when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
+ mBouncerExpansionCallback.onVisibilityChanged(true);
+
+ assertTrue(mController.shouldPauseAuth());
}
+
+
@Test
public void testRegistersExpansionChangedListenerOnAttached() {
mController.onViewAttached();
@@ -202,20 +129,6 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
}
@Test
- public void testShouldPauseAuthBouncerShowing() {
- mController.onViewAttached();
- captureStatusBarStateListeners();
- sendStatusBarStateChanged(StatusBarState.KEYGUARD);
-
- captureAltAuthInterceptor();
- when(mStatusBarKeyguardViewManager.isBouncerShowing()).thenReturn(true);
- when(mStatusBarKeyguardViewManager.bouncerIsOrWillBeShowing()).thenReturn(true);
- mAltAuthInterceptor.onBouncerVisibilityChanged();
-
- assertTrue(mController.shouldPauseAuth());
- }
-
- @Test
public void testShouldPauseAuthUnpausedAlpha0() {
mController.onViewAttached();
captureStatusBarStateListeners();
@@ -503,41 +416,8 @@ public class UdfpsKeyguardViewControllerTest extends SysuiTestCase {
verify(mView, atLeastOnce()).setPauseAuth(false);
}
- private void sendStatusBarStateChanged(int statusBarState) {
- mStatusBarStateListener.onStateChanged(statusBarState);
- }
-
- private void captureStatusBarStateListeners() {
- verify(mStatusBarStateController).addCallback(mStateListenerCaptor.capture());
- mStatusBarStateListener = mStateListenerCaptor.getValue();
- }
-
- private void captureStatusBarExpansionListeners() {
- verify(mShadeExpansionStateManager, times(2))
- .addExpansionListener(mExpansionListenerCaptor.capture());
- // first (index=0) is from super class, UdfpsAnimationViewController.
- // second (index=1) is from UdfpsKeyguardViewController
- mExpansionListeners = mExpansionListenerCaptor.getAllValues();
- }
-
- private void updateStatusBarExpansion(float fraction, boolean expanded) {
- ShadeExpansionChangeEvent event =
- new ShadeExpansionChangeEvent(
- fraction, expanded, /* tracking= */ false, /* dragDownPxAmount= */ 0f);
- for (ShadeExpansionListener listener : mExpansionListeners) {
- listener.onPanelExpansionChanged(event);
- }
- }
-
- private void captureAltAuthInterceptor() {
- verify(mStatusBarKeyguardViewManager).setAlternateAuthInterceptor(
- mAltAuthInterceptorCaptor.capture());
- mAltAuthInterceptor = mAltAuthInterceptorCaptor.getValue();
- }
-
- private void captureKeyguardStateControllerCallback() {
- verify(mKeyguardStateController).addCallback(
- mKeyguardStateControllerCallbackCaptor.capture());
- mKeyguardStateControllerCallback = mKeyguardStateControllerCallbackCaptor.getValue();
+ private void captureBouncerExpansionCallback() {
+ verify(mBouncer).addBouncerExpansionCallback(mBouncerExpansionCallbackCaptor.capture());
+ mBouncerExpansionCallback = mBouncerExpansionCallbackCaptor.getValue();
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
new file mode 100644
index 000000000000..7b1976811868
--- /dev/null
+++ b/packages/SystemUI/tests/src/com/android/systemui/biometrics/UdfpsKeyguardViewControllerWithCoroutinesTest.kt
@@ -0,0 +1,102 @@
+/*
+ * Copyright (C) 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.systemui.biometrics
+
+import android.os.Handler
+import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
+import androidx.test.filters.SmallTest
+import com.android.keyguard.KeyguardSecurityModel
+import com.android.systemui.classifier.FalsingCollector
+import com.android.systemui.keyguard.DismissCallbackRegistry
+import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
+import com.android.systemui.keyguard.domain.interactor.BouncerCallbackInteractor
+import com.android.systemui.keyguard.domain.interactor.BouncerInteractor
+import com.android.systemui.statusbar.StatusBarState
+import com.android.systemui.statusbar.phone.KeyguardBouncer
+import com.android.systemui.statusbar.phone.KeyguardBypassController
+import kotlinx.coroutines.Dispatchers
+import kotlinx.coroutines.runBlocking
+import kotlinx.coroutines.yield
+import org.junit.Assert.assertTrue
+import org.junit.Before
+import org.junit.Test
+import org.junit.runner.RunWith
+import org.mockito.Mockito.mock
+import org.mockito.MockitoAnnotations
+
+@RunWith(AndroidTestingRunner::class)
+@SmallTest
+@TestableLooper.RunWithLooper
+class UdfpsKeyguardViewControllerWithCoroutinesTest : UdfpsKeyguardViewControllerBaseTest() {
+ lateinit var keyguardBouncerRepository: KeyguardBouncerRepository
+
+ @Before
+ override fun setUp() {
+ allowTestableLooperAsMainThread() // repeatWhenAttached requires the main thread
+ MockitoAnnotations.initMocks(this)
+ keyguardBouncerRepository =
+ KeyguardBouncerRepository(
+ mock(com.android.keyguard.ViewMediatorCallback::class.java),
+ mKeyguardUpdateMonitor
+ )
+ super.setUp()
+ }
+
+ override fun createUdfpsKeyguardViewController(): UdfpsKeyguardViewController? {
+ mBouncerInteractor =
+ BouncerInteractor(
+ keyguardBouncerRepository,
+ mock(BouncerView::class.java),
+ mock(Handler::class.java),
+ mKeyguardStateController,
+ mock(KeyguardSecurityModel::class.java),
+ mock(BouncerCallbackInteractor::class.java),
+ mock(FalsingCollector::class.java),
+ mock(DismissCallbackRegistry::class.java),
+ mock(KeyguardBypassController::class.java),
+ mKeyguardUpdateMonitor
+ )
+ return createUdfpsKeyguardViewController(/* useModernBouncer */ true)
+ }
+
+ /** After migration, replaces LockIconViewControllerTest version */
+ @Test
+ fun testShouldPauseAuthBouncerShowing() =
+ runBlocking(IMMEDIATE) {
+ // GIVEN view attached and we're on the keyguard
+ mController.onViewAttached()
+ captureStatusBarStateListeners()
+ sendStatusBarStateChanged(StatusBarState.KEYGUARD)
+
+ // WHEN the bouncer expansion is VISIBLE
+ val job = mController.listenForBouncerExpansion(this)
+ keyguardBouncerRepository.setVisible(true)
+ keyguardBouncerRepository.setPanelExpansion(KeyguardBouncer.EXPANSION_VISIBLE)
+ yield()
+
+ // THEN UDFPS shouldPauseAuth == true
+ assertTrue(mController.shouldPauseAuth())
+
+ job.cancel()
+ }
+
+ companion object {
+ private val IMMEDIATE = Dispatchers.Main.immediate
+ }
+}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
index b9ab9d37a805..53d9b87b2346 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardRepositoryImplTest.kt
@@ -21,8 +21,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.common.shared.model.Position
import com.android.systemui.doze.DozeHost
import com.android.systemui.keyguard.WakefulnessLifecycle
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import com.android.systemui.plugins.statusbar.StatusBarStateController
+import com.android.systemui.statusbar.phone.BiometricUnlockController
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.mockito.argumentCaptor
import com.android.systemui.util.mockito.whenever
@@ -46,6 +48,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
@Mock private lateinit var dozeHost: DozeHost
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var wakefulnessLifecycle: WakefulnessLifecycle
+ @Mock private lateinit var biometricUnlockController: BiometricUnlockController
private lateinit var underTest: KeyguardRepositoryImpl
@@ -59,6 +62,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
keyguardStateController,
dozeHost,
wakefulnessLifecycle,
+ biometricUnlockController,
)
}
@@ -190,7 +194,7 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
}
@Test
- fun wakefullness() = runBlockingTest {
+ fun wakefulness() = runBlockingTest {
val values = mutableListOf<WakefulnessModel>()
val job = underTest.wakefulnessState.onEach(values::add).launchIn(this)
@@ -217,4 +221,63 @@ class KeyguardRepositoryImplTest : SysuiTestCase() {
job.cancel()
verify(wakefulnessLifecycle).removeObserver(captor.value)
}
+
+ @Test
+ fun isBouncerShowing() = runBlockingTest {
+ whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
+ var latest: Boolean? = null
+ val job = underTest.isBouncerShowing.onEach { latest = it }.launchIn(this)
+
+ assertThat(latest).isFalse()
+
+ val captor = argumentCaptor<KeyguardStateController.Callback>()
+ verify(keyguardStateController).addCallback(captor.capture())
+
+ whenever(keyguardStateController.isBouncerShowing).thenReturn(true)
+ captor.value.onBouncerShowingChanged()
+ assertThat(latest).isTrue()
+
+ whenever(keyguardStateController.isBouncerShowing).thenReturn(false)
+ captor.value.onBouncerShowingChanged()
+ assertThat(latest).isFalse()
+
+ job.cancel()
+ }
+
+ @Test
+ fun biometricUnlockState() = runBlockingTest {
+ val values = mutableListOf<BiometricUnlockModel>()
+ val job = underTest.biometricUnlockState.onEach(values::add).launchIn(this)
+
+ val captor = argumentCaptor<BiometricUnlockController.BiometricModeListener>()
+ verify(biometricUnlockController).addBiometricModeListener(captor.capture())
+
+ captor.value.onModeChanged(BiometricUnlockController.MODE_NONE)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_PULSING)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_SHOW_BOUNCER)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_ONLY_WAKE)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_UNLOCK_COLLAPSING)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_DISMISS_BOUNCER)
+ captor.value.onModeChanged(BiometricUnlockController.MODE_WAKE_AND_UNLOCK_FROM_DREAM)
+
+ assertThat(values)
+ .isEqualTo(
+ listOf(
+ // Initial value will be NONE, followed by onModeChanged() call
+ BiometricUnlockModel.NONE,
+ BiometricUnlockModel.NONE,
+ BiometricUnlockModel.WAKE_AND_UNLOCK,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_PULSING,
+ BiometricUnlockModel.SHOW_BOUNCER,
+ BiometricUnlockModel.ONLY_WAKE,
+ BiometricUnlockModel.UNLOCK_COLLAPSING,
+ BiometricUnlockModel.DISMISS_BOUNCER,
+ BiometricUnlockModel.WAKE_AND_UNLOCK_FROM_DREAM,
+ )
+ )
+
+ job.cancel()
+ verify(biometricUnlockController).removeBiometricModeListener(captor.value)
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
index 64913c7c28c2..27d5d0a98978 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/data/repository/KeyguardTransitionRepositoryTest.kt
@@ -128,11 +128,15 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
assertThat(steps.size).isEqualTo(3)
assertThat(steps[0])
- .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED))
+ .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME))
assertThat(steps[1])
- .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.RUNNING))
+ .isEqualTo(
+ TransitionStep(AOD, LOCKSCREEN, 0.5f, TransitionState.RUNNING, OWNER_NAME)
+ )
assertThat(steps[2])
- .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED))
+ .isEqualTo(
+ TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED, OWNER_NAME)
+ )
job.cancel()
}
@@ -174,15 +178,22 @@ class KeyguardTransitionRepositoryTest : SysuiTestCase() {
}
private fun assertSteps(steps: List<TransitionStep>, fractions: List<BigDecimal>) {
- assertThat(steps[0]).isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED))
+ assertThat(steps[0])
+ .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 0f, TransitionState.STARTED, OWNER_NAME))
fractions.forEachIndexed { index, fraction ->
assertThat(steps[index + 1])
.isEqualTo(
- TransitionStep(AOD, LOCKSCREEN, fraction.toFloat(), TransitionState.RUNNING)
+ TransitionStep(
+ AOD,
+ LOCKSCREEN,
+ fraction.toFloat(),
+ TransitionState.RUNNING,
+ OWNER_NAME
+ )
)
}
assertThat(steps[steps.size - 1])
- .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED))
+ .isEqualTo(TransitionStep(AOD, LOCKSCREEN, 1f, TransitionState.FINISHED, OWNER_NAME))
assertThat(wtfHandler.failed).isFalse()
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt
index e6c8dd87d982..5743b2f03d3a 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/BouncerInteractorTest.kt
@@ -27,8 +27,8 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.classifier.FalsingCollector
import com.android.systemui.keyguard.DismissCallbackRegistry
import com.android.systemui.keyguard.data.BouncerView
+import com.android.systemui.keyguard.data.BouncerViewDelegate
import com.android.systemui.keyguard.data.repository.KeyguardBouncerRepository
-import com.android.systemui.keyguard.shared.model.BouncerCallbackActionsModel
import com.android.systemui.keyguard.shared.model.BouncerShowMessageModel
import com.android.systemui.keyguard.shared.model.KeyguardBouncerModel
import com.android.systemui.plugins.ActivityStarter
@@ -57,6 +57,7 @@ class BouncerInteractorTest : SysuiTestCase() {
@Mock(answer = Answers.RETURNS_DEEP_STUBS)
private lateinit var repository: KeyguardBouncerRepository
@Mock(answer = Answers.RETURNS_DEEP_STUBS) private lateinit var bouncerView: BouncerView
+ @Mock private lateinit var bouncerViewDelegate: BouncerViewDelegate
@Mock private lateinit var keyguardStateController: KeyguardStateController
@Mock private lateinit var keyguardSecurityModel: KeyguardSecurityModel
@Mock private lateinit var bouncerCallbackInteractor: BouncerCallbackInteractor
@@ -86,6 +87,7 @@ class BouncerInteractorTest : SysuiTestCase() {
)
`when`(repository.startingDisappearAnimation.value).thenReturn(null)
`when`(repository.show.value).thenReturn(null)
+ `when`(bouncerView.delegate).thenReturn(bouncerViewDelegate)
}
@Test
@@ -97,7 +99,7 @@ class BouncerInteractorTest : SysuiTestCase() {
verify(repository).setHide(false)
verify(repository).setStartingToHide(false)
verify(repository).setScrimmed(true)
- verify(repository).setExpansion(EXPANSION_VISIBLE)
+ verify(repository).setPanelExpansion(EXPANSION_VISIBLE)
verify(repository).setShowingSoon(true)
verify(keyguardStateController).notifyBouncerShowing(true)
verify(bouncerCallbackInteractor).dispatchStartingToShow()
@@ -108,7 +110,7 @@ class BouncerInteractorTest : SysuiTestCase() {
@Test
fun testShow_isNotScrimmed() {
- verify(repository, never()).setExpansion(EXPANSION_VISIBLE)
+ verify(repository, never()).setPanelExpansion(EXPANSION_VISIBLE)
}
@Test
@@ -124,7 +126,6 @@ class BouncerInteractorTest : SysuiTestCase() {
verify(falsingCollector).onBouncerHidden()
verify(keyguardStateController).notifyBouncerShowing(false)
verify(repository).setShowingSoon(false)
- verify(repository).setOnDismissAction(null)
verify(repository).setVisible(false)
verify(repository).setHide(true)
verify(repository).setShow(null)
@@ -132,26 +133,26 @@ class BouncerInteractorTest : SysuiTestCase() {
@Test
fun testExpansion() {
- `when`(repository.expansionAmount.value).thenReturn(0.5f)
- bouncerInteractor.setExpansion(0.6f)
- verify(repository).setExpansion(0.6f)
+ `when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
+ bouncerInteractor.setPanelExpansion(0.6f)
+ verify(repository).setPanelExpansion(0.6f)
verify(bouncerCallbackInteractor).dispatchExpansionChanged(0.6f)
}
@Test
fun testExpansion_fullyShown() {
- `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ `when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
`when`(repository.startingDisappearAnimation.value).thenReturn(null)
- bouncerInteractor.setExpansion(EXPANSION_VISIBLE)
+ bouncerInteractor.setPanelExpansion(EXPANSION_VISIBLE)
verify(falsingCollector).onBouncerShown()
verify(bouncerCallbackInteractor).dispatchFullyShown()
}
@Test
fun testExpansion_fullyHidden() {
- `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ `when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
`when`(repository.startingDisappearAnimation.value).thenReturn(null)
- bouncerInteractor.setExpansion(EXPANSION_HIDDEN)
+ bouncerInteractor.setPanelExpansion(EXPANSION_HIDDEN)
verify(repository).setVisible(false)
verify(repository).setShow(null)
verify(falsingCollector).onBouncerHidden()
@@ -161,8 +162,8 @@ class BouncerInteractorTest : SysuiTestCase() {
@Test
fun testExpansion_startingToHide() {
- `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE)
- bouncerInteractor.setExpansion(0.1f)
+ `when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE)
+ bouncerInteractor.setPanelExpansion(0.1f)
verify(repository).setStartingToHide(true)
verify(bouncerCallbackInteractor).dispatchStartingToHide()
}
@@ -178,8 +179,7 @@ class BouncerInteractorTest : SysuiTestCase() {
val onDismissAction = mock(ActivityStarter.OnDismissAction::class.java)
val cancelAction = mock(Runnable::class.java)
bouncerInteractor.setDismissAction(onDismissAction, cancelAction)
- verify(repository)
- .setOnDismissAction(BouncerCallbackActionsModel(onDismissAction, cancelAction))
+ verify(bouncerViewDelegate).setDismissAction(onDismissAction, cancelAction)
}
@Test
@@ -234,7 +234,7 @@ class BouncerInteractorTest : SysuiTestCase() {
@Test
fun testIsFullShowing() {
`when`(repository.isVisible.value).thenReturn(true)
- `when`(repository.expansionAmount.value).thenReturn(EXPANSION_VISIBLE)
+ `when`(repository.panelExpansionAmount.value).thenReturn(EXPANSION_VISIBLE)
`when`(repository.startingDisappearAnimation.value).thenReturn(null)
assertThat(bouncerInteractor.isFullyShowing()).isTrue()
`when`(repository.isVisible.value).thenReturn(false)
@@ -255,7 +255,7 @@ class BouncerInteractorTest : SysuiTestCase() {
assertThat(bouncerInteractor.isInTransit()).isTrue()
`when`(repository.showingSoon.value).thenReturn(false)
assertThat(bouncerInteractor.isInTransit()).isFalse()
- `when`(repository.expansionAmount.value).thenReturn(0.5f)
+ `when`(repository.panelExpansionAmount.value).thenReturn(0.5f)
assertThat(bouncerInteractor.isInTransit()).isTrue()
}
@@ -269,10 +269,9 @@ class BouncerInteractorTest : SysuiTestCase() {
@Test
fun testWillDismissWithAction() {
- `when`(repository.onDismissAction.value?.onDismissAction)
- .thenReturn(mock(ActivityStarter.OnDismissAction::class.java))
+ `when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(true)
assertThat(bouncerInteractor.willDismissWithAction()).isTrue()
- `when`(repository.onDismissAction.value?.onDismissAction).thenReturn(null)
+ `when`(bouncerViewDelegate.willDismissWithActions()).thenReturn(false)
assertThat(bouncerInteractor.willDismissWithAction()).isFalse()
}
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
index 0424c28a1c24..6333b244fd38 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/keyguard/domain/interactor/KeyguardTransitionInteractorTest.kt
@@ -54,9 +54,11 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
@Test
fun `transition collectors receives only appropriate events`() =
runBlocking(IMMEDIATE) {
- var goneToAodSteps = mutableListOf<TransitionStep>()
+ var lockscreenToAodSteps = mutableListOf<TransitionStep>()
val job1 =
- underTest.goneToAodTransition.onEach { goneToAodSteps.add(it) }.launchIn(this)
+ underTest.lockscreenToAodTransition
+ .onEach { lockscreenToAodSteps.add(it) }
+ .launchIn(this)
var aodToLockscreenSteps = mutableListOf<TransitionStep>()
val job2 =
@@ -70,14 +72,14 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
- steps.add(TransitionStep(GONE, AOD, 0f, STARTED))
- steps.add(TransitionStep(GONE, AOD, 0.1f, RUNNING))
- steps.add(TransitionStep(GONE, AOD, 0.2f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.1f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.2f, RUNNING))
steps.forEach { repository.sendTransitionStep(it) }
assertThat(aodToLockscreenSteps).isEqualTo(steps.subList(2, 5))
- assertThat(goneToAodSteps).isEqualTo(steps.subList(5, 8))
+ assertThat(lockscreenToAodSteps).isEqualTo(steps.subList(5, 8))
job1.cancel()
job2.cancel()
@@ -119,10 +121,8 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
fun keyguardStateTests() =
runBlocking(IMMEDIATE) {
var finishedSteps = mutableListOf<KeyguardState>()
- val job1 =
+ val job =
underTest.finishedKeyguardState.onEach { finishedSteps.add(it) }.launchIn(this)
- var startedSteps = mutableListOf<KeyguardState>()
- val job2 = underTest.startedKeyguardState.onEach { startedSteps.add(it) }.launchIn(this)
val steps = mutableListOf<TransitionStep>()
@@ -137,10 +137,60 @@ class KeyguardTransitionInteractorTest : SysuiTestCase() {
steps.forEach { repository.sendTransitionStep(it) }
assertThat(finishedSteps).isEqualTo(listOf(LOCKSCREEN, AOD))
- assertThat(startedSteps).isEqualTo(listOf(LOCKSCREEN, AOD, GONE))
- job1.cancel()
- job2.cancel()
+ job.cancel()
+ }
+
+ @Test
+ fun finishedKeyguardTransitionStepTests() =
+ runBlocking(IMMEDIATE) {
+ var finishedSteps = mutableListOf<TransitionStep>()
+ val job =
+ underTest.finishedKeyguardTransitionStep
+ .onEach { finishedSteps.add(it) }
+ .launchIn(this)
+
+ val steps = mutableListOf<TransitionStep>()
+
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+
+ steps.forEach { repository.sendTransitionStep(it) }
+
+ assertThat(finishedSteps).isEqualTo(listOf(steps[2], steps[5]))
+
+ job.cancel()
+ }
+
+ @Test
+ fun startedKeyguardTransitionStepTests() =
+ runBlocking(IMMEDIATE) {
+ var startedSteps = mutableListOf<TransitionStep>()
+ val job =
+ underTest.startedKeyguardTransitionStep
+ .onEach { startedSteps.add(it) }
+ .launchIn(this)
+
+ val steps = mutableListOf<TransitionStep>()
+
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0f, STARTED))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 0.5f, RUNNING))
+ steps.add(TransitionStep(AOD, LOCKSCREEN, 1f, FINISHED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0f, STARTED))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 0.9f, RUNNING))
+ steps.add(TransitionStep(LOCKSCREEN, AOD, 1f, FINISHED))
+ steps.add(TransitionStep(AOD, GONE, 1f, STARTED))
+
+ steps.forEach { repository.sendTransitionStep(it) }
+
+ assertThat(startedSteps).isEqualTo(listOf(steps[0], steps[3], steps[6]))
+
+ job.cancel()
}
companion object {
diff --git a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
index 6adce7a827b6..c1fa9b39f50b 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/navigationbar/NavigationBarTest.java
@@ -61,6 +61,7 @@ import android.view.Display;
import android.view.DisplayInfo;
import android.view.MotionEvent;
import android.view.View;
+import android.view.ViewRootImpl;
import android.view.ViewTreeObserver;
import android.view.WindowInsets;
import android.view.WindowManager;
@@ -201,6 +202,8 @@ public class NavigationBarTest extends SysuiTestCase {
private WakefulnessLifecycle mWakefulnessLifecycle;
@Mock
private Resources mResources;
+ @Mock
+ private ViewRootImpl mViewRootImpl;
private FakeExecutor mFakeExecutor = new FakeExecutor(new FakeSystemClock());
private DeviceConfigProxyFake mDeviceConfigProxyFake = new DeviceConfigProxyFake();
@@ -227,6 +230,7 @@ public class NavigationBarTest extends SysuiTestCase {
when(mUserContextProvider.createCurrentUserContext(any(Context.class)))
.thenReturn(mContext);
when(mNavigationBarView.getResources()).thenReturn(mResources);
+ when(mNavigationBarView.getViewRootImpl()).thenReturn(mViewRootImpl);
setupSysuiDependency();
// This class inflates views that call Dependency.get, thus these injections are still
// necessary.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
index 2c76be64aa7c..b067ee76f3b3 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/qs/tileimpl/QSIconViewImplTest.java
@@ -24,6 +24,7 @@ import static org.mockito.Mockito.never;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
+import android.graphics.drawable.AnimatedVectorDrawable;
import android.graphics.drawable.Drawable;
import android.service.quicksettings.Tile;
import android.testing.AndroidTestingRunner;
@@ -136,6 +137,20 @@ public class QSIconViewImplTest extends SysuiTestCase {
assertEquals(mIconView.getColor(s1), mIconView.getColor(s2));
}
+ @Test
+ public void testIconNotAnimatedWhenAllowAnimationsFalse() {
+ ImageView iv = new ImageView(mContext);
+ AnimatedVectorDrawable d = mock(AnimatedVectorDrawable.class);
+ State s = new State();
+ s.icon = mock(Icon.class);
+ when(s.icon.getDrawable(any())).thenReturn(d);
+ when(s.icon.getInvisibleDrawable(any())).thenReturn(d);
+
+ mIconView.updateIcon(iv, s, false);
+
+ verify(d, never()).start();
+ }
+
private static Drawable.ConstantState fakeConstantState(Drawable otherDrawable) {
return new Drawable.ConstantState() {
@Override
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
index 45b4353d0ec0..c98c1f2bc97e 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerTest.java
@@ -486,6 +486,7 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
mSysUiState,
() -> mKeyguardBottomAreaViewController,
mKeyguardUnlockAnimationController,
+ mKeyguardIndicationController,
mNotificationListContainer,
mNotificationStackSizeCalculator,
mUnlockedScreenOffAnimationController,
@@ -499,8 +500,6 @@ public class NotificationPanelViewControllerTest extends SysuiTestCase {
() -> {},
mNotificationShelfController);
mNotificationPanelViewController.setHeadsUpManager(mHeadsUpManager);
- mNotificationPanelViewController.setKeyguardIndicationController(
- mKeyguardIndicationController);
ArgumentCaptor<View.OnAttachStateChangeListener> onAttachStateChangeListenerArgumentCaptor =
ArgumentCaptor.forClass(View.OnAttachStateChangeListener.class);
verify(mView, atLeast(1)).addOnAttachStateChangeListener(
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
index 26a0770a7bba..a4a7995ae3c8 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.java
@@ -152,7 +152,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we should intercept touch
@@ -165,7 +165,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
// WHEN not showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(false);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(false);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we shouldn't intercept touch
@@ -178,7 +178,7 @@ public class NotificationShadeWindowViewTest extends SysuiTestCase {
// WHEN showing alt auth, not dozing, drag down helper doesn't want to intercept
when(mStatusBarStateController.isDozing()).thenReturn(false);
- when(mStatusBarKeyguardViewManager.isShowingAlternateAuthOrAnimating()).thenReturn(true);
+ when(mStatusBarKeyguardViewManager.isShowingAlternateAuth()).thenReturn(true);
when(mDragDownHelper.onInterceptTouchEvent(any())).thenReturn(false);
// THEN we should handle the touch
diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
index 09add652483e..43c694245eba 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/shade/PulsingGestureListenerTest.kt
@@ -66,6 +66,8 @@ class PulsingGestureListenerTest : SysuiTestCase() {
private lateinit var dumpManager: DumpManager
@Mock
private lateinit var statusBarStateController: StatusBarStateController
+ @Mock
+ private lateinit var shadeLogger: ShadeLogger
private lateinit var tunableCaptor: ArgumentCaptor<Tunable>
private lateinit var underTest: PulsingGestureListener
@@ -81,6 +83,7 @@ class PulsingGestureListenerTest : SysuiTestCase() {
centralSurfaces,
ambientDisplayConfiguration,
statusBarStateController,
+ shadeLogger,
tunerService,
dumpManager
)
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
index 421f918a135e..7478e4c1f878 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/row/NotificationTestHelper.java
@@ -22,6 +22,7 @@ import static android.app.NotificationManager.IMPORTANCE_HIGH;
import static com.android.systemui.statusbar.NotificationEntryHelper.modifyRanking;
+import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertTrue;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyInt;
@@ -60,6 +61,7 @@ import com.android.systemui.statusbar.NotificationRemoteInputManager;
import com.android.systemui.statusbar.NotificationShadeWindowController;
import com.android.systemui.statusbar.SmartReplyController;
import com.android.systemui.statusbar.notification.ConversationNotificationProcessor;
+import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.collection.NotificationEntry;
import com.android.systemui.statusbar.notification.collection.NotificationEntryBuilder;
import com.android.systemui.statusbar.notification.collection.notifcollection.CommonNotifCollection;
@@ -194,6 +196,25 @@ public class NotificationTestHelper {
}
/**
+ * Creates a generic row with rounded border.
+ *
+ * @return a generic row with the set roundness.
+ * @throws Exception
+ */
+ public ExpandableNotificationRow createRowWithRoundness(
+ float topRoundness,
+ float bottomRoundness,
+ SourceType sourceType
+ ) throws Exception {
+ ExpandableNotificationRow row = createRow();
+ row.requestTopRoundness(topRoundness, false, sourceType);
+ row.requestBottomRoundness(bottomRoundness, /*animate = */ false, sourceType);
+ assertEquals(topRoundness, row.getTopRoundness(), /* delta = */ 0f);
+ assertEquals(bottomRoundness, row.getBottomRoundness(), /* delta = */ 0f);
+ return row;
+ }
+
+ /**
* Creates a generic row.
*
* @return a generic row with no special properties.
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
index 7c41abba6176..438b528944be 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationChildrenContainerTest.java
@@ -25,6 +25,7 @@ import android.view.View;
import androidx.test.filters.SmallTest;
import com.android.systemui.SysuiTestCase;
+import com.android.systemui.statusbar.notification.SourceType;
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow;
import com.android.systemui.statusbar.notification.row.NotificationTestHelper;
@@ -151,4 +152,37 @@ public class NotificationChildrenContainerTest extends SysuiTestCase {
Assert.assertNotNull("Children container must have a header after recreation",
mChildrenContainer.getCurrentHeaderView());
}
+
+ @Test
+ public void addNotification_shouldResetOnScrollRoundness() throws Exception {
+ ExpandableNotificationRow row = mNotificationTestHelper.createRowWithRoundness(
+ /* topRoundness = */ 1f,
+ /* bottomRoundness = */ 1f,
+ /* sourceType = */ SourceType.OnScroll);
+
+ mChildrenContainer.addNotification(row, 0);
+
+ Assert.assertEquals(0f, row.getTopRoundness(), /* delta = */ 0f);
+ Assert.assertEquals(0f, row.getBottomRoundness(), /* delta = */ 0f);
+ }
+
+ @Test
+ public void addNotification_shouldNotResetOtherRoundness() throws Exception {
+ ExpandableNotificationRow row1 = mNotificationTestHelper.createRowWithRoundness(
+ /* topRoundness = */ 1f,
+ /* bottomRoundness = */ 1f,
+ /* sourceType = */ SourceType.DefaultValue);
+ ExpandableNotificationRow row2 = mNotificationTestHelper.createRowWithRoundness(
+ /* topRoundness = */ 1f,
+ /* bottomRoundness = */ 1f,
+ /* sourceType = */ SourceType.OnDismissAnimation);
+
+ mChildrenContainer.addNotification(row1, 0);
+ mChildrenContainer.addNotification(row2, 0);
+
+ Assert.assertEquals(1f, row1.getTopRoundness(), /* delta = */ 0f);
+ Assert.assertEquals(1f, row1.getBottomRoundness(), /* delta = */ 0f);
+ Assert.assertEquals(1f, row2.getTopRoundness(), /* delta = */ 0f);
+ Assert.assertEquals(1f, row2.getBottomRoundness(), /* delta = */ 0f);
+ }
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
index 77418138158b..bda233611158 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/notification/stack/NotificationShelfTest.kt
@@ -1,6 +1,7 @@
package com.android.systemui.statusbar.notification.stack
import android.testing.AndroidTestingRunner
+import android.testing.TestableLooper
import android.testing.TestableLooper.RunWithLooper
import androidx.test.filters.SmallTest
import com.android.keyguard.BouncerPanelExpansionCalculator.aboutToShowBouncerProgress
@@ -8,8 +9,10 @@ import com.android.systemui.SysuiTestCase
import com.android.systemui.animation.ShadeInterpolation
import com.android.systemui.statusbar.NotificationShelf
import com.android.systemui.statusbar.StatusBarIconView
+import com.android.systemui.statusbar.notification.SourceType
import com.android.systemui.statusbar.notification.row.ExpandableNotificationRow
import com.android.systemui.statusbar.notification.row.ExpandableView
+import com.android.systemui.statusbar.notification.row.NotificationTestHelper
import com.android.systemui.statusbar.notification.stack.StackScrollAlgorithm.StackScrollAlgorithmState
import com.android.systemui.util.mockito.mock
import junit.framework.Assert.assertEquals
@@ -37,6 +40,13 @@ class NotificationShelfTest : SysuiTestCase() {
private val shelfState = shelf.viewState as NotificationShelf.ShelfState
private val ambientState = mock(AmbientState::class.java)
private val hostLayoutController: NotificationStackScrollLayoutController = mock()
+ private val notificationTestHelper by lazy {
+ allowTestableLooperAsMainThread()
+ NotificationTestHelper(
+ mContext,
+ mDependency,
+ TestableLooper.get(this))
+ }
@Before
fun setUp() {
@@ -299,6 +309,39 @@ class NotificationShelfTest : SysuiTestCase() {
)
}
+ @Test
+ fun resetOnScrollRoundness_shouldSetOnScrollTo0() {
+ val row: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
+ /* topRoundness = */ 1f,
+ /* bottomRoundness = */ 1f,
+ /* sourceType = */ SourceType.OnScroll)
+
+ NotificationShelf.resetOnScrollRoundness(row)
+
+ assertEquals(0f, row.topRoundness)
+ assertEquals(0f, row.bottomRoundness)
+ }
+
+ @Test
+ fun resetOnScrollRoundness_shouldNotResetOtherRoundness() {
+ val row1: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
+ /* topRoundness = */ 1f,
+ /* bottomRoundness = */ 1f,
+ /* sourceType = */ SourceType.DefaultValue)
+ val row2: ExpandableNotificationRow = notificationTestHelper.createRowWithRoundness(
+ /* topRoundness = */ 1f,
+ /* bottomRoundness = */ 1f,
+ /* sourceType = */ SourceType.OnDismissAnimation)
+
+ NotificationShelf.resetOnScrollRoundness(row1)
+ NotificationShelf.resetOnScrollRoundness(row2)
+
+ assertEquals(1f, row1.topRoundness)
+ assertEquals(1f, row1.bottomRoundness)
+ assertEquals(1f, row2.topRoundness)
+ assertEquals(1f, row2.bottomRoundness)
+ }
+
private fun setFractionToShade(fraction: Float) {
whenever(ambientState.fractionToShade).thenReturn(fraction)
}
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
index 6fa217415044..b17747ad2d92 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/BiometricsUnlockControllerTest.java
@@ -143,7 +143,7 @@ public class BiometricsUnlockControllerTest extends SysuiTestCase {
mAuthController, mStatusBarStateController, mKeyguardUnlockAnimationController,
mSessionTracker, mLatencyTracker, mScreenOffAnimationController, mVibratorHelper);
mBiometricUnlockController.setKeyguardViewController(mStatusBarKeyguardViewManager);
- mBiometricUnlockController.setBiometricModeListener(mBiometricModeListener);
+ mBiometricUnlockController.addBiometricModeListener(mBiometricModeListener);
}
@Test
diff --git a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
index 57557821cca6..7ce3a67ce835 100644
--- a/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
+++ b/packages/SystemUI/tests/src/com/android/systemui/statusbar/phone/CentralSurfacesImplTest.java
@@ -500,7 +500,7 @@ public class CentralSurfacesImplTest extends SysuiTestCase {
mKeyguardVieMediatorCallback);
// TODO: we should be able to call mCentralSurfaces.start() and have all the below values
- // initialized automatically.
+ // initialized automatically and make NPVC private.
mCentralSurfaces.mNotificationShadeWindowView = mNotificationShadeWindowView;
mCentralSurfaces.mNotificationPanelViewController = mNotificationPanelViewController;
mCentralSurfaces.mDozeScrimController = mDozeScrimController;
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 0c35659b458a..716666657871 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
@@ -307,7 +307,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void onPanelExpansionChanged_neverTranslatesBouncerWhenLaunchingApp() {
- when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
+ when(mNotificationPanelView.isLaunchTransitionFinished()).thenReturn(true);
mStatusBarKeyguardViewManager.onPanelExpansionChanged(
expansionEvent(
/* fraction= */ KeyguardBouncer.EXPANSION_VISIBLE,
@@ -361,7 +361,7 @@ public class StatusBarKeyguardViewManagerTest extends SysuiTestCase {
@Test
public void setOccluded_isInLaunchTransition_onKeyguardOccludedChangedCalled() {
- when(mCentralSurfaces.isInLaunchTransition()).thenReturn(true);
+ when(mNotificationPanelView.isLaunchTransitionFinished()).thenReturn(true);
mStatusBarKeyguardViewManager.show(null);
mStatusBarKeyguardViewManager.setOccluded(true /* occluded */, false /* animated */);
diff --git a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
index 11178db8afd2..627bd096143e 100644
--- a/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
+++ b/packages/SystemUI/tests/utils/src/com/android/systemui/keyguard/data/repository/FakeKeyguardRepository.kt
@@ -18,6 +18,7 @@
package com.android.systemui.keyguard.data.repository
import com.android.systemui.common.shared.model.Position
+import com.android.systemui.keyguard.shared.model.BiometricUnlockModel
import com.android.systemui.keyguard.shared.model.StatusBarState
import com.android.systemui.keyguard.shared.model.WakefulnessModel
import kotlinx.coroutines.flow.Flow
@@ -52,6 +53,12 @@ class FakeKeyguardRepository : KeyguardRepository {
private val _wakefulnessState = MutableStateFlow(WakefulnessModel.ASLEEP)
override val wakefulnessState: Flow<WakefulnessModel> = _wakefulnessState
+ private val _isBouncerShowing = MutableStateFlow(false)
+ override val isBouncerShowing: Flow<Boolean> = _isBouncerShowing
+
+ private val _biometricUnlockState = MutableStateFlow(BiometricUnlockModel.NONE)
+ override val biometricUnlockState: Flow<BiometricUnlockModel> = _biometricUnlockState
+
override fun isKeyguardShowing(): Boolean {
return _isKeyguardShowing.value
}
diff --git a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
index 252dcfc5eb9e..4430bb4b3292 100644
--- a/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
+++ b/services/appwidget/java/com/android/server/appwidget/AppWidgetServiceImpl.java
@@ -26,6 +26,7 @@ import static com.android.server.pm.PackageManagerService.PLATFORM_PACKAGE_NAME;
import android.annotation.NonNull;
import android.annotation.Nullable;
+import android.annotation.RequiresPermission;
import android.annotation.UserIdInt;
import android.app.ActivityManager;
import android.app.ActivityManagerInternal;
@@ -254,11 +255,13 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
private boolean mSafeMode;
private int mMaxWidgetBitmapMemory;
private boolean mIsProviderInfoPersisted;
+ private boolean mIsCombinedBroadcastEnabled;
AppWidgetServiceImpl(Context context) {
mContext = context;
}
+ @RequiresPermission(android.Manifest.permission.READ_DEVICE_CONFIG)
public void onStart() {
mPackageManager = AppGlobals.getPackageManager();
mAlarmManager = (AlarmManager) mContext.getSystemService(Context.ALARM_SERVICE);
@@ -277,6 +280,8 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
mIsProviderInfoPersisted = !ActivityManager.isLowRamDeviceStatic()
&& DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
SystemUiDeviceConfigFlags.PERSISTS_WIDGET_PROVIDER_INFO, true);
+ mIsCombinedBroadcastEnabled = DeviceConfig.getBoolean(NAMESPACE_SYSTEMUI,
+ SystemUiDeviceConfigFlags.COMBINED_BROADCAST_ENABLED, true);
if (DEBUG_PROVIDER_INFO_CACHE && !mIsProviderInfoPersisted) {
Slog.d(TAG, "App widget provider info will not be persisted on this device");
}
@@ -1123,16 +1128,16 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
final int widgetCount = provider.widgets.size();
if (widgetCount == 1) {
- // Tell the provider that it's ready.
- sendEnableIntentLocked(provider);
+ // If we are binding the very first widget from a provider, we will send
+ // a combined broadcast or 2 separate broadcasts to tell the provider that
+ // it's ready, and we need them to provide the update now.
+ sendEnableAndUpdateIntentLocked(provider, new int[]{appWidgetId});
+ } else {
+ // For any widget other then the first one, we just send update intent
+ // as we normally would.
+ sendUpdateIntentLocked(provider, new int[]{appWidgetId});
}
- // Send an update now -- We need this update now, and just for this appWidgetId.
- // It's less critical when the next one happens, so when we schedule the next one,
- // we add updatePeriodMillis to its start time. That time will have some slop,
- // but that's okay.
- sendUpdateIntentLocked(provider, new int[] {appWidgetId});
-
// Schedule the future updates.
registerForBroadcastsLocked(provider, getWidgetIds(provider.widgets));
@@ -2361,6 +2366,22 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
cancelBroadcastsLocked(provider);
}
+ private void sendEnableAndUpdateIntentLocked(@NonNull Provider p, int[] appWidgetIds) {
+ final boolean canSendCombinedBroadcast = mIsCombinedBroadcastEnabled && p.info != null
+ && p.info.isExtendedFromAppWidgetProvider;
+ if (!canSendCombinedBroadcast) {
+ // If this function is called by mistake, send two separate broadcasts instead
+ sendEnableIntentLocked(p);
+ sendUpdateIntentLocked(p, appWidgetIds);
+ return;
+ }
+
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLE_AND_UPDATE);
+ intent.putExtra(AppWidgetManager.EXTRA_APPWIDGET_IDS, appWidgetIds);
+ intent.setComponent(p.id.componentName);
+ sendBroadcastAsUser(intent, p.id.getProfile());
+ }
+
private void sendEnableIntentLocked(Provider p) {
Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLED);
intent.setComponent(p.id.componentName);
@@ -2852,7 +2873,6 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
if (provider.widgets.size() > 0) {
Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
"appwidget init " + provider.id.componentName.getPackageName());
- sendEnableIntentLocked(provider);
provider.widgets.forEach(widget -> {
widget.trackingUpdate = true;
Trace.asyncTraceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER,
@@ -2861,7 +2881,7 @@ class AppWidgetServiceImpl extends IAppWidgetService.Stub implements WidgetBacku
Log.i(TAG, "Widget update scheduled on unlock " + widget.toString());
});
int[] appWidgetIds = getWidgetIds(provider.widgets);
- sendUpdateIntentLocked(provider, appWidgetIds);
+ sendEnableAndUpdateIntentLocked(provider, appWidgetIds);
registerForBroadcastsLocked(provider, appWidgetIds);
Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
}
diff --git a/services/core/java/com/android/server/am/ActivityManagerService.java b/services/core/java/com/android/server/am/ActivityManagerService.java
index df5113b16f49..85d6f293852c 100644
--- a/services/core/java/com/android/server/am/ActivityManagerService.java
+++ b/services/core/java/com/android/server/am/ActivityManagerService.java
@@ -2886,10 +2886,13 @@ public class ActivityManagerService extends IActivityManager.Stub
|| event == Event.ACTIVITY_DESTROYED)) {
contentCaptureService.notifyActivityEvent(userId, activity, event);
}
- // TODO(b/201234353): Move the logic to client side.
- if (mVoiceInteractionManagerProvider != null && (event == Event.ACTIVITY_PAUSED
- || event == Event.ACTIVITY_RESUMED || event == Event.ACTIVITY_STOPPED)) {
- mVoiceInteractionManagerProvider.notifyActivityEventChanged();
+ // Currently we have move most of logic to the client side. When the activity lifecycle
+ // event changed, the client side will notify the VoiceInteractionManagerService. But
+ // when the application process died, the VoiceInteractionManagerService will miss the
+ // activity lifecycle event changed, so we still need ACTIVITY_DESTROYED event here to
+ // know if the activity has been destroyed.
+ if (mVoiceInteractionManagerProvider != null && event == Event.ACTIVITY_DESTROYED) {
+ mVoiceInteractionManagerProvider.notifyActivityDestroyed(appToken);
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
index ca4b7475f7dc..dbe971f32dc1 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/aidl/FaceAuthenticationClient.java
@@ -100,9 +100,7 @@ class FaceAuthenticationClient extends AuthenticationClient<AidlSession>
owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
isStrongBiometric, null /* taskStackListener */, lockoutCache,
allowBackgroundAuthentication,
- context.getResources().getBoolean(
- com.android.internal.R.bool.system_server_plays_face_haptics)
- /* shouldVibrate */,
+ false /* shouldVibrate */,
isKeyguardBypassEnabled);
setRequestId(requestId);
mUsageStats = usageStats;
diff --git a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
index 9baca98e4d2b..91eec7d19b3a 100644
--- a/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/face/hidl/FaceAuthenticationClient.java
@@ -74,7 +74,7 @@ class FaceAuthenticationClient extends AuthenticationClient<IBiometricsFace> {
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
isStrongBiometric, null /* taskStackListener */,
- lockoutTracker, allowBackgroundAuthentication, true /* shouldVibrate */,
+ lockoutTracker, allowBackgroundAuthentication, false /* shouldVibrate */,
isKeyguardBypassEnabled);
setRequestId(requestId);
mUsageStats = usageStats;
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
index a778b573f225..9aecf783a881 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClient.java
@@ -69,7 +69,6 @@ import java.util.function.Supplier;
class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
implements Udfps, LockoutConsumer, PowerPressHandler {
private static final String TAG = "FingerprintAuthenticationClient";
- private static final int MESSAGE_IGNORE_AUTH = 1;
private static final int MESSAGE_AUTH_SUCCESS = 2;
private static final int MESSAGE_FINGER_UP = 3;
@NonNull
@@ -136,7 +135,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
taskStackListener,
lockoutCache,
allowBackgroundAuthentication,
- true /* shouldVibrate */,
+ false /* shouldVibrate */,
false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutCache = lockoutCache;
@@ -235,12 +234,6 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
() -> {
long delay = 0;
if (authenticated && mSensorProps.isAnySidefpsType()) {
- if (mHandler.hasMessages(MESSAGE_IGNORE_AUTH)) {
- Slog.i(TAG, "(sideFPS) Ignoring auth due to recent power press");
- onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0,
- true);
- return;
- }
delay = isKeyguard() ? mWaitForAuthKeyguard : mWaitForAuthBp;
if (mSideFpsLastAcquireStartTime != -1) {
@@ -497,16 +490,12 @@ class FingerprintAuthenticationClient extends AuthenticationClient<AidlSession>
if (mSensorProps.isAnySidefpsType()) {
Slog.i(TAG, "(sideFPS): onPowerPressed");
mHandler.post(() -> {
- if (mHandler.hasMessages(MESSAGE_AUTH_SUCCESS)) {
- Slog.i(TAG, "(sideFPS): Ignoring auth in queue");
- mHandler.removeMessages(MESSAGE_AUTH_SUCCESS);
- // Do not call onError() as that will send an additional callback to coex.
- onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0, true);
- }
- mHandler.removeMessages(MESSAGE_IGNORE_AUTH);
- mHandler.postDelayed(() -> {
- }, MESSAGE_IGNORE_AUTH, mIgnoreAuthFor);
-
+ Slog.i(TAG, "(sideFPS): finishing auth");
+ // Ignore auths after a power has been detected
+ mHandler.removeMessages(MESSAGE_AUTH_SUCCESS);
+ // Do not call onError() as that will send an additional callback to coex.
+ onErrorInternal(BiometricConstants.BIOMETRIC_ERROR_POWER_PRESSED, 0, true);
+ mSensorOverlays.hide(getSensorId());
});
}
}
diff --git a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
index 7ed1a514f9bc..0d620fd3a9e4 100644
--- a/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
+++ b/services/core/java/com/android/server/biometrics/sensors/fingerprint/hidl/FingerprintAuthenticationClient.java
@@ -81,7 +81,7 @@ class FingerprintAuthenticationClient extends AuthenticationClient<IBiometricsFi
super(context, lazyDaemon, token, listener, targetUserId, operationId, restricted,
owner, cookie, requireConfirmation, sensorId, logger, biometricContext,
isStrongBiometric, taskStackListener, lockoutTracker, allowBackgroundAuthentication,
- true /* shouldVibrate */, false /* isKeyguardBypassEnabled */);
+ false /* shouldVibrate */, false /* isKeyguardBypassEnabled */);
setRequestId(requestId);
mLockoutFrameworkImpl = lockoutTracker;
mSensorOverlays = new SensorOverlays(udfpsOverlayController, sidefpsController);
diff --git a/services/core/java/com/android/server/dreams/DreamManagerService.java b/services/core/java/com/android/server/dreams/DreamManagerService.java
index 5589673973c3..e4aa5e574955 100644
--- a/services/core/java/com/android/server/dreams/DreamManagerService.java
+++ b/services/core/java/com/android/server/dreams/DreamManagerService.java
@@ -157,7 +157,7 @@ public final class DreamManagerService extends SystemService {
mDozeConfig = new AmbientDisplayConfiguration(mContext);
mUiEventLogger = new UiEventLoggerImpl();
mDreamUiEventLogger = new DreamUiEventLoggerImpl(
- mContext.getResources().getString(R.string.config_loggable_dream_prefix));
+ mContext.getResources().getStringArray(R.array.config_loggable_dream_prefixes));
AmbientDisplayConfiguration adc = new AmbientDisplayConfiguration(mContext);
mAmbientDisplayComponent = ComponentName.unflattenFromString(adc.ambientDisplayComponent());
mDreamsOnlyEnabledForSystemUser =
diff --git a/services/core/java/com/android/server/dreams/DreamUiEventLoggerImpl.java b/services/core/java/com/android/server/dreams/DreamUiEventLoggerImpl.java
index 26ca74a8d808..96ebcbb6a771 100644
--- a/services/core/java/com/android/server/dreams/DreamUiEventLoggerImpl.java
+++ b/services/core/java/com/android/server/dreams/DreamUiEventLoggerImpl.java
@@ -26,10 +26,10 @@ import com.android.internal.util.FrameworkStatsLog;
* @hide
*/
public class DreamUiEventLoggerImpl implements DreamUiEventLogger {
- final String mLoggableDreamPrefix;
+ private final String[] mLoggableDreamPrefixes;
- DreamUiEventLoggerImpl(String loggableDreamPrefix) {
- mLoggableDreamPrefix = loggableDreamPrefix;
+ DreamUiEventLoggerImpl(String[] loggableDreamPrefixes) {
+ mLoggableDreamPrefixes = loggableDreamPrefixes;
}
@Override
@@ -38,13 +38,20 @@ public class DreamUiEventLoggerImpl implements DreamUiEventLogger {
if (eventID <= 0) {
return;
}
- final boolean isFirstPartyDream =
- mLoggableDreamPrefix.isEmpty() ? false : dreamComponentName.startsWith(
- mLoggableDreamPrefix);
FrameworkStatsLog.write(FrameworkStatsLog.DREAM_UI_EVENT_REPORTED,
/* uid = 1 */ 0,
/* event_id = 2 */ eventID,
/* instance_id = 3 */ 0,
- /* dream_component_name = 4 */ isFirstPartyDream ? dreamComponentName : "other");
+ /* dream_component_name = 4 */
+ isFirstPartyDream(dreamComponentName) ? dreamComponentName : "other");
+ }
+
+ private boolean isFirstPartyDream(String dreamComponentName) {
+ for (int i = 0; i < mLoggableDreamPrefixes.length; ++i) {
+ if (dreamComponentName.startsWith(mLoggableDreamPrefixes[i])) {
+ return true;
+ }
+ }
+ return false;
}
}
diff --git a/services/core/java/com/android/server/wm/ActivityRecord.java b/services/core/java/com/android/server/wm/ActivityRecord.java
index 2eb2cf643c42..e86f34bb5d84 100644
--- a/services/core/java/com/android/server/wm/ActivityRecord.java
+++ b/services/core/java/com/android/server/wm/ActivityRecord.java
@@ -812,7 +812,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
StartingData mStartingData;
WindowState mStartingWindow;
StartingSurfaceController.StartingSurface mStartingSurface;
- boolean startingDisplayed;
boolean startingMoved;
/** The last set {@link DropInputMode} for this activity surface. */
@@ -821,13 +820,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Whether the input to this activity will be dropped during the current playing animation. */
private boolean mIsInputDroppedForAnimation;
- /**
- * If it is non-null, it requires all activities who have the same starting data to be drawn
- * to remove the starting window.
- * TODO(b/189385912): Remove starting window related fields after migrating them to task.
- */
- private StartingData mSharedStartingData;
-
boolean mHandleExitSplashScreen;
@TransferSplashScreenState
int mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
@@ -1200,14 +1192,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
pw.print(" firstWindowDrawn="); pw.print(firstWindowDrawn);
pw.print(" mIsExiting="); pw.println(mIsExiting);
}
- if (mSharedStartingData != null) {
- pw.println(prefix + "mSharedStartingData=" + mSharedStartingData);
- }
- if (mStartingWindow != null || mStartingSurface != null
- || startingDisplayed || startingMoved || mVisibleSetFromTransferredStartingWindow) {
+ if (mStartingWindow != null || mStartingData != null || mStartingSurface != null
+ || startingMoved || mVisibleSetFromTransferredStartingWindow) {
pw.print(prefix); pw.print("startingWindow="); pw.print(mStartingWindow);
pw.print(" startingSurface="); pw.print(mStartingSurface);
- pw.print(" startingDisplayed="); pw.print(startingDisplayed);
+ pw.print(" startingDisplayed="); pw.print(isStartingWindowDisplayed());
pw.print(" startingMoved="); pw.print(startingMoved);
pw.println(" mVisibleSetFromTransferredStartingWindow="
+ mVisibleSetFromTransferredStartingWindow);
@@ -2690,13 +2679,23 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
}
}
+ boolean isStartingWindowDisplayed() {
+ final StartingData data = mStartingData != null ? mStartingData : task != null
+ ? task.mSharedStartingData : null;
+ return data != null && data.mIsDisplayed;
+ }
+
/** Called when the starting window is added to this activity. */
void attachStartingWindow(@NonNull WindowState startingWindow) {
startingWindow.mStartingData = mStartingData;
mStartingWindow = startingWindow;
- // The snapshot type may have called associateStartingDataWithTask().
- if (mStartingData != null && mStartingData.mAssociatedTask != null) {
- attachStartingSurfaceToAssociatedTask();
+ if (mStartingData != null) {
+ if (mStartingData.mAssociatedTask != null) {
+ // The snapshot type may have called associateStartingDataWithTask().
+ attachStartingSurfaceToAssociatedTask();
+ } else if (isEmbedded()) {
+ associateStartingWindowWithTaskIfNeeded();
+ }
}
}
@@ -2711,11 +2710,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
/** Called when the starting window is not added yet but its data is known to fill the task. */
private void associateStartingDataWithTask() {
mStartingData.mAssociatedTask = task;
- task.forAllActivities(r -> {
- if (r.mVisibleRequested && !r.firstWindowDrawn) {
- r.mSharedStartingData = mStartingData;
- }
- });
+ task.mSharedStartingData = mStartingData;
}
/** Associates and attaches an added starting window to the current task. */
@@ -2746,10 +2741,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
void removeStartingWindowAnimation(boolean prepareAnimation) {
mTransferringSplashScreenState = TRANSFER_SPLASH_SCREEN_IDLE;
- if (mSharedStartingData != null) {
- mSharedStartingData.mAssociatedTask.forAllActivities(r -> {
- r.mSharedStartingData = null;
- });
+ if (task != null) {
+ task.mSharedStartingData = null;
}
if (mStartingWindow == null) {
if (mStartingData != null) {
@@ -2772,7 +2765,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
mStartingData = null;
mStartingSurface = null;
mStartingWindow = null;
- startingDisplayed = false;
if (surface == null) {
ProtoLog.v(WM_DEBUG_STARTING_WINDOW, "startingWindow was set but "
+ "startingSurface==null, couldn't remove");
@@ -4257,7 +4249,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
* @return {@code true} if starting window is in app's hierarchy.
*/
boolean hasStartingWindow() {
- if (startingDisplayed || mStartingData != null) {
+ if (mStartingData != null) {
return true;
}
for (int i = mChildren.size() - 1; i >= 0; i--) {
@@ -4355,10 +4347,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Transfer the starting window over to the new token.
mStartingData = fromActivity.mStartingData;
- mSharedStartingData = fromActivity.mSharedStartingData;
mStartingSurface = fromActivity.mStartingSurface;
- startingDisplayed = fromActivity.startingDisplayed;
- fromActivity.startingDisplayed = false;
mStartingWindow = tStartingWindow;
reportedVisible = fromActivity.reportedVisible;
fromActivity.mStartingData = null;
@@ -4424,7 +4413,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_STARTING_WINDOW,
"Moving pending starting from %s to %s", fromActivity, this);
mStartingData = fromActivity.mStartingData;
- mSharedStartingData = fromActivity.mSharedStartingData;
fromActivity.mStartingData = null;
fromActivity.startingMoved = true;
scheduleAddStartingWindow();
@@ -6534,14 +6522,11 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
// Remove starting window directly if is in a pure task. Otherwise if it is associated with
// a task (e.g. nested task fragment), then remove only if all visible windows in the task
// are drawn.
- final Task associatedTask =
- mSharedStartingData != null ? mSharedStartingData.mAssociatedTask : null;
+ final Task associatedTask = task.mSharedStartingData != null ? task : null;
if (associatedTask == null) {
removeStartingWindow();
- } else if (associatedTask.getActivity(r -> r.mVisibleRequested && !r.firstWindowDrawn
- // Don't block starting window removal if an Activity can't be a starting window
- // target.
- && r.mSharedStartingData != null) == null) {
+ } else if (associatedTask.getActivity(
+ r -> r.mVisibleRequested && !r.firstWindowDrawn) == null) {
// The last drawn activity may not be the one that owns the starting window.
final ActivityRecord r = associatedTask.topActivityContainsStartingWindow();
if (r != null) {
@@ -6756,7 +6741,6 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mLastTransactionSequence != mWmService.mTransactionSequence) {
mLastTransactionSequence = mWmService.mTransactionSequence;
mNumDrawnWindows = 0;
- startingDisplayed = false;
// There is the main base application window, even if it is exiting, wait for it
mNumInterestingWindows = findMainWindow(false /* includeStartingApp */) != null ? 1 : 0;
@@ -6800,9 +6784,9 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
isInterestingAndDrawn = true;
}
}
- } else if (w.isDrawn()) {
+ } else if (mStartingData != null && w.isDrawn()) {
// The starting window for this container is drawn.
- startingDisplayed = true;
+ mStartingData.mIsDisplayed = true;
}
}
@@ -7550,7 +7534,8 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
ProtoLog.v(WM_DEBUG_ANIM, "Animation done in %s"
+ ": reportedVisible=%b okToDisplay=%b okToAnimate=%b startingDisplayed=%b",
- this, reportedVisible, okToDisplay(), okToAnimate(), startingDisplayed);
+ this, reportedVisible, okToDisplay(), okToAnimate(),
+ isStartingWindowDisplayed());
// clean up thumbnail window
if (mThumbnail != null) {
@@ -9649,7 +9634,7 @@ final class ActivityRecord extends WindowToken implements WindowManagerService.A
if (mStartingWindow != null) {
mStartingWindow.writeIdentifierToProto(proto, STARTING_WINDOW);
}
- proto.write(STARTING_DISPLAYED, startingDisplayed);
+ proto.write(STARTING_DISPLAYED, isStartingWindowDisplayed());
proto.write(STARTING_MOVED, startingMoved);
proto.write(VISIBLE_SET_FROM_TRANSFERRED_STARTING_WINDOW,
mVisibleSetFromTransferredStartingWindow);
diff --git a/services/core/java/com/android/server/wm/AppTransitionController.java b/services/core/java/com/android/server/wm/AppTransitionController.java
index 9c95e31cc5f5..671524b39bc1 100644
--- a/services/core/java/com/android/server/wm/AppTransitionController.java
+++ b/services/core/java/com/android/server/wm/AppTransitionController.java
@@ -1229,11 +1229,11 @@ public class AppTransitionController {
ProtoLog.v(WM_DEBUG_APP_TRANSITIONS,
"Check opening app=%s: allDrawn=%b startingDisplayed=%b "
+ "startingMoved=%b isRelaunching()=%b startingWindow=%s",
- activity, activity.allDrawn, activity.startingDisplayed,
+ activity, activity.allDrawn, activity.isStartingWindowDisplayed(),
activity.startingMoved, activity.isRelaunching(),
activity.mStartingWindow);
final boolean allDrawn = activity.allDrawn && !activity.isRelaunching();
- if (!allDrawn && !activity.startingDisplayed && !activity.startingMoved) {
+ if (!allDrawn && !activity.isStartingWindowDisplayed() && !activity.startingMoved) {
return false;
}
if (allDrawn) {
diff --git a/services/core/java/com/android/server/wm/Dimmer.java b/services/core/java/com/android/server/wm/Dimmer.java
index e7ab63eab202..13a1cb6daf38 100644
--- a/services/core/java/com/android/server/wm/Dimmer.java
+++ b/services/core/java/com/android/server/wm/Dimmer.java
@@ -216,14 +216,10 @@ class Dimmer {
return;
}
- if (container != null) {
- // The dim method is called from WindowState.prepareSurfaces(), which is always called
- // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
- // relative to the highest Z layer with a dim.
- t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
- } else {
- t.setLayer(d.mDimLayer, Integer.MAX_VALUE);
- }
+ // The dim method is called from WindowState.prepareSurfaces(), which is always called
+ // in the correct Z from lowest Z to highest. This ensures that the dim layer is always
+ // relative to the highest Z layer with a dim.
+ t.setRelativeLayer(d.mDimLayer, container.getSurfaceControl(), relativeLayer);
t.setAlpha(d.mDimLayer, alpha);
t.setBackgroundBlurRadius(d.mDimLayer, blurRadius);
@@ -231,32 +227,6 @@ class Dimmer {
}
/**
- * Finish a dim started by dimAbove in the case there was no call to dimAbove.
- *
- * @param t A Transaction in which to finish the dim.
- */
- void stopDim(SurfaceControl.Transaction t) {
- if (mDimState != null) {
- t.hide(mDimState.mDimLayer);
- mDimState.isVisible = false;
- mDimState.mDontReset = false;
- }
- }
-
- /**
- * Place a Dim above the entire host container. The caller is responsible for calling stopDim to
- * remove this effect. If the Dim can be assosciated with a particular child of the host
- * consider using the other variant of dimAbove which ties the Dim lifetime to the child
- * lifetime more explicitly.
- *
- * @param t A transaction in which to apply the Dim.
- * @param alpha The alpha at which to Dim.
- */
- void dimAbove(SurfaceControl.Transaction t, float alpha) {
- dim(t, null, 1, alpha, 0);
- }
-
- /**
* Place a dim above the given container, which should be a child of the host container.
* for each call to {@link WindowContainer#prepareSurfaces} the Dim state will be reset
* and the child should call dimAbove again to request the Dim to continue.
diff --git a/services/core/java/com/android/server/wm/DisplayPolicy.java b/services/core/java/com/android/server/wm/DisplayPolicy.java
index 42a3ec6abbc5..2688ff757f64 100644
--- a/services/core/java/com/android/server/wm/DisplayPolicy.java
+++ b/services/core/java/com/android/server/wm/DisplayPolicy.java
@@ -81,11 +81,7 @@ import static android.window.DisplayAreaOrganizer.FEATURE_UNDEFINED;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_ANIM;
import static com.android.internal.protolog.ProtoLogGroup.WM_DEBUG_SCREEN_ON;
import static com.android.server.policy.PhoneWindowManager.TOAST_WINDOW_TIMEOUT;
-import static com.android.server.policy.WindowManagerPolicy.TRANSIT_ENTER;
-import static com.android.server.policy.WindowManagerPolicy.TRANSIT_EXIT;
-import static com.android.server.policy.WindowManagerPolicy.TRANSIT_HIDE;
import static com.android.server.policy.WindowManagerPolicy.TRANSIT_PREVIEW_DONE;
-import static com.android.server.policy.WindowManagerPolicy.TRANSIT_SHOW;
import static com.android.server.policy.WindowManagerPolicy.WindowManagerFuncs.LID_ABSENT;
import static com.android.server.wm.WindowManagerDebugConfig.DEBUG_LAYOUT;
import static com.android.server.wm.WindowManagerDebugConfig.TAG_WITH_CLASS_NAME;
@@ -1439,90 +1435,6 @@ public class DisplayPolicy {
*/
int selectAnimation(WindowState win, int transit) {
ProtoLog.i(WM_DEBUG_ANIM, "selectAnimation in %s: transit=%d", win, transit);
- if (win == mStatusBar) {
- if (transit == TRANSIT_EXIT
- || transit == TRANSIT_HIDE) {
- return R.anim.dock_top_exit;
- } else if (transit == TRANSIT_ENTER
- || transit == TRANSIT_SHOW) {
- return R.anim.dock_top_enter;
- }
- } else if (win == mNavigationBar) {
- if (win.getAttrs().windowAnimations != 0) {
- return ANIMATION_STYLEABLE;
- }
- // This can be on either the bottom or the right or the left.
- if (mNavigationBarPosition == NAV_BAR_BOTTOM) {
- if (transit == TRANSIT_EXIT
- || transit == TRANSIT_HIDE) {
- if (mService.mPolicy.isKeyguardShowingAndNotOccluded()) {
- return R.anim.dock_bottom_exit_keyguard;
- } else {
- return R.anim.dock_bottom_exit;
- }
- } else if (transit == TRANSIT_ENTER
- || transit == TRANSIT_SHOW) {
- return R.anim.dock_bottom_enter;
- }
- } else if (mNavigationBarPosition == NAV_BAR_RIGHT) {
- if (transit == TRANSIT_EXIT
- || transit == TRANSIT_HIDE) {
- return R.anim.dock_right_exit;
- } else if (transit == TRANSIT_ENTER
- || transit == TRANSIT_SHOW) {
- return R.anim.dock_right_enter;
- }
- } else if (mNavigationBarPosition == NAV_BAR_LEFT) {
- if (transit == TRANSIT_EXIT
- || transit == TRANSIT_HIDE) {
- return R.anim.dock_left_exit;
- } else if (transit == TRANSIT_ENTER
- || transit == TRANSIT_SHOW) {
- return R.anim.dock_left_enter;
- }
- }
- } else if (win == mStatusBarAlt || win == mNavigationBarAlt || win == mClimateBarAlt
- || win == mExtraNavBarAlt) {
- if (win.getAttrs().windowAnimations != 0) {
- return ANIMATION_STYLEABLE;
- }
-
- int pos = (win == mStatusBarAlt) ? mStatusBarAltPosition : mNavigationBarAltPosition;
-
- boolean isExitOrHide = transit == TRANSIT_EXIT || transit == TRANSIT_HIDE;
- boolean isEnterOrShow = transit == TRANSIT_ENTER || transit == TRANSIT_SHOW;
-
- switch (pos) {
- case ALT_BAR_LEFT:
- if (isExitOrHide) {
- return R.anim.dock_left_exit;
- } else if (isEnterOrShow) {
- return R.anim.dock_left_enter;
- }
- break;
- case ALT_BAR_RIGHT:
- if (isExitOrHide) {
- return R.anim.dock_right_exit;
- } else if (isEnterOrShow) {
- return R.anim.dock_right_enter;
- }
- break;
- case ALT_BAR_BOTTOM:
- if (isExitOrHide) {
- return R.anim.dock_bottom_exit;
- } else if (isEnterOrShow) {
- return R.anim.dock_bottom_enter;
- }
- break;
- case ALT_BAR_TOP:
- if (isExitOrHide) {
- return R.anim.dock_top_exit;
- } else if (isEnterOrShow) {
- return R.anim.dock_top_enter;
- }
- break;
- }
- }
if (transit == TRANSIT_PREVIEW_DONE) {
if (win.hasAppShownWindows()) {
diff --git a/services/core/java/com/android/server/wm/DisplayRotation.java b/services/core/java/com/android/server/wm/DisplayRotation.java
index a8d13c57ffa2..eaa08fd5eb0b 100644
--- a/services/core/java/com/android/server/wm/DisplayRotation.java
+++ b/services/core/java/com/android/server/wm/DisplayRotation.java
@@ -1578,7 +1578,9 @@ public class DisplayRotation {
false /* forceRelayout */);
} else {
// Revert the rotation to our saved value if we transition from HALF_FOLDED.
- mRotation = mHalfFoldSavedRotation;
+ if (mHalfFoldSavedRotation != -1) {
+ mRotation = mHalfFoldSavedRotation;
+ }
// Tell the device to update its orientation (mFoldState is still HALF_FOLDED here
// so we will override USER_ROTATION_LOCKED and allow a rotation).
mService.updateRotation(false /* alwaysSendConfiguration */,
diff --git a/services/core/java/com/android/server/wm/LockTaskController.java b/services/core/java/com/android/server/wm/LockTaskController.java
index f11c2a7da840..dcb7fe3fbc8b 100644
--- a/services/core/java/com/android/server/wm/LockTaskController.java
+++ b/services/core/java/com/android/server/wm/LockTaskController.java
@@ -604,7 +604,10 @@ public class LockTaskController {
getDevicePolicyManager().notifyLockTaskModeChanged(false, null, userId);
}
if (oldLockTaskModeState == LOCK_TASK_MODE_PINNED) {
- getStatusBarService().showPinningEnterExitToast(false /* entering */);
+ final IStatusBarService statusBarService = getStatusBarService();
+ if (statusBarService != null) {
+ statusBarService.showPinningEnterExitToast(false /* entering */);
+ }
}
mWindowManager.onLockTaskStateChanged(mLockTaskModeState);
} catch (RemoteException ex) {
@@ -619,7 +622,10 @@ public class LockTaskController {
void showLockTaskToast() {
if (mLockTaskModeState == LOCK_TASK_MODE_PINNED) {
try {
- getStatusBarService().showPinningEscapeToast();
+ final IStatusBarService statusBarService = getStatusBarService();
+ if (statusBarService != null) {
+ statusBarService.showPinningEscapeToast();
+ }
} catch (RemoteException e) {
Slog.e(TAG, "Failed to send pinning escape toast", e);
}
@@ -727,7 +733,10 @@ public class LockTaskController {
// When lock task starts, we disable the status bars.
try {
if (lockTaskModeState == LOCK_TASK_MODE_PINNED) {
- getStatusBarService().showPinningEnterExitToast(true /* entering */);
+ final IStatusBarService statusBarService = getStatusBarService();
+ if (statusBarService != null) {
+ statusBarService.showPinningEnterExitToast(true /* entering */);
+ }
}
mWindowManager.onLockTaskStateChanged(lockTaskModeState);
mLockTaskModeState = lockTaskModeState;
diff --git a/services/core/java/com/android/server/wm/StartingData.java b/services/core/java/com/android/server/wm/StartingData.java
index fbee343f09b5..300a894d15c9 100644
--- a/services/core/java/com/android/server/wm/StartingData.java
+++ b/services/core/java/com/android/server/wm/StartingData.java
@@ -38,6 +38,9 @@ public abstract class StartingData {
*/
Task mAssociatedTask;
+ /** Whether the starting window is drawn. */
+ boolean mIsDisplayed;
+
protected StartingData(WindowManagerService service, int typeParams) {
mService = service;
mTypeParams = typeParams;
diff --git a/services/core/java/com/android/server/wm/Task.java b/services/core/java/com/android/server/wm/Task.java
index eba49bbc7301..66d7af9b4f6c 100644
--- a/services/core/java/com/android/server/wm/Task.java
+++ b/services/core/java/com/android/server/wm/Task.java
@@ -358,6 +358,13 @@ class Task extends TaskFragment {
int mLockTaskUid = -1; // The uid of the application that called startLockTask().
+ /**
+ * If non-null, the starting window should cover the associated task. It is assigned when the
+ * parent activity of starting window is put in a partial area of the task. This field will be
+ * cleared when all visible activities in this task are drawn.
+ */
+ StartingData mSharedStartingData;
+
/** The process that had previously hosted the root activity of this task.
* Used to know that we should try harder to keep this process around, in case the
* user wants to return to it. */
@@ -3688,6 +3695,9 @@ class Task extends TaskFragment {
if (mRootProcess != null) {
pw.print(prefix); pw.print("mRootProcess="); pw.println(mRootProcess);
}
+ if (mSharedStartingData != null) {
+ pw.println(prefix + "mSharedStartingData=" + mSharedStartingData);
+ }
pw.print(prefix); pw.print("taskId=" + mTaskId);
pw.println(" rootTaskId=" + getRootTaskId());
pw.print(prefix); pw.println("hasChildPipActivity=" + (mChildPipActivity != null));
diff --git a/services/core/java/com/android/server/wm/TaskFragment.java b/services/core/java/com/android/server/wm/TaskFragment.java
index d17867673474..879c7e2d75dc 100644
--- a/services/core/java/com/android/server/wm/TaskFragment.java
+++ b/services/core/java/com/android/server/wm/TaskFragment.java
@@ -1890,10 +1890,10 @@ class TaskFragment extends WindowContainer<WindowContainer> {
RemoteAnimationTarget createRemoteAnimationTarget(
RemoteAnimationController.RemoteAnimationRecord record) {
final ActivityRecord activity = record.getMode() == RemoteAnimationTarget.MODE_OPENING
- // There may be a trampoline activity without window on top of the existing task
- // which is moving to front. Exclude the finishing activity so the window of next
- // activity can be chosen to create the animation target.
- ? getTopNonFinishingActivity()
+ // There may be a launching (e.g. trampoline or embedded) activity without a window
+ // on top of the existing task which is moving to front. Exclude finishing activity
+ // so the window of next activity can be chosen to create the animation target.
+ ? getActivity(r -> !r.finishing && r.hasChild())
: getTopMostActivity();
return activity != null ? activity.createRemoteAnimationTarget(record) : null;
}
diff --git a/services/core/java/com/android/server/wm/WindowManagerService.java b/services/core/java/com/android/server/wm/WindowManagerService.java
index ac720be90563..9c9d751244e6 100644
--- a/services/core/java/com/android/server/wm/WindowManagerService.java
+++ b/services/core/java/com/android/server/wm/WindowManagerService.java
@@ -8684,11 +8684,12 @@ public class WindowManagerService extends IWindowManager.Stub
h.ownerPid = callingPid;
if (region == null) {
- h.replaceTouchableRegionWithCrop = true;
+ h.replaceTouchableRegionWithCrop(null);
} else {
h.touchableRegion.set(region);
+ h.replaceTouchableRegionWithCrop = false;
+ h.setTouchableRegionCrop(surface);
}
- h.setTouchableRegionCrop(null /* use the input surface's bounds */);
final SurfaceControl.Transaction t = mTransactionFactory.get();
t.setInputWindowInfo(surface, h);
diff --git a/services/core/java/com/android/server/wm/WindowState.java b/services/core/java/com/android/server/wm/WindowState.java
index 744bf0aa42d9..d4c1abfa8d24 100644
--- a/services/core/java/com/android/server/wm/WindowState.java
+++ b/services/core/java/com/android/server/wm/WindowState.java
@@ -1842,8 +1842,8 @@ class WindowState extends WindowContainer<WindowState> implements WindowManagerP
* @return {@code true} if one or more windows have been displayed, else false.
*/
boolean hasAppShownWindows() {
- return mActivityRecord != null
- && (mActivityRecord.firstWindowDrawn || mActivityRecord.startingDisplayed);
+ return mActivityRecord != null && (mActivityRecord.firstWindowDrawn
+ || mActivityRecord.isStartingWindowDisplayed());
}
@Override
diff --git a/services/core/java/com/android/server/wm/WindowStateAnimator.java b/services/core/java/com/android/server/wm/WindowStateAnimator.java
index 5c0557f2d1f4..a0ba8fda906f 100644
--- a/services/core/java/com/android/server/wm/WindowStateAnimator.java
+++ b/services/core/java/com/android/server/wm/WindowStateAnimator.java
@@ -374,13 +374,6 @@ class WindowStateAnimator {
}
void destroySurfaceLocked(SurfaceControl.Transaction t) {
- final ActivityRecord activity = mWin.mActivityRecord;
- if (activity != null) {
- if (mWin == activity.mStartingWindow) {
- activity.startingDisplayed = false;
- }
- }
-
if (mSurfaceController == null) {
return;
}
@@ -602,11 +595,17 @@ class WindowStateAnimator {
return true;
}
- final boolean isImeWindow = mWin.mAttrs.type == TYPE_INPUT_METHOD;
- if (isEntrance && isImeWindow) {
+ if (mWin.mAttrs.type == TYPE_INPUT_METHOD) {
mWin.getDisplayContent().adjustForImeIfNeeded();
- mWin.setDisplayLayoutNeeded();
- mService.mWindowPlacerLocked.requestTraversal();
+ if (isEntrance) {
+ mWin.setDisplayLayoutNeeded();
+ mService.mWindowPlacerLocked.requestTraversal();
+ }
+ }
+
+ if (mWin.mControllableInsetProvider != null) {
+ // All our animations should be driven by the insets control target.
+ return false;
}
// Only apply an animation if the display isn't frozen. If it is
@@ -654,14 +653,10 @@ class WindowStateAnimator {
Trace.traceEnd(Trace.TRACE_TAG_WINDOW_MANAGER);
mAnimationIsEntrance = isEntrance;
}
- } else if (!isImeWindow) {
+ } else {
mWin.cancelAnimation();
}
- if (!isEntrance && isImeWindow) {
- mWin.getDisplayContent().adjustForImeIfNeeded();
- }
-
return mWin.isAnimating(0 /* flags */, ANIMATION_TYPE_WINDOW_ANIMATION);
}
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
index 7610b7ca5ec3..b33e22fe6d1e 100644
--- a/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
+++ b/services/tests/servicestests/src/com/android/server/appwidget/AppWidgetServiceImplTest.java
@@ -164,7 +164,7 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
}
public void testRequestPinAppWidget() {
- ComponentName provider = new ComponentName(mTestContext, DummyAppWidget.class);
+ ComponentName provider = new ComponentName(mTestContext, TestAppWidgetProvider.class);
// Set up users.
when(mMockShortcutService.requestPinAppWidget(anyString(),
any(AppWidgetProviderInfo.class), eq(null), eq(null), anyInt()))
@@ -289,6 +289,16 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
assertEquals(4, updates.size());
}
+ public void testReceiveBroadcastBehavior_enableAndUpdate() {
+ TestAppWidgetProvider testAppWidgetProvider = new TestAppWidgetProvider();
+ Intent intent = new Intent(AppWidgetManager.ACTION_APPWIDGET_ENABLE_AND_UPDATE);
+
+ testAppWidgetProvider.onReceive(mTestContext, intent);
+
+ assertTrue(testAppWidgetProvider.isBehaviorSuccess());
+ }
+
+
public void testUpdatesReceived_queueNotEmpty_multipleWidgetIdProvided() {
int widgetId = setupHostAndWidget();
int widgetId2 = bindNewWidget();
@@ -385,7 +395,7 @@ public class AppWidgetServiceImplTest extends InstrumentationTestCase {
}
private int bindNewWidget() {
- ComponentName provider = new ComponentName(mTestContext, DummyAppWidget.class);
+ ComponentName provider = new ComponentName(mTestContext, TestAppWidgetProvider.class);
int widgetId = mService.allocateAppWidgetId(mPkgName, HOST_ID);
assertTrue(mManager.bindAppWidgetIdIfAllowed(widgetId, provider));
assertEquals(provider, mManager.getAppWidgetInfo(widgetId).provider);
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java b/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java
deleted file mode 100644
index fd99b21c9538..000000000000
--- a/services/tests/servicestests/src/com/android/server/appwidget/DummyAppWidget.java
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- * Copyright (C) 2017 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License
- */
-
-package com.android.server.appwidget;
-
-import android.content.BroadcastReceiver;
-import android.content.Context;
-import android.content.Intent;
-
-/**
- * Placeholder widget for testing
- */
-public class DummyAppWidget extends BroadcastReceiver {
-
- @Override
- public void onReceive(Context context, Intent intent) {
- }
-}
diff --git a/services/tests/servicestests/src/com/android/server/appwidget/TestAppWidgetProvider.java b/services/tests/servicestests/src/com/android/server/appwidget/TestAppWidgetProvider.java
new file mode 100644
index 000000000000..6c11a6829c12
--- /dev/null
+++ b/services/tests/servicestests/src/com/android/server/appwidget/TestAppWidgetProvider.java
@@ -0,0 +1,50 @@
+/*
+ * Copyright 2022 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package com.android.server.appwidget;
+
+import android.appwidget.AppWidgetManager;
+import android.appwidget.AppWidgetProvider;
+import android.content.Context;
+
+/**
+ * Placeholder widget for testing
+ */
+public class TestAppWidgetProvider extends AppWidgetProvider {
+ private boolean mEnabled;
+ private boolean mUpdated;
+
+ TestAppWidgetProvider() {
+ super();
+ mEnabled = false;
+ mUpdated = false;
+ }
+
+ public boolean isBehaviorSuccess() {
+ return mEnabled && mUpdated;
+ }
+
+ @Override
+ public void onUpdate(Context context, AppWidgetManager appWidgetManager,
+ int[] appWidgetids) {
+ mUpdated = true;
+ }
+
+ @Override
+ public void onEnabled(Context context) {
+ mEnabled = true;
+ }
+}
diff --git a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
index 606f4864643c..cd4af0a581f8 100644
--- a/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
+++ b/services/tests/servicestests/src/com/android/server/biometrics/sensors/fingerprint/aidl/FingerprintAuthenticationClientTest.java
@@ -616,6 +616,20 @@ public class FingerprintAuthenticationClientTest {
verify(mCallback).onClientFinished(any(), eq(true));
}
+ @Test
+ public void sideFpsPowerPressCancelsIsntantly() throws Exception {
+ when(mSensorProps.isAnySidefpsType()).thenReturn(true);
+
+ final FingerprintAuthenticationClient client = createClient(1);
+ client.start(mCallback);
+
+ client.onPowerPressed();
+ mLooper.dispatchAll();
+
+ verify(mCallback, never()).onClientFinished(any(), eq(true));
+ verify(mCallback).onClientFinished(any(), eq(false));
+ }
+
private FingerprintAuthenticationClient createClient() throws RemoteException {
return createClient(100 /* version */, true /* allowBackgroundAuthentication */);
}
diff --git a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
index 462957a88a6c..8a0a4f7c82b7 100644
--- a/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/ActivityRecordTests.java
@@ -2886,6 +2886,7 @@ public class ActivityRecordTests extends WindowTestsBase {
fragmentSetup.accept(taskFragment1, new Rect(0, 0, width / 2, height));
task.addChild(taskFragment1, POSITION_TOP);
assertEquals(task, activity1.mStartingData.mAssociatedTask);
+ assertEquals(activity1.mStartingData, task.mSharedStartingData);
final TaskFragment taskFragment2 = new TaskFragment(
mAtm, null /* fragmentToken */, false /* createdByOrganizer */);
@@ -2905,7 +2906,6 @@ public class ActivityRecordTests extends WindowTestsBase {
verify(activity1.getSyncTransaction()).reparent(eq(startingWindow.mSurfaceControl),
eq(task.mSurfaceControl));
- assertEquals(activity1.mStartingData, startingWindow.mStartingData);
assertEquals(task.mSurfaceControl, startingWindow.getAnimationLeashParent());
assertEquals(taskFragment1.getBounds(), activity1.getBounds());
// The activity was resized by task fragment, but starting window must still cover the task.
@@ -2916,6 +2916,7 @@ public class ActivityRecordTests extends WindowTestsBase {
activity1.onFirstWindowDrawn(activityWindow);
activity2.onFirstWindowDrawn(activityWindow);
assertNull(activity1.mStartingWindow);
+ assertNull(task.mSharedStartingData);
}
@Test
@@ -2991,10 +2992,10 @@ public class ActivityRecordTests extends WindowTestsBase {
final WindowManager.LayoutParams attrs =
new WindowManager.LayoutParams(TYPE_APPLICATION_STARTING);
final TestWindowState startingWindow = createWindowState(attrs, activity);
- activity.startingDisplayed = true;
+ activity.mStartingData = mock(StartingData.class);
activity.addWindow(startingWindow);
assertTrue("Starting window should be present", activity.hasStartingWindow());
- activity.startingDisplayed = false;
+ activity.mStartingData = null;
assertTrue("Starting window should be present", activity.hasStartingWindow());
activity.removeChild(startingWindow);
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
index 513791d2b8a5..0332c4b4597b 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionControllerTest.java
@@ -1300,6 +1300,8 @@ public class AppTransitionControllerTest extends WindowTestsBase {
activity.allDrawn = true;
// Skip manipulate the SurfaceControl.
doNothing().when(activity).setDropInputMode(anyInt());
+ // Assume the activity contains a window.
+ doReturn(true).when(activity).hasChild();
// Make sure activity can create remote animation target.
doReturn(mock(RemoteAnimationTarget.class)).when(activity).createRemoteAnimationTarget(
any());
diff --git a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
index f61effa284ef..d55e53cca8a6 100644
--- a/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/AppTransitionTests.java
@@ -321,7 +321,6 @@ public class AppTransitionTests extends WindowTestsBase {
final ActivityRecord activity2 = createActivityRecord(dc2);
activity1.allDrawn = true;
- activity1.startingDisplayed = true;
activity1.startingMoved = true;
// Simulate activity resume / finish flows to prepare app transition & set visibility,
diff --git a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
index 55a7c1ba0bca..befe4e85a7de 100644
--- a/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
+++ b/services/tests/wmtests/src/com/android/server/wm/DimmerTests.java
@@ -24,7 +24,6 @@ import static com.android.dx.mockito.inline.extended.ExtendedMockito.times;
import static com.android.dx.mockito.inline.extended.ExtendedMockito.verify;
import static com.android.server.wm.SurfaceAnimator.ANIMATION_TYPE_DIMMER;
-import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNotNull;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyBoolean;
@@ -139,34 +138,12 @@ public class DimmerTests extends WindowTestsBase {
}
@Test
- public void testDimAboveNoChildCreatesSurface() {
- final float alpha = 0.8f;
- mDimmer.dimAbove(mTransaction, alpha);
-
- SurfaceControl dimLayer = getDimLayer();
-
- assertNotNull("Dimmer should have created a surface", dimLayer);
-
- verify(mTransaction).setAlpha(dimLayer, alpha);
- verify(mTransaction).setLayer(dimLayer, Integer.MAX_VALUE);
- }
-
- @Test
- public void testDimAboveNoChildRedundantlyUpdatesAlphaOnExistingSurface() {
- float alpha = 0.8f;
- mDimmer.dimAbove(mTransaction, alpha);
- final SurfaceControl firstSurface = getDimLayer();
-
- alpha = 0.9f;
- mDimmer.dimAbove(mTransaction, alpha);
-
- assertEquals(firstSurface, getDimLayer());
- verify(mTransaction).setAlpha(firstSurface, 0.9f);
- }
-
- @Test
public void testUpdateDimsAppliesCrop() {
- mDimmer.dimAbove(mTransaction, 0.8f);
+ TestWindowContainer child = new TestWindowContainer(mWm);
+ mHost.addChild(child, 0);
+
+ final float alpha = 0.8f;
+ mDimmer.dimAbove(mTransaction, child, alpha);
int width = 100;
int height = 300;
@@ -178,17 +155,6 @@ public class DimmerTests extends WindowTestsBase {
}
@Test
- public void testDimAboveNoChildNotReset() {
- mDimmer.dimAbove(mTransaction, 0.8f);
- SurfaceControl dimLayer = getDimLayer();
- mDimmer.resetDimStates();
-
- mDimmer.updateDims(mTransaction, new Rect());
- verify(mTransaction).show(getDimLayer());
- verify(mTransaction, never()).remove(dimLayer);
- }
-
- @Test
public void testDimAboveWithChildCreatesSurfaceAboveChild() {
TestWindowContainer child = new TestWindowContainer(mWm);
mHost.addChild(child, 0);
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
index 352d8d115b6a..bc5c9ec7a29c 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerService.java
@@ -172,11 +172,11 @@ public class VoiceInteractionManagerService extends SystemService {
mAmInternal.setVoiceInteractionManagerProvider(
new ActivityManagerInternal.VoiceInteractionManagerProvider() {
@Override
- public void notifyActivityEventChanged() {
+ public void notifyActivityDestroyed(IBinder activityToken) {
if (DEBUG) {
- Slog.d(TAG, "call notifyActivityEventChanged");
+ Slog.d(TAG, "notifyActivityDestroyed activityToken=" + activityToken);
}
- mServiceStub.notifyActivityEventChanged();
+ mServiceStub.notifyActivityDestroyed(activityToken);
}
});
}
@@ -447,11 +447,12 @@ public class VoiceInteractionManagerService extends SystemService {
return mImpl.supportsLocalVoiceInteraction();
}
- void notifyActivityEventChanged() {
+ void notifyActivityDestroyed(@NonNull IBinder activityToken) {
synchronized (this) {
- if (mImpl == null) return;
+ if (mImpl == null || activityToken == null) return;
- Binder.withCleanCallingIdentity(() -> mImpl.notifyActivityEventChangedLocked());
+ Binder.withCleanCallingIdentity(
+ () -> mImpl.notifyActivityDestroyedLocked(activityToken));
}
}
@@ -1223,6 +1224,16 @@ public class VoiceInteractionManagerService extends SystemService {
}
}
+ @Override
+ public void notifyActivityEventChanged(@NonNull IBinder activityToken, int type) {
+ synchronized (this) {
+ if (mImpl == null || activityToken == null) {
+ return;
+ }
+ Binder.withCleanCallingIdentity(
+ () -> mImpl.notifyActivityEventChangedLocked(activityToken, type));
+ }
+ }
//----------------- Hotword Detection/Validation APIs --------------------------------//
@Override
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
index b9793cafb6fe..fabab2524e83 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionManagerServiceImpl.java
@@ -520,9 +520,23 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
mActiveSession.stopListeningVisibleActivityChangedLocked();
}
- public void notifyActivityEventChangedLocked() {
+ public void notifyActivityDestroyedLocked(@NonNull IBinder activityToken) {
if (DEBUG) {
- Slog.d(TAG, "notifyActivityEventChangedLocked");
+ Slog.d(TAG, "notifyActivityDestroyedLocked activityToken=" + activityToken);
+ }
+ if (mActiveSession == null || !mActiveSession.mShown) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityDestroyedLocked not allowed on no session or"
+ + " hidden session");
+ }
+ return;
+ }
+ mActiveSession.notifyActivityDestroyedLocked(activityToken);
+ }
+
+ public void notifyActivityEventChangedLocked(@NonNull IBinder activityToken, int type) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityEventChangedLocked type=" + type);
}
if (mActiveSession == null || !mActiveSession.mShown) {
if (DEBUG) {
@@ -531,7 +545,7 @@ class VoiceInteractionManagerServiceImpl implements VoiceInteractionSessionConne
}
return;
}
- mActiveSession.notifyActivityEventChangedLocked();
+ mActiveSession.notifyActivityEventChangedLocked(activityToken, type);
}
public void updateStateLocked(
diff --git a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
index ae9be8c09e60..b24337fd54c9 100644
--- a/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
+++ b/services/voiceinteraction/java/com/android/server/voiceinteraction/VoiceInteractionSessionConnection.java
@@ -29,6 +29,7 @@ import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_DATA;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_KEY_STRUCTURE;
import static com.android.server.wm.ActivityTaskManagerInternal.ASSIST_TASK_ID;
+import android.annotation.NonNull;
import android.app.ActivityManager;
import android.app.ActivityTaskManager;
import android.app.AppOpsManager;
@@ -59,6 +60,7 @@ import android.service.voice.IVoiceInteractionSessionService;
import android.service.voice.VisibleActivityInfo;
import android.service.voice.VoiceInteractionService;
import android.service.voice.VoiceInteractionSession;
+import android.util.ArrayMap;
import android.util.Slog;
import android.view.IWindowManager;
@@ -128,7 +130,11 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
private boolean mListeningVisibleActivity;
private final ScheduledExecutorService mScheduledExecutorService =
Executors.newSingleThreadScheduledExecutor();
- private final List<VisibleActivityInfo> mVisibleActivityInfos = new ArrayList<>();
+ // Records the visible activity information the system has already called onVisible, without
+ // confirming the result of callback. When activity visible state is changed, we use this to
+ // determine to call onVisible or onInvisible to assistant application.
+ private final ArrayMap<IBinder, VisibleActivityInfo> mVisibleActivityInfoForToken =
+ new ArrayMap<>();
private final PowerManagerInternal mPowerManagerInternal;
private final LowPowerStandbyControllerInternal mLowPowerStandbyControllerInternal;
private final Runnable mRemoveFromLowPowerStandbyAllowlistRunnable =
@@ -530,7 +536,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
public void cancelLocked(boolean finishTask) {
mListeningVisibleActivity = false;
- mVisibleActivityInfos.clear();
+ mVisibleActivityInfoForToken.clear();
hideLocked();
mCanceled = true;
if (mBound) {
@@ -608,17 +614,24 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
if (DEBUG) {
Slog.d(TAG, "startListeningVisibleActivityChangedLocked");
}
+
+ if (!mShown || mCanceled || mSession == null) {
+ return;
+ }
+
mListeningVisibleActivity = true;
- mVisibleActivityInfos.clear();
+ mVisibleActivityInfoForToken.clear();
- mScheduledExecutorService.execute(() -> {
- if (DEBUG) {
- Slog.d(TAG, "call handleVisibleActivitiesLocked from enable listening");
- }
- synchronized (mLock) {
- handleVisibleActivitiesLocked();
- }
- });
+ // It should only need to report which activities are visible
+ final ArrayMap<IBinder, VisibleActivityInfo> newVisibleActivityInfos =
+ getTopVisibleActivityInfosLocked();
+
+ if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) {
+ return;
+ }
+ notifyVisibleActivitiesChangedLocked(newVisibleActivityInfos,
+ VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
+ mVisibleActivityInfoForToken.putAll(newVisibleActivityInfos);
}
void stopListeningVisibleActivityChangedLocked() {
@@ -626,12 +639,13 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
Slog.d(TAG, "stopListeningVisibleActivityChangedLocked");
}
mListeningVisibleActivity = false;
- mVisibleActivityInfos.clear();
+ mVisibleActivityInfoForToken.clear();
}
- void notifyActivityEventChangedLocked() {
+ void notifyActivityEventChangedLocked(@NonNull IBinder activityToken, int type) {
if (DEBUG) {
- Slog.d(TAG, "notifyActivityEventChangedLocked");
+ Slog.d(TAG, "notifyActivityEventChangedLocked activityToken=" + activityToken
+ + ", type=" + type);
}
if (!mListeningVisibleActivity) {
if (DEBUG) {
@@ -640,99 +654,139 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
return;
}
mScheduledExecutorService.execute(() -> {
- if (DEBUG) {
- Slog.d(TAG, "call handleVisibleActivitiesLocked from activity event");
- }
synchronized (mLock) {
- handleVisibleActivitiesLocked();
+ handleVisibleActivitiesLocked(activityToken, type);
}
});
}
- private List<VisibleActivityInfo> getVisibleActivityInfosLocked() {
+ private ArrayMap<IBinder, VisibleActivityInfo> getTopVisibleActivityInfosLocked() {
if (DEBUG) {
- Slog.d(TAG, "getVisibleActivityInfosLocked");
+ Slog.d(TAG, "getTopVisibleActivityInfosLocked");
}
List<ActivityAssistInfo> allVisibleActivities =
LocalServices.getService(ActivityTaskManagerInternal.class)
.getTopVisibleActivities();
if (DEBUG) {
- Slog.d(TAG,
- "getVisibleActivityInfosLocked: allVisibleActivities=" + allVisibleActivities);
+ Slog.d(TAG, "getTopVisibleActivityInfosLocked: allVisibleActivities="
+ + allVisibleActivities);
}
- if (allVisibleActivities == null || allVisibleActivities.isEmpty()) {
+ if (allVisibleActivities.isEmpty()) {
Slog.w(TAG, "no visible activity");
return null;
}
final int count = allVisibleActivities.size();
- final List<VisibleActivityInfo> visibleActivityInfos = new ArrayList<>(count);
+ final ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfoArrayMap =
+ new ArrayMap<>(count);
for (int i = 0; i < count; i++) {
ActivityAssistInfo info = allVisibleActivities.get(i);
if (DEBUG) {
- Slog.d(TAG, " : activityToken=" + info.getActivityToken()
+ Slog.d(TAG, "ActivityAssistInfo : activityToken=" + info.getActivityToken()
+ ", assistToken=" + info.getAssistToken()
+ ", taskId=" + info.getTaskId());
}
- visibleActivityInfos.add(
+ visibleActivityInfoArrayMap.put(info.getActivityToken(),
new VisibleActivityInfo(info.getTaskId(), info.getAssistToken()));
}
- return visibleActivityInfos;
+ return visibleActivityInfoArrayMap;
}
- private void handleVisibleActivitiesLocked() {
+ // TODO(b/242359988): Split this method up
+ private void handleVisibleActivitiesLocked(@NonNull IBinder activityToken, int type) {
if (DEBUG) {
- Slog.d(TAG, "handleVisibleActivitiesLocked");
+ Slog.d(TAG, "handleVisibleActivitiesLocked activityToken=" + activityToken
+ + ", type=" + type);
}
- if (mSession == null) {
+
+ if (!mListeningVisibleActivity) {
+ if (DEBUG) {
+ Slog.d(TAG, "not enable listening visible activity");
+ }
return;
}
- if (!mShown || !mListeningVisibleActivity || mCanceled) {
+ if (!mShown || mCanceled || mSession == null) {
return;
}
- final List<VisibleActivityInfo> newVisibleActivityInfos = getVisibleActivityInfosLocked();
- if (newVisibleActivityInfos == null || newVisibleActivityInfos.isEmpty()) {
- notifyVisibleActivitiesChangedLocked(mVisibleActivityInfos,
- VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
- mVisibleActivityInfos.clear();
- return;
- }
- if (mVisibleActivityInfos.isEmpty()) {
- notifyVisibleActivitiesChangedLocked(newVisibleActivityInfos,
- VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
- mVisibleActivityInfos.addAll(newVisibleActivityInfos);
+ // We use this local variable to determine to call onVisible or onInvisible.
+ boolean notifyOnVisible = false;
+ VisibleActivityInfo notifyVisibleActivityInfo = null;
+
+ if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_START
+ || type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_RESUME) {
+ // It seems that the onStart is unnecessary. But if we have it, the assistant
+ // application can request the directActions early. Even if we have the onStart,
+ // we still need the onResume because it is possible that the activity goes to
+ // onResume from onPause with invisible before the activity goes to onStop from
+ // onPause.
+
+ // Check if we have reported this activity as visible. If we have reported it as
+ // visible, do nothing.
+ if (mVisibleActivityInfoForToken.containsKey(activityToken)) {
+ return;
+ }
+
+ // Before reporting this activity as visible, we need to make sure the activity
+ // is really visible.
+ notifyVisibleActivityInfo = getVisibleActivityInfoFromTopVisibleActivity(
+ activityToken);
+ if (notifyVisibleActivityInfo == null) {
+ return;
+ }
+ notifyOnVisible = true;
+ } else if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_PAUSE) {
+ // For the onPause stage, the Activity is not necessarily invisible now, so we need
+ // to check its state.
+ // Note: After syncing with Activity owner, before the onPause is called, the
+ // visibility state has been updated.
+ notifyVisibleActivityInfo = getVisibleActivityInfoFromTopVisibleActivity(
+ activityToken);
+ if (notifyVisibleActivityInfo != null) {
+ return;
+ }
+
+ // Also make sure we previously reported this Activity as visible.
+ notifyVisibleActivityInfo = mVisibleActivityInfoForToken.get(activityToken);
+ if (notifyVisibleActivityInfo == null) {
+ return;
+ }
+ } else if (type == VoiceInteractionSession.VOICE_INTERACTION_ACTIVITY_EVENT_STOP) {
+ // For the onStop stage, the activity is in invisible state. We only need to consider if
+ // we have reported this activity as visible. If we have reported it as visible, we
+ // need to report it as invisible.
+ // Why we still need onStop? Because it is possible that the activity is in a visible
+ // state during onPause stage, when the activity enters onStop from onPause, we may
+ // need to notify onInvisible.
+ // Note: After syncing with Activity owner, before the onStop is called, the
+ // visibility state has been updated.
+ notifyVisibleActivityInfo = mVisibleActivityInfoForToken.get(activityToken);
+ if (notifyVisibleActivityInfo == null) {
+ return;
+ }
+ } else {
+ Slog.w(TAG, "notifyActivityEventChangedLocked unexpected type=" + type);
return;
}
- final List<VisibleActivityInfo> addedActivities = new ArrayList<>();
- final List<VisibleActivityInfo> removedActivities = new ArrayList<>();
-
- removedActivities.addAll(mVisibleActivityInfos);
- for (int i = 0; i < newVisibleActivityInfos.size(); i++) {
- final VisibleActivityInfo candidateVisibleActivityInfo = newVisibleActivityInfos.get(i);
- if (!removedActivities.isEmpty() && removedActivities.contains(
- candidateVisibleActivityInfo)) {
- removedActivities.remove(candidateVisibleActivityInfo);
- } else {
- addedActivities.add(candidateVisibleActivityInfo);
+ try {
+ mSession.notifyVisibleActivityInfoChanged(notifyVisibleActivityInfo,
+ notifyOnVisible ? VisibleActivityInfo.TYPE_ACTIVITY_ADDED
+ : VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "handleVisibleActivitiesLocked RemoteException : " + e);
}
}
- if (!addedActivities.isEmpty()) {
- notifyVisibleActivitiesChangedLocked(addedActivities,
- VisibleActivityInfo.TYPE_ACTIVITY_ADDED);
- }
- if (!removedActivities.isEmpty()) {
- notifyVisibleActivitiesChangedLocked(removedActivities,
- VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
+ if (notifyOnVisible) {
+ mVisibleActivityInfoForToken.put(activityToken, notifyVisibleActivityInfo);
+ } else {
+ mVisibleActivityInfoForToken.remove(activityToken);
}
-
- mVisibleActivityInfos.clear();
- mVisibleActivityInfos.addAll(newVisibleActivityInfos);
}
private void notifyVisibleActivitiesChangedLocked(
- List<VisibleActivityInfo> visibleActivityInfos, int type) {
+ ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfos, int type) {
if (visibleActivityInfos == null || visibleActivityInfos.isEmpty()) {
return;
}
@@ -741,7 +795,7 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
}
try {
for (int i = 0; i < visibleActivityInfos.size(); i++) {
- mSession.notifyVisibleActivityInfoChanged(visibleActivityInfos.get(i), type);
+ mSession.notifyVisibleActivityInfoChanged(visibleActivityInfos.valueAt(i), type);
}
} catch (RemoteException e) {
if (DEBUG) {
@@ -754,6 +808,51 @@ final class VoiceInteractionSessionConnection implements ServiceConnection,
}
}
+ private VisibleActivityInfo getVisibleActivityInfoFromTopVisibleActivity(
+ @NonNull IBinder activityToken) {
+ final ArrayMap<IBinder, VisibleActivityInfo> visibleActivityInfos =
+ getTopVisibleActivityInfosLocked();
+ if (visibleActivityInfos == null) {
+ return null;
+ }
+ return visibleActivityInfos.get(activityToken);
+ }
+
+ void notifyActivityDestroyedLocked(@NonNull IBinder activityToken) {
+ if (DEBUG) {
+ Slog.d(TAG, "notifyActivityDestroyedLocked activityToken=" + activityToken);
+ }
+ if (!mListeningVisibleActivity) {
+ if (DEBUG) {
+ Slog.d(TAG, "not enable listening visible activity");
+ }
+ return;
+ }
+ mScheduledExecutorService.execute(() -> {
+ synchronized (mLock) {
+ if (!mListeningVisibleActivity) {
+ return;
+ }
+ if (!mShown || mCanceled || mSession == null) {
+ return;
+ }
+
+ VisibleActivityInfo visibleActivityInfo = mVisibleActivityInfoForToken.remove(
+ activityToken);
+ if (visibleActivityInfo != null) {
+ try {
+ mSession.notifyVisibleActivityInfoChanged(visibleActivityInfo,
+ VisibleActivityInfo.TYPE_ACTIVITY_REMOVED);
+ } catch (RemoteException e) {
+ if (DEBUG) {
+ Slog.w(TAG, "notifyVisibleActivityInfoChanged RemoteException : " + e);
+ }
+ }
+ }
+ }
+ });
+ }
+
private void removeFromLowPowerStandbyAllowlist() {
synchronized (mLock) {
if (mLowPowerStandbyAllowlisted) {