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

Commit 39fcf4f8 authored by Alejandro Nijamkin's avatar Alejandro Nijamkin
Browse files

KeyguardBottomAreaView new impl. - data layer

- In this first CL in the series, we create the data layer that will power
the new implementation of KeyguardBottomAreaView
- On its own, this CL doesn't do anything and nothing is using it
- The data layer contains both the repositories and models
- There are two repositories:
  - The generic KeyguardRepository is responsible for providing generic
    application state that the view will ultimately consume. Note that
    some states are very specific to the bottom area and/or the view -
    this was done mostly as a shortcut because the current codebase
    makes it intractable to trace the true source of truth for these.
    Later, when things get cleaned up across the codebase, the
    repository can be changed to source the state from the real source
    of truth without making changes to its consumers
  - The specific KeyguardQuickAffordanceRepository is responsible for
    providing "quick affordance" (previously known as "bottom button")
    state. It creates a simple framework for defining the configuration
    for each of these buttons, positioning them on the left/right, and
    deciding which one takes precedence when more than on is available
    at the same position on the screen

Bug: 235403546
Test: Unit tests are included for both repositories and for two out of
the three quick affordance data sources. It was not possible to add a
unit test for the "home controls" data source because of my use of
`combine` which seems to not play nice with test coroutine scopes. I
also manually verified that the bottom area behaves as expected once the
entire CL chain is compiled together

Change-Id: I90f8ccf0233c8a089b564a831f14157bf2096c31
parent c738a8cc
Loading
Loading
Loading
Loading
+54 −0
Original line number Diff line number Diff line
/*
 *  Copyright (C) 2022 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.systemui.common.coroutine

import android.util.Log
import kotlinx.coroutines.channels.SendChannel
import kotlinx.coroutines.channels.onFailure

object ChannelExt {

    /**
     * Convenience wrapper around [SendChannel.trySend] that also logs on failure. This is the
     * equivalent of calling:
     *
     * ```
     * sendChannel.trySend(element).onFailure {
     *     Log.e(
     *         loggingTag,
     *         "Failed to send $elementDescription" +
     *             " - downstream canceled or failed.",
     *          it,
     *    )
     *}
     * ```
     */
    fun <T> SendChannel<T>.trySendWithFailureLogging(
        element: T,
        loggingTag: String,
        elementDescription: String = "updated state",
    ) {
        trySend(element).onFailure {
            Log.e(
                loggingTag,
                "Failed to send $elementDescription - downstream canceled or failed.",
                it,
            )
        }
    }
}
+40 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.systemui.common.coroutine

import kotlin.experimental.ExperimentalTypeInference
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.ProducerScope
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.buffer
import kotlinx.coroutines.flow.callbackFlow

object ConflatedCallbackFlow {

    /**
     * A [callbackFlow] that uses a buffer [Channel] that is "conflated" meaning that, if
     * backpressure occurs (if the producer that emits new values into the flow is faster than the
     * consumer(s) of the values in the flow), the values are buffered and, if the buffer fills up,
     * we drop the oldest values automatically instead of suspending the producer.
     */
    @Suppress("EXPERIMENTAL_IS_NOT_ENABLED")
    @OptIn(ExperimentalTypeInference::class, ExperimentalCoroutinesApi::class)
    fun <T> conflatedCallbackFlow(
        @BuilderInference block: suspend ProducerScope<T>.() -> Unit,
    ): Flow<T> = callbackFlow(block).buffer(capacity = Channel.CONFLATED)
}
+23 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2022 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.systemui.common.data.model

/** Models a two-dimensional position */
data class Position(
    val x: Int,
    val y: Int,
)
+27 −0
Original line number Diff line number Diff line
/*
 *  Copyright (C) 2022 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.systemui.containeddrawable

import android.graphics.drawable.Drawable
import androidx.annotation.DrawableRes

/** Convenience container for [Drawable] or a way to load it later. */
sealed class ContainedDrawable {
    data class WithDrawable(val drawable: Drawable) : ContainedDrawable()
    data class WithResource(@DrawableRes val resourceId: Int) : ContainedDrawable()
}
+10 −7
Original line number Diff line number Diff line
@@ -21,20 +21,22 @@ import android.content.Context
import android.database.ContentObserver
import android.os.UserHandle
import android.provider.Settings
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.systemui.controls.controller.ControlsController
import com.android.systemui.controls.controller.ControlsTileResourceConfiguration
import com.android.systemui.controls.controller.ControlsTileResourceConfigurationImpl
import com.android.systemui.controls.management.ControlsListingController
import com.android.systemui.controls.ui.ControlsUiController
import com.android.systemui.dagger.SysUISingleton
import com.android.systemui.settings.UserTracker
import com.android.systemui.statusbar.policy.KeyguardStateController
import com.android.systemui.util.settings.SecureSettings
import com.android.internal.widget.LockPatternUtils
import com.android.internal.widget.LockPatternUtils.StrongAuthTracker.STRONG_AUTH_REQUIRED_AFTER_BOOT
import com.android.systemui.controls.controller.ControlsTileResourceConfiguration
import com.android.systemui.controls.controller.ControlsTileResourceConfigurationImpl
import dagger.Lazy
import java.util.Optional
import javax.inject.Inject
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.asStateFlow

/**
 * Pseudo-component to inject into classes outside `com.android.systemui.controls`.
@@ -59,7 +61,8 @@ class ControlsComponent @Inject constructor(
    private val contentResolver: ContentResolver
        get() = context.contentResolver

    private var canShowWhileLockedSetting = false
    private val _canShowWhileLockedSetting = MutableStateFlow(false)
    val canShowWhileLockedSetting = _canShowWhileLockedSetting.asStateFlow()

    private val controlsTileResourceConfiguration: ControlsTileResourceConfiguration =
        optionalControlsTileResourceConfiguration.orElse(
@@ -117,7 +120,7 @@ class ControlsComponent @Inject constructor(
                == STRONG_AUTH_REQUIRED_AFTER_BOOT) {
            return Visibility.AVAILABLE_AFTER_UNLOCK
        }
        if (!canShowWhileLockedSetting && !keyguardStateController.isUnlocked()) {
        if (!canShowWhileLockedSetting.value && !keyguardStateController.isUnlocked()) {
            return Visibility.AVAILABLE_AFTER_UNLOCK
        }

@@ -125,7 +128,7 @@ class ControlsComponent @Inject constructor(
    }

    private fun updateShowWhileLocked() {
        canShowWhileLockedSetting = secureSettings.getIntForUser(
        _canShowWhileLockedSetting.value = secureSettings.getIntForUser(
            Settings.Secure.LOCKSCREEN_SHOW_CONTROLS, 0, UserHandle.USER_CURRENT) != 0
    }

Loading