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

Commit 564e2bcd authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 10174855 from ecfb1b5e to udc-qpr1-release

Change-Id: I98cf701e0e611d8ad9b747893a71ccb9d4bd4d2a
parents c7af8495 ecfb1b5e
Loading
Loading
Loading
Loading
+14 −0
Original line number Diff line number Diff line
@@ -2882,6 +2882,20 @@ public abstract class PackageManager {
    public static final String FEATURE_CAR_TEMPLATES_HOST =
            "android.software.car.templates_host";

    /**
     * Feature for {@link #getSystemAvailableFeatures} and {@link #hasSystemFeature}:If this
     * feature is supported, the device should also declare {@link #FEATURE_AUTOMOTIVE} and show
     * a UI that can display multiple tasks at the same time on a single display. The user can
     * perform multiple actions on different tasks simultaneously. Apps open in split screen mode
     * by default, instead of full screen. Unlike Android's multi-window mode, where users can
     * choose how to display apps, the device determines how apps are shown.
     *
     * @hide
     */
    @SdkConstant(SdkConstantType.FEATURE)
    public static final String FEATURE_CAR_SPLITSCREEN_MULTITASKING =
            "android.software.car.splitscreen_multitasking";

    /**
     * Feature for {@link #getSystemAvailableFeatures} and
     * {@link #hasSystemFeature(String, int)}: If this feature is supported, the device supports
+12 −3
Original line number Diff line number Diff line
@@ -65,9 +65,18 @@ public class PhonePipKeepClearAlgorithm implements PipKeepClearAlgorithmInterfac
        }
        Rect pipBounds = new Rect(startingBounds);

        // move PiP towards corner if user hasn't moved it manually or the flag is on
        if (mKeepClearAreaGravityEnabled
                || (!pipBoundsState.hasUserMovedPip() && !pipBoundsState.hasUserResizedPip())) {
        boolean shouldApplyGravity = false;
        // if PiP is outside of screen insets, reposition using gravity
        if (!insets.contains(pipBounds)) {
            shouldApplyGravity = true;
        }
        // if user has not interacted with PiP, reposition using gravity
        if (!pipBoundsState.hasUserMovedPip() && !pipBoundsState.hasUserResizedPip()) {
            shouldApplyGravity = true;
        }

        // apply gravity that will position PiP in bottom left or bottom right corner within insets
        if (mKeepClearAreaGravityEnabled || shouldApplyGravity) {
            float snapFraction = pipBoundsAlgorithm.getSnapFraction(startingBounds);
            int verticalGravity = Gravity.BOTTOM;
            int horizontalGravity;
+13 −1
Original line number Diff line number Diff line
@@ -19,9 +19,12 @@ package com.android.systemui.biometrics.ui;
import android.annotation.NonNull;
import android.annotation.Nullable;
import android.content.Context;
import android.graphics.Insets;
import android.util.AttributeSet;
import android.util.Log;
import android.view.View;
import android.view.WindowInsets;
import android.view.WindowManager;
import android.widget.FrameLayout;
import android.widget.LinearLayout;
import android.widget.TextView;
@@ -44,6 +47,8 @@ public class BiometricPromptLayout extends LinearLayout {

    private static final String TAG = "BiometricPromptLayout";

    @NonNull
    private final WindowManager mWindowManager;
    @Nullable
    private AuthController.ScaleFactorProvider mScaleFactorProvider;
    @Nullable
@@ -60,6 +65,8 @@ public class BiometricPromptLayout extends LinearLayout {
    public BiometricPromptLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        mWindowManager = context.getSystemService(WindowManager.class);

        mUseCustomBpSize = getResources().getBoolean(R.bool.use_custom_bp_size);
        mCustomBpWidth = getResources().getDimensionPixelSize(R.dimen.biometric_dialog_width);
        mCustomBpHeight = getResources().getDimensionPixelSize(R.dimen.biometric_dialog_height);
@@ -144,8 +151,13 @@ public class BiometricPromptLayout extends LinearLayout {
            width = Math.min(width, height);
        }

        // add nav bar insets since the parent AuthContainerView
        // uses LAYOUT_IN_DISPLAY_CUTOUT_MODE_ALWAYS
        final Insets insets = mWindowManager.getMaximumWindowMetrics().getWindowInsets()
                .getInsets(WindowInsets.Type.navigationBars());
        final AuthDialog.LayoutParams params = onMeasureInternal(width, height);
        setMeasuredDimension(params.mMediumWidth, params.mMediumHeight);
        setMeasuredDimension(params.mMediumWidth + insets.left + insets.right,
                params.mMediumHeight + insets.bottom);
    }

    @Override
+11 −1
Original line number Diff line number Diff line
@@ -16,4 +16,14 @@

package com.android.systemui.bouncer.ui.viewmodel

sealed interface AuthMethodBouncerViewModel
import kotlinx.coroutines.flow.StateFlow

sealed interface AuthMethodBouncerViewModel {
    /**
     * Whether user input is enabled.
     *
     * If `false`, user input should be completely ignored in the UI as the user is "locked out" of
     * being able to attempt to unlock the device.
     */
    val isInputEnabled: StateFlow<Boolean>
}
+80 −0
Original line number Diff line number Diff line
@@ -17,6 +17,7 @@
package com.android.systemui.bouncer.ui.viewmodel

