Merge "Asynchronously inflate the bouncer." into tm-qpr-dev

This commit is contained in:
Aaron Liu
2023-03-14 19:07:01 +00:00
committed by Android (Google) Code Review
9 changed files with 178 additions and 68 deletions

View File

@@ -18,13 +18,9 @@ package com.android.keyguard;
import android.content.Context;
import android.content.res.TypedArray;
import android.os.Handler;
import android.os.Looper;
import android.os.SystemClock;
import android.text.TextUtils;
import android.util.AttributeSet;
import android.util.TypedValue;
import android.view.View;
import android.view.ViewGroup;
import android.widget.TextView;
@@ -33,22 +29,10 @@ import androidx.annotation.Nullable;
import com.android.internal.policy.SystemBarUtils;
import com.android.systemui.R;
import java.lang.ref.WeakReference;
/***
* Manages a number of views inside of the given layout. See below for a list of widgets.
*/
public abstract class KeyguardMessageArea extends TextView implements SecurityMessageDisplay {
/** Handler token posted with accessibility announcement runnables. */
private static final Object ANNOUNCE_TOKEN = new Object();
/**
* Delay before speaking an accessibility announcement. Used to prevent
* lift-to-type from interrupting itself.
*/
private static final long ANNOUNCEMENT_DELAY = 250;
private final Handler mHandler;
private CharSequence mMessage;
private boolean mIsVisible;
@@ -65,7 +49,6 @@ public abstract class KeyguardMessageArea extends TextView implements SecurityMe
super(context, attrs);
setLayerType(LAYER_TYPE_HARDWARE, null); // work around nested unclipped SaveLayer bug
mHandler = new Handler(Looper.myLooper());
onThemeChanged();
}
@@ -127,9 +110,6 @@ public abstract class KeyguardMessageArea extends TextView implements SecurityMe
private void securityMessageChanged(CharSequence message) {
mMessage = message;
update();
mHandler.removeCallbacksAndMessages(ANNOUNCE_TOKEN);
mHandler.postAtTime(new AnnounceRunnable(this, getText()), ANNOUNCE_TOKEN,
(SystemClock.uptimeMillis() + ANNOUNCEMENT_DELAY));
}
private void clearMessage() {
@@ -156,25 +136,4 @@ public abstract class KeyguardMessageArea extends TextView implements SecurityMe
/** Set the text color */
protected abstract void updateTextColor();
/**
* Runnable used to delay accessibility announcements.
*/
private static class AnnounceRunnable implements Runnable {
private final WeakReference<View> mHost;
private final CharSequence mTextToAnnounce;
AnnounceRunnable(View host, CharSequence textToAnnounce) {
mHost = new WeakReference<View>(host);
mTextToAnnounce = textToAnnounce;
}
@Override
public void run() {
final View host = mHost.get();
if (host != null) {
host.announceForAccessibility(mTextToAnnounce);
}
}
}
}

View File

@@ -18,11 +18,17 @@ package com.android.keyguard;
import android.content.res.ColorStateList;
import android.content.res.Configuration;
import android.text.TextUtils;
import android.view.View;
import androidx.annotation.VisibleForTesting;
import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.statusbar.policy.ConfigurationController.ConfigurationListener;
import com.android.systemui.util.ViewController;
import java.lang.ref.WeakReference;
import javax.inject.Inject;
/**
@@ -31,8 +37,14 @@ import javax.inject.Inject;
*/
public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
extends ViewController<T> {
/**
* Delay before speaking an accessibility announcement. Used to prevent
* lift-to-type from interrupting itself.
*/
private static final long ANNOUNCEMENT_DELAY = 250;
private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
private final ConfigurationController mConfigurationController;
private final AnnounceRunnable mAnnounceRunnable;
private KeyguardUpdateMonitorCallback mInfoCallback = new KeyguardUpdateMonitorCallback() {
public void onFinishedGoingToSleep(int why) {
@@ -68,6 +80,7 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
mKeyguardUpdateMonitor = keyguardUpdateMonitor;
mConfigurationController = configurationController;
mAnnounceRunnable = new AnnounceRunnable(mView);
}
@Override
@@ -100,6 +113,12 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
*/
public void setMessage(CharSequence s, boolean animate) {
mView.setMessage(s, animate);
CharSequence msg = mView.getText();
if (!TextUtils.isEmpty(msg)) {
mView.removeCallbacks(mAnnounceRunnable);
mAnnounceRunnable.setTextToAnnounce(msg);
mView.postDelayed(mAnnounceRunnable, ANNOUNCEMENT_DELAY);
}
}
public void setMessage(int resId) {
@@ -134,4 +153,30 @@ public class KeyguardMessageAreaController<T extends KeyguardMessageArea>
view, mKeyguardUpdateMonitor, mConfigurationController);
}
}
/**
* Runnable used to delay accessibility announcements.
*/
@VisibleForTesting
public static class AnnounceRunnable implements Runnable {
private final WeakReference<View> mHost;
private CharSequence mTextToAnnounce;
AnnounceRunnable(View host) {
mHost = new WeakReference<>(host);
}
/** Sets the text to announce. */
public void setTextToAnnounce(CharSequence textToAnnounce) {
mTextToAnnounce = textToAnnounce;
}
@Override
public void run() {
final View host = mHost.get();
if (host != null) {
host.announceForAccessibility(mTextToAnnounce);
}
}
}
}

