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

Commit 5c8c3ca1 authored by Android Build Coastguard Worker's avatar Android Build Coastguard Worker
Browse files

Snap for 10347638 from d75df52b to udc-qpr1-release

Change-Id: I1f77e59248eb5eb138686460f14062b0c120380a
parents 561a610d d75df52b
Loading
Loading
Loading
Loading
+8 −2
Original line number Diff line number Diff line
@@ -28,6 +28,7 @@ import android.graphics.drawable.Drawable;
import android.os.Parcel;
import android.os.Parcelable;
import android.os.SystemProperties;
import android.os.Trace;
import android.util.Log;
import android.util.MathUtils;
import android.util.Size;
@@ -144,6 +145,7 @@ public final class WallpaperColors implements Parcelable {
            throw new IllegalArgumentException("Drawable cannot be null");
        }

        Trace.beginSection("WallpaperColors#fromDrawable");
        Rect initialBounds = drawable.copyBounds();
        int width = drawable.getIntrinsicWidth();
        int height = drawable.getIntrinsicHeight();
@@ -165,6 +167,7 @@ public final class WallpaperColors implements Parcelable {
        bitmap.recycle();

        drawable.setBounds(initialBounds);
        Trace.endSection();
        return colors;
    }

@@ -195,7 +198,7 @@ public final class WallpaperColors implements Parcelable {
    public static WallpaperColors fromBitmap(@NonNull Bitmap bitmap,
            @FloatRange (from = 0f, to = 1f) float dimAmount) {
        Objects.requireNonNull(bitmap, "Bitmap can't be null");

        Trace.beginSection("WallpaperColors#fromBitmap");
        final int bitmapArea = bitmap.getWidth() * bitmap.getHeight();
        boolean shouldRecycle = false;
        if (bitmapArea > MAX_WALLPAPER_EXTRACTION_AREA) {
@@ -247,6 +250,7 @@ public final class WallpaperColors implements Parcelable {
            bitmap.recycle();
        }

        Trace.endSection();
        return new WallpaperColors(populationByColor, HINT_FROM_BITMAP | hints);
    }

@@ -462,7 +466,7 @@ public final class WallpaperColors implements Parcelable {
     * Gets the most visually representative color of the wallpaper.
     * "Visually representative" means easily noticeable in the image,
     * probably happening at high frequency.
     *
     *fromBitmap
     * @return A color.
     */
    public @NonNull Color getPrimaryColor() {
@@ -545,6 +549,7 @@ public final class WallpaperColors implements Parcelable {
            return 0;
        }

        Trace.beginSection("WallpaperColors#calculateDarkHints");
        dimAmount = MathUtils.saturate(dimAmount);
        int[] pixels = new int[source.getWidth() * source.getHeight()];
        double totalLuminance = 0;
@@ -607,6 +612,7 @@ public final class WallpaperColors implements Parcelable {
                    " maxD: " + maxDarkPixels + " numPixels: " + pixels.length);
        }

        Trace.endSection();
        return hints;
    }

+0 −27
Original line number Diff line number Diff line
<!--
     Copyright (C) 2019 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.
-->
<!-- This drawable should only be used by the Bluetooth application for its share target icon. -->
<vector xmlns:android="http://schemas.android.com/apk/res/android"
    android:width="24dp"
    android:height="24dp"
    android:viewportWidth="24"
    android:viewportHeight="24"
    android:tint="@*android:color/accent_device_default_light">

    <path
        android:fillColor="@android:color/white"
        android:pathData="M17.71,7.71L12,2h-1v7.59L6.41,5L5,6.41L10.59,12L5,17.59L6.41,19L11,14.41V22h1l5.71-5.71L13.41,12L17.71,7.71z M13,5.83 l1.88,1.88L13,9.59V5.83z M14.88,16.29L13,18.17v-3.76L14.88,16.29z" />
</vector>
+0 −1
Original line number Diff line number Diff line
@@ -21,7 +21,6 @@
    <item>@*android:drawable/ic_audio_alarm</item>
    <item>@*android:drawable/ic_audio_alarm_mute</item>
    <item>@*android:drawable/ic_battery_80_24dp</item>
    <item>@*android:drawable/ic_bluetooth_share_icon</item>
    <item>@*android:drawable/ic_bt_headphones_a2dp</item>
    <item>@*android:drawable/ic_bt_headset_hfp</item>
    <item>@*android:drawable/ic_bt_hearing_aid</item>
+0 −1
Original line number Diff line number Diff line
@@ -44,7 +44,6 @@

        <item>android:drawable/ic_audio_alarm</item>
        <item>android:drawable/ic_audio_alarm_mute</item>
        <item>android:drawable/ic_bluetooth_share_icon</item>
        <item>android:drawable/ic_bt_headphones_a2dp</item>
        <item>android:drawable/ic_bt_headset_hfp</item>
        <item>android:drawable/ic_bt_hearing_aid</item>
+175 −92
Original line number Diff line number Diff line
@@ -21,11 +21,14 @@ package com.android.systemui.bouncer.ui.composable
import android.view.HapticFeedbackConstants
import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.animation.animateColorAsState
import androidx.compose.animation.core.Animatable
import androidx.compose.animation.core.AnimationSpec
import androidx.compose.animation.core.AnimationVector1D
import androidx.compose.animation.core.MutableTransitionState
import androidx.compose.animation.core.Transition
import androidx.compose.animation.core.animateDp
import androidx.compose.animation.core.animateDpAsState
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.animation.core.keyframes
import androidx.compose.animation.core.snap
import androidx.compose.animation.core.tween
@@ -58,6 +61,7 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.drawBehind
import androidx.compose.ui.geometry.CornerRadius
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.platform.LocalView
@@ -67,6 +71,7 @@ import androidx.compose.ui.unit.dp
import com.android.compose.animation.Easings
import com.android.compose.grid.VerticalGrid
import com.android.systemui.R
import com.android.systemui.bouncer.ui.viewmodel.ActionButtonAppearance
import com.android.systemui.bouncer.ui.viewmodel.EnteredKey
import com.android.systemui.bouncer.ui.viewmodel.PinBouncerViewModel
import com.android.systemui.common.shared.model.ContentDescription
@@ -76,6 +81,7 @@ import com.android.systemui.compose.modifiers.thenIf
import kotlin.time.Duration.Companion.milliseconds
import kotlin.time.DurationUnit
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch

@@ -87,78 +93,13 @@ internal fun PinBouncer(
    // Report that the UI is shown to let the view-model run some logic.
    LaunchedEffect(Unit) { viewModel.onShown() }

    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()

    // Show the failure animation if the user entered the wrong input.
    LaunchedEffect(animateFailure) {
        if (animateFailure) {
            showFailureAnimation()
            viewModel.onFailureAnimationShown()
        }
    }

    Column(
        horizontalAlignment = Alignment.CenterHorizontally,
        modifier = modifier,
    ) {
        PinInputDisplay(viewModel)

        Spacer(Modifier.height(100.dp))

        VerticalGrid(
            columns = 3,
            verticalSpacing = 12.dp,
            horizontalSpacing = 20.dp,
        ) {
            repeat(9) { index ->
                val digit = index + 1
                PinButton(
                    onClicked = { viewModel.onPinButtonClicked(digit) },
                    isEnabled = isInputEnabled,
                ) { contentColor ->
                    PinDigit(digit, contentColor)
                }
            }

            PinButton(
                onClicked = { viewModel.onBackspaceButtonClicked() },
                onLongPressed = { viewModel.onBackspaceButtonLongPressed() },
                isEnabled = isInputEnabled,
                isIconButton = true,
            ) { contentColor ->
                PinIcon(
                    Icon.Resource(
                        res = R.drawable.ic_backspace_24dp,
                        contentDescription =
                            ContentDescription.Resource(R.string.keyboardview_keycode_delete),
                    ),
                    contentColor,
                )
            }

            PinButton(
                onClicked = { viewModel.onPinButtonClicked(0) },
                isEnabled = isInputEnabled,
            ) { contentColor ->
                PinDigit(0, contentColor)
            }

            PinButton(
                onClicked = { viewModel.onAuthenticateButtonClicked() },
                isEnabled = isInputEnabled,
                isIconButton = true,
            ) { contentColor ->
                PinIcon(
                    Icon.Resource(
                        res = R.drawable.ic_keyboard_tab_36dp,
                        contentDescription =
                            ContentDescription.Resource(R.string.keyboardview_keycode_enter),
                    ),
                    contentColor,
                )
            }
        }
        PinPad(viewModel)
    }
}

@@ -305,38 +246,153 @@ private fun ObscuredInputEntry(transition: Transition<EntryVisibility>) {
}

@Composable
private fun PinDigit(
private fun PinPad(viewModel: PinBouncerViewModel) {
    val isInputEnabled: Boolean by viewModel.isInputEnabled.collectAsState()
    val backspaceButtonAppearance by viewModel.backspaceButtonAppearance.collectAsState()
    val confirmButtonAppearance by viewModel.confirmButtonAppearance.collectAsState()
    val animateFailure: Boolean by viewModel.animateFailure.collectAsState()

    val buttonScaleAnimatables = remember { List(12) { Animatable(1f) } }
    LaunchedEffect(animateFailure) {
        // Show the failure animation if the user entered the wrong input.
        if (animateFailure) {
            showFailureAnimation(buttonScaleAnimatables)
            viewModel.onFailureAnimationShown()
        }
    }

    VerticalGrid(
        columns = 3,
        verticalSpacing = 12.dp,
        horizontalSpacing = 20.dp,
    ) {
        repeat(9) { index ->
            DigitButton(
                index + 1,
                isInputEnabled,
                viewModel::onPinButtonClicked,
                buttonScaleAnimatables[index]::value,
            )
        }

        ActionButton(
            icon =
                Icon.Resource(
                    res = R.drawable.ic_backspace_24dp,
                    contentDescription =
                        ContentDescription.Resource(R.string.keyboardview_keycode_delete),
                ),
            isInputEnabled = isInputEnabled,
            onClicked = viewModel::onBackspaceButtonClicked,
            onLongPressed = viewModel::onBackspaceButtonLongPressed,
            appearance = backspaceButtonAppearance,
            scaling = buttonScaleAnimatables[9]::value,
        )

        DigitButton(
            0,
            isInputEnabled,
            viewModel::onPinButtonClicked,
            buttonScaleAnimatables[10]::value,
        )

        ActionButton(
            icon =
                Icon.Resource(
                    res = R.drawable.ic_keyboard_tab_36dp,
                    contentDescription =
                        ContentDescription.Resource(R.string.keyboardview_keycode_enter),
                ),
            isInputEnabled = isInputEnabled,
            onClicked = viewModel::onAuthenticateButtonClicked,
            appearance = confirmButtonAppearance,
            scaling = buttonScaleAnimatables[11]::value,
        )
    }
}

@Composable
private fun DigitButton(
    digit: Int,
    contentColor: Color,
    isInputEnabled: Boolean,
    onClicked: (Int) -> Unit,
    scaling: () -> Float,
) {
    // TODO(b/281878426): once "color: () -> Color" (added to BasicText in aosp/2568972) makes it
    //  into Text, use that here, to animate more efficiently.
    PinPadButton(
        onClicked = { onClicked(digit) },
        isEnabled = isInputEnabled,
        backgroundColor = MaterialTheme.colorScheme.surfaceVariant,
        foregroundColor = MaterialTheme.colorScheme.onSurfaceVariant,
        modifier =
            Modifier.graphicsLayer {
                val scale = scaling()
                scaleX = scale
                scaleY = scale
            }
    ) { contentColor ->
        // TODO(b/281878426): once "color: () -> Color" (added to BasicText in aosp/2568972) makes
        // it into Text, use that here, to animate more efficiently.
        Text(
            text = digit.toString(),
            style = MaterialTheme.typography.headlineLarge,
        color = contentColor,
            color = contentColor(),
        )
    }
}

@Composable
private fun PinIcon(
private fun ActionButton(
    icon: Icon,
    contentColor: Color,
    isInputEnabled: Boolean,
    onClicked: () -> Unit,
    onLongPressed: (() -> Unit)? = null,
    appearance: ActionButtonAppearance,
    scaling: () -> Float,
) {
    val isHidden = appearance == ActionButtonAppearance.Hidden
    val hiddenAlpha by animateFloatAsState(if (isHidden) 0f else 1f, label = "Action button alpha")

    val foregroundColor =
        when (appearance) {
            ActionButtonAppearance.Shown -> MaterialTheme.colorScheme.onSecondaryContainer
            else -> MaterialTheme.colorScheme.onSurface
        }
    val backgroundColor =
        when (appearance) {
            ActionButtonAppearance.Shown -> MaterialTheme.colorScheme.secondaryContainer
            else -> MaterialTheme.colorScheme.surface
        }

    PinPadButton(
        onClicked = onClicked,
        onLongPressed = onLongPressed,
        isEnabled = isInputEnabled && !isHidden,
        backgroundColor = backgroundColor,
        foregroundColor = foregroundColor,
        modifier =
            Modifier.graphicsLayer {
                alpha = hiddenAlpha
                val scale = scaling()
                scaleX = scale
                scaleY = scale
            }
    ) { contentColor ->
        Icon(
            icon = icon,
        tint = contentColor,
            tint = contentColor(),
        )
    }
}

@Composable
private fun PinButton(
private fun PinPadButton(
    onClicked: () -> Unit,
    isEnabled: Boolean,
    backgroundColor: Color,
    foregroundColor: Color,
    modifier: Modifier = Modifier,
    onLongPressed: (() -> Unit)? = null,
    isIconButton: Boolean = false,
    content: @Composable (contentColor: Color) -> Unit,
    content: @Composable (contentColor: () -> Color) -> Unit,
) {
    var isPressed: Boolean by remember { mutableStateOf(false) }

@@ -370,18 +426,16 @@ private fun PinButton(
        animateColorAsState(
            when {
                isPressed -> MaterialTheme.colorScheme.primary
                isIconButton -> MaterialTheme.colorScheme.secondaryContainer
                else -> MaterialTheme.colorScheme.surfaceVariant
                else -> backgroundColor
            },
            label = "Pin button container color",
            animationSpec = colorAnimationSpec
        )
    val contentColor: Color by
    val contentColor =
        animateColorAsState(
            when {
                isPressed -> MaterialTheme.colorScheme.onPrimary
                isIconButton -> MaterialTheme.colorScheme.onSecondaryContainer
                else -> MaterialTheme.colorScheme.onSurfaceVariant
                else -> foregroundColor
            },
            label = "Pin button container color",
            animationSpec = colorAnimationSpec
@@ -420,17 +474,46 @@ private fun PinButton(
                    }
                },
    ) {
        content(contentColor)
        content(contentColor::value)
    }
}

private fun showFailureAnimation() {
    // TODO(b/282730134): implement.
private suspend fun showFailureAnimation(
    buttonScaleAnimatables: List<Animatable<Float, AnimationVector1D>>
) {
    coroutineScope {
        buttonScaleAnimatables.forEachIndexed { index, animatable ->
            launch {
                animatable.animateTo(
                    targetValue = pinButtonErrorShrinkFactor,
                    animationSpec =
                        tween(
                            durationMillis = pinButtonErrorShrinkMs,
                            delayMillis = index * pinButtonErrorStaggerDelayMs,
                            easing = Easings.Linear,
                        ),
                )

                animatable.animateTo(
                    targetValue = 1f,
                    animationSpec =
                        tween(
                            durationMillis = pinButtonErrorRevertMs,
                            easing = Easings.Legacy,
                        ),
                )
            }
        }
    }
}

private val entryShapeSize = 16.dp

private val pinButtonSize = 84.dp
private val pinButtonErrorShrinkFactor = 67.dp / pinButtonSize
private const val pinButtonErrorShrinkMs = 50
private const val pinButtonErrorStaggerDelayMs = 33
private const val pinButtonErrorRevertMs = 617

// Pin button motion spec: http://shortn/_9TTIG6SoEa
private val pinButtonPressedDuration = 100.milliseconds
Loading