import android.content.Context
import com.android.systemui.R
import com.android.systemui.authentication.shared.model.AuthenticationMethodModel
import com.android.systemui.bouncer.domain.interactor.BouncerInteractor
import com.android.systemui.dagger.qualifiers.Application
@@ -24,10 +25,14 @@ import dagger.assisted.Assisted
import dagger.assisted.AssistedFactory
import dagger.assisted.AssistedInject
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow
import kotlinx.coroutines.flow.distinctUntilChanged
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch

/** Holds UI state and handles user input on bouncer UIs. */
class BouncerViewModel
@@ -40,16 +45,42 @@ constructor(
) {
    private val interactor: BouncerInteractor = interactorFactory.create(containerName)

    /**
     * Whether updates to the message should be cross-animated from one message to another.
     *
     * If `false`, no animation should be applied, the message text should just be replaced
     * instantly.
     */
    val isMessageUpdateAnimationsEnabled: StateFlow<Boolean> =
        interactor.throttling
            .map { it == null }
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = interactor.throttling.value == null,
            )

    private val isInputEnabled: StateFlow<Boolean> =
        interactor.throttling
            .map { it == null }
            .stateIn(
                scope = applicationScope,
                started = SharingStarted.WhileSubscribed(),
                initialValue = interactor.throttling.value == null,
            )

    private val pin: PinBouncerViewModel by lazy {
        PinBouncerViewModel(
            applicationScope = applicationScope,
            interactor = interactor,
            isInputEnabled = isInputEnabled,
        )
    }

    private val password: PasswordBouncerViewModel by lazy {
        PasswordBouncerViewModel(
            interactor = interactor,
            isInputEnabled = isInputEnabled,
        )
    }

@@ -58,6 +89,7 @@ constructor(
            applicationContext = applicationContext,
            applicationScope = applicationScope,
            interactor = interactor,
            isInputEnabled = isInputEnabled,
        )
    }

@@ -81,11 +113,59 @@ constructor(
                initialValue = interactor.message.value ?: "",
            )

    private val _throttlingDialogMessage = MutableStateFlow<String?>(null)
    /**
     * A message for a throttling dialog to show when the user has attempted the wrong credential
     * too many times and now must wait a while before attempting again.
     *
     * If `null`, no dialog should be shown.
     *
     * Once the dialog is shown, the UI should call [onThrottlingDialogDismissed] when the user
     * dismisses this dialog.
     */
    val throttlingDialogMessage: StateFlow<String?> = _throttlingDialogMessage.asStateFlow()

    init {
        applicationScope.launch {
            interactor.throttling
                .map { model ->
                    model?.let {
                        when (interactor.authenticationMethod.value) {
                            is AuthenticationMethodModel.PIN ->
                                R.string.kg_too_many_failed_pin_attempts_dialog_message
                            is AuthenticationMethodModel.Password ->
                                R.string.kg_too_many_failed_password_attempts_dialog_message
                            is AuthenticationMethodModel.Pattern ->
                                R.string.kg_too_many_failed_pattern_attempts_dialog_message
                            else -> null
                        }?.let { stringResourceId ->
                            applicationContext.getString(
                                stringResourceId,
                                model.failedAttemptCount,
                                model.totalDurationSec,
                            )
                        }
                    }
                }
                .distinctUntilChanged()
                .collect { dialogMessageOrNull ->
                    if (dialogMessageOrNull != null) {
                        _throttlingDialogMessage.value = dialogMessageOrNull
                    }
                }
        }
    }

    /** Notifies that the emergency services button was clicked. */
    fun onEmergencyServicesButtonClicked() {
        // TODO(b/280877228): implement this
    }

    /** Notifies that a throttling dialog has been dismissed by the user. */
    fun onThrottlingDialogDismissed() {
        _throttlingDialogMessage.value = null
    }

    private fun toViewModel(
        authMethod: AuthenticationMethodModel,
    ): AuthMethodBouncerViewModel? {
Loading