View File

@@ -1065,23 +1065,28 @@ public class KeyguardSecurityContainerController extends ViewController<Keyguard
}
private void reloadColors() {
reinflateViewFlipper();
mView.reloadColors();
reinflateViewFlipper(() -> mView.reloadColors());
}
/** Handles density or font scale changes. */
private void onDensityOrFontScaleChanged() {
reinflateViewFlipper();
mView.onDensityOrFontScaleChanged();
reinflateViewFlipper(() -> mView.onDensityOrFontScaleChanged());
}
/**
* Reinflate the view flipper child view.
*/
public void reinflateViewFlipper() {
public void reinflateViewFlipper(
KeyguardSecurityViewFlipperController.OnViewInflatedListener onViewInflatedListener) {
mSecurityViewFlipperController.clearViews();
mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
mKeyguardSecurityCallback);
if (mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER)) {
mSecurityViewFlipperController.asynchronouslyInflateView(mCurrentSecurityMode,
mKeyguardSecurityCallback, onViewInflatedListener);
} else {
mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
mKeyguardSecurityCallback);
onViewInflatedListener.onViewInflated();
}
}
/**

View File

@@ -19,11 +19,16 @@ package com.android.keyguard;
import android.util.Log;
import android.view.LayoutInflater;
import androidx.annotation.Nullable;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
import com.android.internal.annotations.VisibleForTesting;
import com.android.keyguard.KeyguardInputViewController.Factory;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.keyguard.dagger.KeyguardBouncerScope;
import com.android.systemui.R;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.util.ViewController;
import java.util.ArrayList;
@@ -44,18 +49,24 @@ public class KeyguardSecurityViewFlipperController
private final List<KeyguardInputViewController<KeyguardInputView>> mChildren =
new ArrayList<>();
private final LayoutInflater mLayoutInflater;
private final AsyncLayoutInflater mAsyncLayoutInflater;
private final EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
private final Factory mKeyguardSecurityViewControllerFactory;
private final FeatureFlags mFeatureFlags;
@Inject
protected KeyguardSecurityViewFlipperController(KeyguardSecurityViewFlipper view,
LayoutInflater layoutInflater,
AsyncLayoutInflater asyncLayoutInflater,
KeyguardInputViewController.Factory keyguardSecurityViewControllerFactory,
EmergencyButtonController.Factory emergencyButtonControllerFactory) {
EmergencyButtonController.Factory emergencyButtonControllerFactory,
FeatureFlags featureFlags) {
super(view);
mKeyguardSecurityViewControllerFactory = keyguardSecurityViewControllerFactory;
mLayoutInflater = layoutInflater;
mEmergencyButtonControllerFactory = emergencyButtonControllerFactory;
mAsyncLayoutInflater = asyncLayoutInflater;
mFeatureFlags = featureFlags;
}
@Override
@@ -92,13 +103,12 @@ public class KeyguardSecurityViewFlipperController
}
}
if (childController == null
if (!mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER) && childController == null
&& securityMode != SecurityMode.None && securityMode != SecurityMode.Invalid) {
int layoutId = getLayoutIdFor(securityMode);
KeyguardInputView view = null;
if (layoutId != 0) {
if (DEBUG) Log.v(TAG, "inflating id = " + layoutId);
if (DEBUG) Log.v(TAG, "inflating on main thread id = " + layoutId);
view = (KeyguardInputView) mLayoutInflater.inflate(
layoutId, mView, false);
mView.addView(view);
@@ -119,6 +129,36 @@ public class KeyguardSecurityViewFlipperController
return childController;
}
/**
* Asynchronously inflate view and then add it to view flipper on the main thread when complete.
*
* OnInflateFinishedListener will be called on the main thread.
*
* @param securityMode
* @param keyguardSecurityCallback
*/
public void asynchronouslyInflateView(SecurityMode securityMode,
KeyguardSecurityCallback keyguardSecurityCallback,
@Nullable OnViewInflatedListener onViewInflatedListener) {
int layoutId = getLayoutIdFor(securityMode);
if (layoutId != 0) {
if (DEBUG) Log.v(TAG, "inflating on bg thread id = " + layoutId);
mAsyncLayoutInflater.inflate(layoutId, mView,
(view, resId, parent) -> {
mView.addView(view);
KeyguardInputViewController<KeyguardInputView> childController =
mKeyguardSecurityViewControllerFactory.create(
(KeyguardInputView) view, securityMode,
keyguardSecurityCallback);
childController.init();
mChildren.add(childController);
if (onViewInflatedListener != null) {
onViewInflatedListener.onViewInflated();
}
});
}
}
private int getLayoutIdFor(SecurityMode securityMode) {
switch (securityMode) {
case Pattern: return R.layout.keyguard_pattern_view;
@@ -162,4 +202,10 @@ public class KeyguardSecurityViewFlipperController
return 0;
}
}
/** Listener to when view has finished inflation. */
public interface OnViewInflatedListener {
/** Notifies that view has been inflated */
void onViewInflated();
}
}

