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

Commit c9b8f551 authored by Dave Mankoff's avatar Dave Mankoff Committed by Android (Google) Code Review
Browse files

Merge changes from topic "b166448040-keyguard-message-area"

* changes:
  3/N Use KeyguardSecurityContainerController in KHVC.
  2/N Use KeyguardHostViewController in KeyguardBouncer.
  1/N Add Controllers for most KeyguardBouncer related views.
parents 8d77cf0a 860a2c03
Loading
Loading
Loading
Loading
+6 −7
Original line number Diff line number Diff line
@@ -30,7 +30,6 @@ import androidx.annotation.VisibleForTesting;
import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.KeyguardViewController;
import com.android.keyguard.ViewMediatorCallback;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
import com.android.systemui.car.CarServiceProvider;
import com.android.systemui.car.navigationbar.CarNavigationBarController;
@@ -40,6 +39,7 @@ import com.android.systemui.dagger.SysUISingleton;
import com.android.systemui.dagger.qualifiers.Main;
import com.android.systemui.statusbar.phone.BiometricUnlockController;
import com.android.systemui.statusbar.phone.KeyguardBouncer;
import com.android.systemui.statusbar.phone.KeyguardBouncer.Factory;
import com.android.systemui.statusbar.phone.KeyguardBypassController;
import com.android.systemui.statusbar.phone.NotificationPanelViewController;
import com.android.systemui.statusbar.phone.StatusBar;
@@ -66,7 +66,7 @@ public class CarKeyguardViewController extends OverlayViewController implements
    private final Lazy<BiometricUnlockController> mBiometricUnlockControllerLazy;
    private final ViewMediatorCallback mViewMediatorCallback;
    private final CarNavigationBarController mCarNavigationBarController;
    private final KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
    private final Factory mKeyguardBouncerFactory;
    // Needed to instantiate mBouncer.
    private final KeyguardBouncer.BouncerExpansionCallback
            mExpansionCallback = new KeyguardBouncer.BouncerExpansionCallback() {
@@ -107,7 +107,7 @@ public class CarKeyguardViewController extends OverlayViewController implements
            Lazy<BiometricUnlockController> biometricUnlockControllerLazy,
            ViewMediatorCallback viewMediatorCallback,
            CarNavigationBarController carNavigationBarController,
            KeyguardBouncerComponent.Factory keyguardBouncerComponentFactory) {
            KeyguardBouncer.Factory keyguardBouncerFactory) {

        super(R.id.keyguard_stub, overlayViewGlobalStateController);

@@ -118,7 +118,7 @@ public class CarKeyguardViewController extends OverlayViewController implements
        mBiometricUnlockControllerLazy = biometricUnlockControllerLazy;
        mViewMediatorCallback = viewMediatorCallback;
        mCarNavigationBarController = carNavigationBarController;
        mKeyguardBouncerComponentFactory = keyguardBouncerComponentFactory;
        mKeyguardBouncerFactory = keyguardBouncerFactory;

        registerUserSwitchedListener();
    }
@@ -130,9 +130,8 @@ public class CarKeyguardViewController extends OverlayViewController implements

    @Override
    public void onFinishInflate() {
        mBouncer = mKeyguardBouncerComponentFactory
                .build(getLayout().findViewById(R.id.keyguard_container), mExpansionCallback)
                .createKeyguardBouncer();
        mBouncer = mKeyguardBouncerFactory
                .create(getLayout().findViewById(R.id.keyguard_container), mExpansionCallback);
        mBiometricUnlockControllerLazy.get().setKeyguardViewController(this);
    }

+4 −8
Original line number Diff line number Diff line
@@ -36,7 +36,6 @@ import androidx.test.filters.SmallTest;

