Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit e3c687ce authored by Aaron Liu's avatar Aaron Liu
Browse files

Asynchronously inflate the bouncer.

As we are lazily inflating the bouncer every time we show it, we can get
huge performance gains from AsyncInflater. From the class documentation
"This is intended for parts of the UI that are created lazily or in
response to user interactions. This allows the UI thread to continue to
be responsive & animate while the relatively heavy inflate is being
performed." it seems that this is the perfect use case.

I've added the AsyncInflater in a dagger module for testing purposes. I
also moved the handler constructrion in KeyguardMessageArea to its
corresponding view controller to conform to the AsynInflater
requirements.

Interestingly this also fixes b/265384692 as it seemed like the bouncer
was being inflated before the onclicklisteners were added.

Fixes: 269522372
Fixes: 265384692
Test: Test for talk back to ensure that it announces the text view.
Test: open scrimmed and non scrimmed bouncer.
Test: Pin, Password, pattern, simpin, simpuk
Test: modified unit tests.

Change-Id: Ic65af22da9f6822e5137aae3cfee03a6e819ab94
parent f7fa5cb4
Loading
Loading
Loading
Loading
+0 −41
Original line number Diff line number Diff line
@@ -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);
            }
        }
    }
}
+45 −0
Original line number Diff line number Diff line
@@ -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);
            }
        }
    }
}
+12 −7
Original line number Diff line number Diff line
@@ -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();
        if (mFeatureFlags.isEnabled(Flags.ASYNC_INFLATE_BOUNCER)) {
            mSecurityViewFlipperController.asynchronouslyInflateView(mCurrentSecurityMode,
                    mKeyguardSecurityCallback, onViewInflatedListener);
        } else {
            mSecurityViewFlipperController.getSecurityView(mCurrentSecurityMode,
                    mKeyguardSecurityCallback);
            onViewInflatedListener.onViewInflated();
        }
    }

    /**
+50 −4
Original line number Diff line number Diff line
@@ -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();
    }
}
+8 −0
Original line number Diff line number Diff line
@@ -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);
Loading