View File

@@ -97,6 +97,7 @@ import android.view.accessibility.CaptioningManager;
import android.view.inputmethod.InputMethodManager;
import android.view.textclassifier.TextClassificationManager;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
import androidx.core.app.NotificationManagerCompat;
import com.android.internal.app.IBatteryStats;
@@ -388,6 +389,13 @@ public class FrameworkServicesModule {
return LayoutInflater.from(context);
}
/** */
@Provides
@Singleton
public AsyncLayoutInflater provideAsyncLayoutInflater(Context context) {
return new AsyncLayoutInflater(context);
}
@Provides
static MediaProjectionManager provideMediaProjectionManager(Context context) {
return context.getSystemService(MediaProjectionManager.class);

View File

@@ -34,7 +34,6 @@ import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransition
import com.android.systemui.lifecycle.repeatWhenAttached
import com.android.systemui.plugins.ActivityStarter
import kotlinx.coroutines.awaitCancellation
import kotlinx.coroutines.flow.collect
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.launch
@@ -112,16 +111,18 @@ object KeyguardBouncerViewBinder {
launch {
viewModel.show.collect {
// Reset Security Container entirely.
view.visibility = View.VISIBLE
securityContainerController.onBouncerVisibilityChanged(
/* isVisible= */ true
)
securityContainerController.reinflateViewFlipper()
securityContainerController.showPrimarySecurityScreen(
/* turningOff= */ false
)
securityContainerController.appear()
securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON)
securityContainerController.reinflateViewFlipper {
// Reset Security Container entirely.
view.visibility = View.VISIBLE
securityContainerController.onBouncerVisibilityChanged(
/* isVisible= */ true
)
securityContainerController.showPrimarySecurityScreen(
/* turningOff= */ false
)
securityContainerController.appear()
securityContainerController.onResume(KeyguardSecurityView.SCREEN_ON)
}
}
}

View File

@@ -18,12 +18,15 @@ package com.android.keyguard;
import static com.google.common.truth.Truth.assertThat;
import static org.mockito.ArgumentMatchers.any;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.ArgumentMatchers.eq;
import static org.mockito.Mockito.verify;
import static org.mockito.Mockito.when;
import android.test.suitebuilder.annotation.SmallTest;
import android.testing.AndroidTestingRunner;
import android.testing.TestableLooper;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.statusbar.policy.ConfigurationController;
@@ -37,6 +40,7 @@ import org.mockito.Mock;
import org.mockito.MockitoAnnotations;
@SmallTest
@TestableLooper.RunWithLooper
@RunWith(AndroidTestingRunner.class)
public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
@Mock
@@ -45,14 +49,14 @@ public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
private KeyguardUpdateMonitor mKeyguardUpdateMonitor;
@Mock
private KeyguardMessageArea mKeyguardMessageArea;
private KeyguardMessageAreaController mMessageAreaController;
@Before
public void setUp() throws Exception {
MockitoAnnotations.initMocks(this);
mMessageAreaController = new KeyguardMessageAreaController.Factory(
mKeyguardUpdateMonitor, mConfigurationController).create(mKeyguardMessageArea);
mKeyguardUpdateMonitor, mConfigurationController).create(
mKeyguardMessageArea);
}
@Test
@@ -88,6 +92,19 @@ public class KeyguardMessageAreaControllerTest extends SysuiTestCase {
verify(mKeyguardMessageArea).setMessage("", /* animate= */ true);
}
@Test
public void testSetMessage_AnnounceForAccessibility() {
ArgumentCaptor<Runnable> argumentCaptor = ArgumentCaptor.forClass(Runnable.class);
when(mKeyguardMessageArea.getText()).thenReturn("abc");
mMessageAreaController.setMessage("abc");
verify(mKeyguardMessageArea).setMessage("abc", /* animate= */ true);
verify(mKeyguardMessageArea).removeCallbacks(any(Runnable.class));
verify(mKeyguardMessageArea).postDelayed(argumentCaptor.capture(), anyLong());
argumentCaptor.getValue().run();
verify(mKeyguardMessageArea).announceForAccessibility("abc");
}
@Test
public void testSetBouncerVisible() {
mMessageAreaController.setIsVisible(true);

View File

@@ -66,6 +66,7 @@ import com.android.systemui.biometrics.SideFpsUiRequestSource;
import com.android.systemui.classifier.FalsingA11yDelegate;
import com.android.systemui.classifier.FalsingCollector;
import com.android.systemui.flags.FeatureFlags;
import com.android.systemui.flags.Flags;
import com.android.systemui.log.SessionTracker;
import com.android.systemui.plugins.ActivityStarter;
import com.android.systemui.plugins.FalsingManager;
@@ -617,12 +618,25 @@ public class KeyguardSecurityContainerControllerTest extends SysuiTestCase {
@Test
public void testReinflateViewFlipper() {
mKeyguardSecurityContainerController.reinflateViewFlipper();
mKeyguardSecurityContainerController.reinflateViewFlipper(() -> {});
verify(mKeyguardSecurityViewFlipperController).clearViews();
verify(mKeyguardSecurityViewFlipperController).getSecurityView(any(SecurityMode.class),
any(KeyguardSecurityCallback.class));
}
@Test
public void testReinflateViewFlipper_asyncBouncerFlagOn() {
when(mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER)).thenReturn(true);
KeyguardSecurityViewFlipperController.OnViewInflatedListener onViewInflatedListener =
() -> {
};
mKeyguardSecurityContainerController.reinflateViewFlipper(onViewInflatedListener);
verify(mKeyguardSecurityViewFlipperController).clearViews();
verify(mKeyguardSecurityViewFlipperController).asynchronouslyInflateView(
any(SecurityMode.class),
any(KeyguardSecurityCallback.class), eq(onViewInflatedListener));
}
@Test
public void testSideFpsControllerShow() {
mKeyguardSecurityContainerController.updateSideFpsVisibility(/* isVisible= */ true);

View File

@@ -31,10 +31,12 @@ import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.WindowInsetsController;
import androidx.asynclayoutinflater.view.AsyncLayoutInflater;
import androidx.test.filters.SmallTest;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.flags.FeatureFlags;
import org.junit.Before;
import org.junit.Rule;
@@ -57,6 +59,8 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
@Mock
private LayoutInflater mLayoutInflater;
@Mock
private AsyncLayoutInflater mAsyncLayoutInflater;
@Mock
private KeyguardInputViewController.Factory mKeyguardSecurityViewControllerFactory;
@Mock
private EmergencyButtonController.Factory mEmergencyButtonControllerFactory;
@@ -70,6 +74,8 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
private WindowInsetsController mWindowInsetsController;
@Mock
private KeyguardSecurityCallback mKeyguardSecurityCallback;
@Mock
private FeatureFlags mFeatureFlags;
private KeyguardSecurityViewFlipperController mKeyguardSecurityViewFlipperController;
@@ -82,10 +88,11 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
when(mView.getWindowInsetsController()).thenReturn(mWindowInsetsController);
when(mEmergencyButtonControllerFactory.create(any(EmergencyButton.class)))
.thenReturn(mEmergencyButtonController);
when(mView.getContext()).thenReturn(getContext());
mKeyguardSecurityViewFlipperController = new KeyguardSecurityViewFlipperController(mView,
mLayoutInflater, mKeyguardSecurityViewControllerFactory,
mEmergencyButtonControllerFactory);
mLayoutInflater, mAsyncLayoutInflater, mKeyguardSecurityViewControllerFactory,
mEmergencyButtonControllerFactory, mFeatureFlags);
}
@Test
@@ -107,6 +114,14 @@ public class KeyguardSecurityViewFlipperControllerTest extends SysuiTestCase {
}
}
@Test
public void asynchronouslyInflateView() {
mKeyguardSecurityViewFlipperController.asynchronouslyInflateView(SecurityMode.PIN,
mKeyguardSecurityCallback, null);
verify(mAsyncLayoutInflater).inflate(anyInt(), eq(mView), any(
AsyncLayoutInflater.OnInflateFinishedListener.class));
}
@Test
public void onDensityOrFontScaleChanged() {
mKeyguardSecurityViewFlipperController.clearViews();