import com.android.keyguard.KeyguardUpdateMonitor;
import com.android.keyguard.ViewMediatorCallback;
import com.android.keyguard.dagger.KeyguardBouncerComponent;
import com.android.systemui.R;
import com.android.systemui.SysuiTestCase;
import com.android.systemui.car.CarServiceProvider;
@@ -67,9 +66,7 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase {
    @Mock
    private CarKeyguardViewController.OnKeyguardCancelClickedListener mCancelClickedListener;
    @Mock
    private KeyguardBouncerComponent.Factory mKeyguardBouncerComponentFactory;
    @Mock
    private KeyguardBouncerComponent mKeyguardBouncerComponent;
    private KeyguardBouncer.Factory mKeyguardBouncerFactory;
    @Mock
    private KeyguardBouncer mBouncer;

@@ -77,11 +74,10 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase {
    public void setUp() {
        MockitoAnnotations.initMocks(this);

        when(mKeyguardBouncerComponentFactory.build(
        when(mKeyguardBouncerFactory.create(
                any(ViewGroup.class),
                any(KeyguardBouncer.BouncerExpansionCallback.class)))
                .thenReturn(mKeyguardBouncerComponent);
        when(mKeyguardBouncerComponent.createKeyguardBouncer()).thenReturn(mBouncer);
                .thenReturn(mBouncer);

        mCarKeyguardViewController = new CarKeyguardViewController(
                Handler.getMain(),
@@ -92,7 +88,7 @@ public class CarKeyguardViewControllerTest extends SysuiTestCase {
                () -> mock(BiometricUnlockController.class),
                mock(ViewMediatorCallback.class),
                mock(CarNavigationBarController.class),
                mKeyguardBouncerComponentFactory
                mKeyguardBouncerFactory
        );
        mCarKeyguardViewController.inflate((ViewGroup) LayoutInflater.from(mContext).inflate(
                R.layout.sysui_overlay_window, /* root= */ null));
+1 −402
Original line number Diff line number Diff line
@@ -16,32 +16,11 @@

package com.android.keyguard;

import android.app.ActivityManager;
import android.content.Context;
import android.content.res.ColorStateList;
import android.content.res.Resources;
import android.graphics.Canvas;
import android.media.AudioManager;
import android.os.SystemClock;
import android.service.trust.TrustAgentService;
import android.telephony.TelephonyManager;
import android.util.AttributeSet;
import android.util.Log;
import android.view.KeyEvent;
import android.widget.FrameLayout;

import androidx.annotation.VisibleForTesting;

import com.android.internal.widget.LockPatternUtils;
import com.android.keyguard.KeyguardSecurityContainer.SecurityCallback;
import com.android.keyguard.KeyguardSecurityModel.SecurityMode;
import com.android.settingslib.Utils;
import com.android.systemui.Dependency;
import com.android.systemui.R;
import com.android.systemui.plugins.ActivityStarter.OnDismissAction;

import java.io.File;

/**
 * Base class for keyguard view.  {@link #reset} is where you should
 * reset the state of your view.  Use the {@link KeyguardViewCallback} via
@@ -51,59 +30,10 @@ import java.io.File;
 * Handles intercepting of media keys that still work when the keyguard is
 * showing.
 */
public class KeyguardHostView extends FrameLayout implements SecurityCallback {
public class KeyguardHostView extends FrameLayout {

    private AudioManager mAudioManager;
    private TelephonyManager mTelephonyManager = null;
    protected ViewMediatorCallback mViewMediatorCallback;
    protected LockPatternUtils mLockPatternUtils;
    private OnDismissAction mDismissAction;
    private Runnable mCancelAction;

    private final KeyguardUpdateMonitorCallback mUpdateCallback =
            new KeyguardUpdateMonitorCallback() {

        @Override
        public void onUserSwitchComplete(int userId) {
            getSecurityContainer().showPrimarySecurityScreen(false /* turning off */);
        }

        @Override
        public void onTrustGrantedWithFlags(int flags, int userId) {
            if (userId != KeyguardUpdateMonitor.getCurrentUser()) return;
            if (!isAttachedToWindow()) return;
            boolean bouncerVisible = isVisibleToUser();
            boolean initiatedByUser =
                    (flags & TrustAgentService.FLAG_GRANT_TRUST_INITIATED_BY_USER) != 0;
            boolean dismissKeyguard =
                    (flags & TrustAgentService.FLAG_GRANT_TRUST_DISMISS_KEYGUARD) != 0;

            if (initiatedByUser || dismissKeyguard) {
                if (mViewMediatorCallback.isScreenOn() && (bouncerVisible || dismissKeyguard)) {
                    if (!bouncerVisible) {
                        // The trust agent dismissed the keyguard without the user proving
                        // that they are present (by swiping up to show the bouncer). That's fine if
                        // the user proved presence via some other way to the trust agent.
                        Log.i(TAG, "TrustAgent dismissed Keyguard.");
                    }
                    dismiss(false /* authenticated */, userId,
                            /* bypassSecondaryLockScreen */ false);
                } else {
                    mViewMediatorCallback.playTrustedSound();
                }
            }
        }
    };

    // Whether the volume keys should be handled by keyguard. If true, then
    // they will be handled here for specific media types such as music, otherwise
    // the audio service will bring up the volume dialog.
    private static final boolean KEYGUARD_MANAGES_VOLUME = false;
    public static final boolean DEBUG = KeyguardConstants.DEBUG;
    private static final String TAG = "KeyguardViewBase";

    @VisibleForTesting
    protected KeyguardSecurityContainer mSecurityContainer;

    public KeyguardHostView(Context context) {
        this(context, null);
@@ -111,7 +41,6 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback {

    public KeyguardHostView(Context context, AttributeSet attrs) {
        super(context, attrs);
        Dependency.get(KeyguardUpdateMonitor.class).registerCallback(mUpdateCallback);
    }

    @Override
@@ -122,337 +51,7 @@ public class KeyguardHostView extends FrameLayout implements SecurityCallback {
        }
    }

    /**
     * Sets an action to run when keyguard finishes.
     *
     * @param action
     */
    public void setOnDismissAction(OnDismissAction action, Runnable cancelAction) {
        if (mCancelAction != null) {
            mCancelAction.run();
            mCancelAction = null;
        }
        mDismissAction = action;
        mCancelAction = cancelAction;
    }

    public boolean hasDismissActions() {
        return mDismissAction != null || mCancelAction != null;
    }

    public void cancelDismissAction() {
        setOnDismissAction(null, null);
    }

    @Override
    protected void onFinishInflate() {
        mSecurityContainer =
                findViewById(R.id.keyguard_security_container);
        mLockPatternUtils = new LockPatternUtils(mContext);
        mSecurityContainer.setLockPatternUtils(mLockPatternUtils);
        mSecurityContainer.setSecurityCallback(this);
        mSecurityContainer.showPrimarySecurityScreen(false);
    }

    /**
     * Called when the view needs to be shown.
     */
    public void showPrimarySecurityScreen() {
        if (DEBUG) Log.d(TAG, "show()");
        mSecurityContainer.showPrimarySecurityScreen(false);
    }

    public KeyguardSecurityView getCurrentSecurityView() {
        return mSecurityContainer != null ? mSecurityContainer.getCurrentSecurityView() : null;
    }

    /**
     * Show a string explaining why the security view needs to be solved.
     *
     * @param reason a flag indicating which string should be shown, see
     *               {@link KeyguardSecurityView#PROMPT_REASON_NONE},
     *               {@link KeyguardSecurityView#PROMPT_REASON_RESTART},
     *               {@link KeyguardSecurityView#PROMPT_REASON_TIMEOUT}, and
     *               {@link KeyguardSecurityView#PROMPT_REASON_PREPARE_FOR_UPDATE}.
     */
    public void showPromptReason(int reason) {
        mSecurityContainer.showPromptReason(reason);
    }

    public void showMessage(CharSequence message, ColorStateList colorState) {
        mSecurityContainer.showMessage(message, colorState);
    }

    public void showErrorMessage(CharSequence message) {
        showMessage(message, Utils.getColorError(mContext));
    }

    /**
     * Dismisses the keyguard by going to the next screen or making it gone.
     * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
     * @return True if the keyguard is done.
     */
    public boolean dismiss(int targetUserId) {
        return dismiss(false, targetUserId, false);
    }

    public boolean handleBackKey() {
        if (mSecurityContainer.getCurrentSecuritySelection() != SecurityMode.None) {
            mSecurityContainer.dismiss(false, KeyguardUpdateMonitor.getCurrentUser());
            return true;
        }
        return false;
    }

    protected KeyguardSecurityContainer getSecurityContainer() {
        return mSecurityContainer;
    }

    @Override
    public boolean dismiss(boolean authenticated, int targetUserId,
            boolean bypassSecondaryLockScreen) {
        return mSecurityContainer.showNextSecurityScreenOrFinish(authenticated, targetUserId,
                bypassSecondaryLockScreen);
    }

    /**
     * Authentication has happened and it's time to dismiss keyguard. This function
     * should clean up and inform KeyguardViewMediator.
     *
     * @param strongAuth whether the user has authenticated with strong authentication like
     *                   pattern, password or PIN but not by trust agents or fingerprint
     * @param targetUserId a user that needs to be the foreground user at the dismissal completion.
     */
    @Override
    public void finish(boolean strongAuth, int targetUserId) {
        // If there's a pending runnable because the user interacted with a widget
        // and we're leaving keyguard, then run it.
        boolean deferKeyguardDone = false;
        if (mDismissAction != null) {
            deferKeyguardDone = mDismissAction.onDismiss();
            mDismissAction = null;
            mCancelAction = null;
        }
        if (mViewMediatorCallback != null) {
            if (deferKeyguardDone) {
                mViewMediatorCallback.keyguardDonePending(strongAuth, targetUserId);
            } else {
                mViewMediatorCallback.keyguardDone(strongAuth, targetUserId);
            }
        }
    }

    @Override
    public void reset() {
        mViewMediatorCallback.resetKeyguard();
    }

    @Override
    public void onCancelClicked() {
        mViewMediatorCallback.onCancelClicked();
    }

    public void resetSecurityContainer() {
        mSecurityContainer.reset();
    }

    @Override
    public void onSecurityModeChanged(SecurityMode securityMode, boolean needsInput) {
        if (mViewMediatorCallback != null) {
            mViewMediatorCallback.setNeedsInput(needsInput);
        }
    }

    public CharSequence getAccessibilityTitleForCurrentMode() {
        return mSecurityContainer.getTitle();
    }

    public void userActivity() {
        if (mViewMediatorCallback != null) {
            mViewMediatorCallback.userActivity();
        }
    }

    /**
     * Called when the Keyguard is not actively shown anymore on the screen.
     */
    public void onPause() {
        if (DEBUG) Log.d(TAG, String.format("screen off, instance %s at %s",
                Integer.toHexString(hashCode()), SystemClock.uptimeMillis()));
        mSecurityContainer.showPrimarySecurityScreen(true);
        mSecurityContainer.onPause();
        clearFocus();
    }

    /**
     * Called when the Keyguard is actively shown on the screen.
     */
    public void onResume() {
        if (DEBUG) Log.d(TAG, "screen on, instance " + Integer.toHexString(hashCode()));
        mSecurityContainer.onResume(KeyguardSecurityView.SCREEN_ON);
        requestFocus();
    }

    /**
     * Starts the animation when the Keyguard gets shown.
     */
    public void startAppearAnimation() {
        mSecurityContainer.startAppearAnimation();
    }

    public void startDisappearAnimation(Runnable finishRunnable) {
        if (!mSecurityContainer.startDisappearAnimation(finishRunnable) && finishRunnable != null) {
            finishRunnable.run();
        }
    }

    /**
     * Called before this view is being removed.
     */
    public void cleanUp() {
        getSecurityContainer().onPause();
    }

    @Override
    public boolean dispatchKeyEvent(KeyEvent event) {
        if (interceptMediaKey(event)) {
            return true;
        }
        return super.dispatchKeyEvent(event);
    }

    /**
     * Allows the media keys to work when the keyguard is showing.
     * The media keys should be of no interest to the actual keyguard view(s),
     * so intercepting them here should not be of any harm.
     * @param event The key event
     * @return whether the event was consumed as a media key.
     */
    public boolean interceptMediaKey(KeyEvent event) {
        final int keyCode = event.getKeyCode();
        if (event.getAction() == KeyEvent.ACTION_DOWN) {
            switch (keyCode) {
                case KeyEvent.KEYCODE_MEDIA_PLAY:
                case KeyEvent.KEYCODE_MEDIA_PAUSE:
                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                    /* Suppress PLAY/PAUSE toggle when phone is ringing or
                     * in-call to avoid music playback */
                    if (mTelephonyManager == null) {
                        mTelephonyManager = (TelephonyManager) getContext().getSystemService(
                                Context.TELEPHONY_SERVICE);
                    }
                    if (mTelephonyManager != null &&
                            mTelephonyManager.getCallState() != TelephonyManager.CALL_STATE_IDLE) {
                        return true;  // suppress key event
                    }
                case KeyEvent.KEYCODE_MUTE:
                case KeyEvent.KEYCODE_HEADSETHOOK:
                case KeyEvent.KEYCODE_MEDIA_STOP:
                case KeyEvent.KEYCODE_MEDIA_NEXT:
                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                case KeyEvent.KEYCODE_MEDIA_REWIND:
                case KeyEvent.KEYCODE_MEDIA_RECORD:
                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
                    handleMediaKeyEvent(event);
                    return true;
                }

                case KeyEvent.KEYCODE_VOLUME_UP:
                case KeyEvent.KEYCODE_VOLUME_DOWN:
                case KeyEvent.KEYCODE_VOLUME_MUTE: {
                    if (KEYGUARD_MANAGES_VOLUME) {
                        synchronized (this) {
                            if (mAudioManager == null) {
                                mAudioManager = (AudioManager) getContext().getSystemService(
                                        Context.AUDIO_SERVICE);
                            }
                        }
                        // Volume buttons should only function for music (local or remote).
                        // TODO: Actually handle MUTE.
                        mAudioManager.adjustSuggestedStreamVolume(
                                keyCode == KeyEvent.KEYCODE_VOLUME_UP
                                        ? AudioManager.ADJUST_RAISE
                                        : AudioManager.ADJUST_LOWER /* direction */,
                                AudioManager.STREAM_MUSIC /* stream */, 0 /* flags */);
                        // Don't execute default volume behavior
                        return true;
                    } else {
                        return false;
                    }
                }
            }
        } else if (event.getAction() == KeyEvent.ACTION_UP) {
            switch (keyCode) {
                case KeyEvent.KEYCODE_MUTE:
                case KeyEvent.KEYCODE_HEADSETHOOK:
                case KeyEvent.KEYCODE_MEDIA_PLAY:
                case KeyEvent.KEYCODE_MEDIA_PAUSE:
                case KeyEvent.KEYCODE_MEDIA_PLAY_PAUSE:
                case KeyEvent.KEYCODE_MEDIA_STOP:
                case KeyEvent.KEYCODE_MEDIA_NEXT:
                case KeyEvent.KEYCODE_MEDIA_PREVIOUS:
                case KeyEvent.KEYCODE_MEDIA_REWIND:
                case KeyEvent.KEYCODE_MEDIA_RECORD:
                case KeyEvent.KEYCODE_MEDIA_FAST_FORWARD:
                case KeyEvent.KEYCODE_MEDIA_AUDIO_TRACK: {
                    handleMediaKeyEvent(event);
                    return true;
                }
            }
        }
        return false;
    }

    private void handleMediaKeyEvent(KeyEvent keyEvent) {
        synchronized (this) {
            if (mAudioManager == null) {
                mAudioManager = (AudioManager) getContext().getSystemService(
                        Context.AUDIO_SERVICE);
            }
        }
        mAudioManager.dispatchMediaKeyEvent(keyEvent);
    }

    /**
     * In general, we enable unlocking the insecure keyguard with the menu key. However, there are
     * some cases where we wish to disable it, notably when the menu button placement or technology
     * is prone to false positives.
     *
     * @return true if the menu key should be enabled
     */
    private static final String ENABLE_MENU_KEY_FILE = "/data/local/enable_menu_key";
    public boolean shouldEnableMenuKey() {
        final Resources res = getResources();
        final boolean configDisabled = res.getBoolean(R.bool.config_disableMenuKeyInLockScreen);
        final boolean isTestHarness = ActivityManager.isRunningInTestHarness();
        final boolean fileOverride = (new File(ENABLE_MENU_KEY_FILE)).exists();
        return !configDisabled || isTestHarness || fileOverride;
    }

    public void setViewMediatorCallback(ViewMediatorCallback viewMediatorCallback) {
        mViewMediatorCallback = viewMediatorCallback;
        // Update ViewMediator with the current input method requirements
        mViewMediatorCallback.setNeedsInput(mSecurityContainer.needsInput());
    }

    public void setLockPatternUtils(LockPatternUtils utils) {
        mLockPatternUtils = utils;
        mSecurityContainer.setLockPatternUtils(utils);
    }

    public SecurityMode getSecurityMode() {
        return mSecurityContainer.getSecurityMode();
    }

    public SecurityMode getCurrentSecurityMode() {
        return mSecurityContainer.getCurrentSecurityMode();
    }

    /**
     * When bouncer was visible and is starting to become hidden.
     */
    public void onStartingToHide() {
        mSecurityContainer.onStartingToHide();
    }
}
+460 −0

File added.

Preview size limit exceeded, changes collapsed.

+68 −0
Original line number Diff line number Diff line
/*
 * 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.keyguard;

import com.android.systemui.statusbar.policy.ConfigurationController;
import com.android.systemui.util.ViewController;

import javax.inject.Inject;

/** Controller for a {@link KeyguardMessageAreaController}. */
public class KeyguardMessageAreaController extends ViewController<KeyguardMessageArea> {
    private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
    private final ConfigurationController mConfigurationController;

    private KeyguardMessageAreaController(KeyguardMessageArea view,
            KeyguardUpdateMonitor keyguardUpdateMonitor,
            ConfigurationController configurationController) {
        super(view);

        mKeyguardUpdateMonitor = keyguardUpdateMonitor;
        mConfigurationController = configurationController;
    }

    @Override
    protected void onViewAttached() {
        //mConfigurationController.addCallback();
        //mKeyguardUpdateMonitor.registerCallback();
    }

    @Override
    protected void onViewDetached() {
        //mConfigurationController.removeCallback();
        //mKeyguardUpdateMonitor.removeCallback();
    }

    /** Factory for createing {@link com.android.keyguard.KeyguardMessageAreaController}. */
    public static class Factory {
        private final KeyguardUpdateMonitor mKeyguardUpdateMonitor;
        private final ConfigurationController mConfigurationController;

        @Inject
        public Factory(KeyguardUpdateMonitor keyguardUpdateMonitor,
                ConfigurationController configurationController) {
            mKeyguardUpdateMonitor = keyguardUpdateMonitor;
            mConfigurationController = configurationController;
        }

        /** Build a new {@link KeyguardMessageAreaController}. */
        public KeyguardMessageAreaController create(KeyguardMessageArea view) {
            return new KeyguardMessageAreaController(
                    view, mKeyguardUpdateMonitor, mConfigurationController);
        }
    }
}
Loading