diff --git a/packages/SystemUI/compose/core/src/com/android/compose/swipeable/Swipeable.kt b/packages/SystemUI/compose/core/src/com/android/compose/swipeable/Swipeable.kt deleted file mode 100644 index 946e77959b1cb..0000000000000 --- a/packages/SystemUI/compose/core/src/com/android/compose/swipeable/Swipeable.kt +++ /dev/null @@ -1,849 +0,0 @@ -/* - * Copyright (C) 2023 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.compose.swipeable - -import androidx.compose.animation.core.Animatable -import androidx.compose.animation.core.AnimationSpec -import androidx.compose.animation.core.SpringSpec -import androidx.compose.foundation.gestures.DraggableState -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.gestures.draggable -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.runtime.Composable -import androidx.compose.runtime.DisposableEffect -import androidx.compose.runtime.Immutable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.Stable -import androidx.compose.runtime.State -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.saveable.Saver -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.runtime.snapshotFlow -import androidx.compose.ui.Modifier -import androidx.compose.ui.composed -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.input.nestedscroll.NestedScrollConnection -import androidx.compose.ui.input.nestedscroll.NestedScrollSource -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.debugInspectorInfo -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.Velocity -import androidx.compose.ui.unit.dp -import com.android.compose.swipeable.SwipeableDefaults.AnimationSpec -import com.android.compose.swipeable.SwipeableDefaults.StandardResistanceFactor -import com.android.compose.swipeable.SwipeableDefaults.VelocityThreshold -import com.android.compose.swipeable.SwipeableDefaults.resistanceConfig -import com.android.compose.ui.util.lerp -import kotlin.math.PI -import kotlin.math.abs -import kotlin.math.sign -import kotlin.math.sin -import kotlinx.coroutines.CancellationException -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.filter -import kotlinx.coroutines.flow.take -import kotlinx.coroutines.launch - -/** - * State of the [swipeable] modifier. - * - * This contains necessary information about any ongoing swipe or animation and provides methods to - * change the state either immediately or by starting an animation. To create and remember a - * [SwipeableState] with the default animation clock, use [rememberSwipeableState]. - * - * @param initialValue The initial value of the state. - * @param animationSpec The default animation that will be used to animate to a new state. - * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change. - * - * TODO(b/272311106): this is a fork from material. Unfork it when Swipeable.kt reaches material3. - */ -@Stable -open class SwipeableState( - initialValue: T, - internal val animationSpec: AnimationSpec = AnimationSpec, - internal val confirmStateChange: (newValue: T) -> Boolean = { true } -) { - /** - * The current value of the state. - * - * If no swipe or animation is in progress, this corresponds to the anchor at which the - * [swipeable] is currently settled. If a swipe or animation is in progress, this corresponds - * the last anchor at which the [swipeable] was settled before the swipe or animation started. - */ - var currentValue: T by mutableStateOf(initialValue) - private set - - /** Whether the state is currently animating. */ - var isAnimationRunning: Boolean by mutableStateOf(false) - private set - - /** - * The current position (in pixels) of the [swipeable]. - * - * You should use this state to offset your content accordingly. The recommended way is to use - * `Modifier.offsetPx`. This includes the resistance by default, if resistance is enabled. - */ - val offset: State - get() = offsetState - - /** The amount by which the [swipeable] has been swiped past its bounds. */ - val overflow: State - get() = overflowState - - // Use `Float.NaN` as a placeholder while the state is uninitialised. - private val offsetState = mutableStateOf(0f) - private val overflowState = mutableStateOf(0f) - - // the source of truth for the "real"(non ui) position - // basically position in bounds + overflow - private val absoluteOffset = mutableStateOf(0f) - - // current animation target, if animating, otherwise null - private val animationTarget = mutableStateOf(null) - - internal var anchors by mutableStateOf(emptyMap()) - - private val latestNonEmptyAnchorsFlow: Flow> = - snapshotFlow { anchors }.filter { it.isNotEmpty() }.take(1) - - internal var minBound = Float.NEGATIVE_INFINITY - internal var maxBound = Float.POSITIVE_INFINITY - - internal fun ensureInit(newAnchors: Map) { - if (anchors.isEmpty()) { - // need to do initial synchronization synchronously :( - val initialOffset = newAnchors.getOffset(currentValue) - requireNotNull(initialOffset) { "The initial value must have an associated anchor." } - offsetState.value = initialOffset - absoluteOffset.value = initialOffset - } - } - - internal suspend fun processNewAnchors(oldAnchors: Map, newAnchors: Map) { - if (oldAnchors.isEmpty()) { - // If this is the first time that we receive anchors, then we need to initialise - // the state so we snap to the offset associated to the initial value. - minBound = newAnchors.keys.minOrNull()!! - maxBound = newAnchors.keys.maxOrNull()!! - val initialOffset = newAnchors.getOffset(currentValue) - requireNotNull(initialOffset) { "The initial value must have an associated anchor." } - snapInternalToOffset(initialOffset) - } else if (newAnchors != oldAnchors) { - // If we have received new anchors, then the offset of the current value might - // have changed, so we need to animate to the new offset. If the current value - // has been removed from the anchors then we animate to the closest anchor - // instead. Note that this stops any ongoing animation. - minBound = Float.NEGATIVE_INFINITY - maxBound = Float.POSITIVE_INFINITY - val animationTargetValue = animationTarget.value - // if we're in the animation already, let's find it a new home - val targetOffset = - if (animationTargetValue != null) { - // first, try to map old state to the new state - val oldState = oldAnchors[animationTargetValue] - val newState = newAnchors.getOffset(oldState) - // return new state if exists, or find the closes one among new anchors - newState ?: newAnchors.keys.minByOrNull { abs(it - animationTargetValue) }!! - } else { - // we're not animating, proceed by finding the new anchors for an old value - val actualOldValue = oldAnchors[offset.value] - val value = if (actualOldValue == currentValue) currentValue else actualOldValue - newAnchors.getOffset(value) - ?: newAnchors.keys.minByOrNull { abs(it - offset.value) }!! - } - try { - animateInternalToOffset(targetOffset, animationSpec) - } catch (c: CancellationException) { - // If the animation was interrupted for any reason, snap as a last resort. - snapInternalToOffset(targetOffset) - } finally { - currentValue = newAnchors.getValue(targetOffset) - minBound = newAnchors.keys.minOrNull()!! - maxBound = newAnchors.keys.maxOrNull()!! - } - } - } - - internal var thresholds: (Float, Float) -> Float by mutableStateOf({ _, _ -> 0f }) - - internal var velocityThreshold by mutableStateOf(0f) - - internal var resistance: ResistanceConfig? by mutableStateOf(null) - - internal val draggableState = DraggableState { - val newAbsolute = absoluteOffset.value + it - val clamped = newAbsolute.coerceIn(minBound, maxBound) - val overflow = newAbsolute - clamped - val resistanceDelta = resistance?.computeResistance(overflow) ?: 0f - offsetState.value = clamped + resistanceDelta - overflowState.value = overflow - absoluteOffset.value = newAbsolute - } - - private suspend fun snapInternalToOffset(target: Float) { - draggableState.drag { dragBy(target - absoluteOffset.value) } - } - - private suspend fun animateInternalToOffset(target: Float, spec: AnimationSpec) { - draggableState.drag { - var prevValue = absoluteOffset.value - animationTarget.value = target - isAnimationRunning = true - try { - Animatable(prevValue).animateTo(target, spec) { - dragBy(this.value - prevValue) - prevValue = this.value - } - } finally { - animationTarget.value = null - isAnimationRunning = false - } - } - } - - /** - * The target value of the state. - * - * If a swipe is in progress, this is the value that the [swipeable] would animate to if the - * swipe finished. If an animation is running, this is the target value of that animation. - * Finally, if no swipe or animation is in progress, this is the same as the [currentValue]. - */ - val targetValue: T - get() { - // TODO(calintat): Track current velocity (b/149549482) and use that here. - val target = - animationTarget.value - ?: computeTarget( - offset = offset.value, - lastValue = anchors.getOffset(currentValue) ?: offset.value, - anchors = anchors.keys, - thresholds = thresholds, - velocity = 0f, - velocityThreshold = Float.POSITIVE_INFINITY - ) - return anchors[target] ?: currentValue - } - - /** - * Information about the ongoing swipe or animation, if any. See [SwipeProgress] for details. - * - * If no swipe or animation is in progress, this returns `SwipeProgress(value, value, 1f)`. - */ - val progress: SwipeProgress - get() { - val bounds = findBounds(offset.value, anchors.keys) - val from: T - val to: T - val fraction: Float - when (bounds.size) { - 0 -> { - from = currentValue - to = currentValue - fraction = 1f - } - 1 -> { - from = anchors.getValue(bounds[0]) - to = anchors.getValue(bounds[0]) - fraction = 1f - } - else -> { - val (a, b) = - if (direction > 0f) { - bounds[0] to bounds[1] - } else { - bounds[1] to bounds[0] - } - from = anchors.getValue(a) - to = anchors.getValue(b) - fraction = (offset.value - a) / (b - a) - } - } - return SwipeProgress(from, to, fraction) - } - - /** - * The direction in which the [swipeable] is moving, relative to the current [currentValue]. - * - * This will be either 1f if it is is moving from left to right or top to bottom, -1f if it is - * moving from right to left or bottom to top, or 0f if no swipe or animation is in progress. - */ - val direction: Float - get() = anchors.getOffset(currentValue)?.let { sign(offset.value - it) } ?: 0f - - /** - * Set the state without any animation and suspend until it's set - * - * @param targetValue The new target value to set [currentValue] to. - */ - suspend fun snapTo(targetValue: T) { - latestNonEmptyAnchorsFlow.collect { anchors -> - val targetOffset = anchors.getOffset(targetValue) - requireNotNull(targetOffset) { "The target value must have an associated anchor." } - snapInternalToOffset(targetOffset) - currentValue = targetValue - } - } - - /** - * Set the state to the target value by starting an animation. - * - * @param targetValue The new value to animate to. - * @param anim The animation that will be used to animate to the new value. - */ - suspend fun animateTo(targetValue: T, anim: AnimationSpec = animationSpec) { - latestNonEmptyAnchorsFlow.collect { anchors -> - try { - val targetOffset = anchors.getOffset(targetValue) - requireNotNull(targetOffset) { "The target value must have an associated anchor." } - animateInternalToOffset(targetOffset, anim) - } finally { - val endOffset = absoluteOffset.value - val endValue = - anchors - // fighting rounding error once again, anchor should be as close as 0.5 - // pixels - .filterKeys { anchorOffset -> abs(anchorOffset - endOffset) < 0.5f } - .values - .firstOrNull() - ?: currentValue - currentValue = endValue - } - } - } - - /** - * Perform fling with settling to one of the anchors which is determined by the given - * [velocity]. Fling with settling [swipeable] will always consume all the velocity provided - * since it will settle at the anchor. - * - * In general cases, [swipeable] flings by itself when being swiped. This method is to be used - * for nested scroll logic that wraps the [swipeable]. In nested scroll developer may want to - * trigger settling fling when the child scroll container reaches the bound. - * - * @param velocity velocity to fling and settle with - * @return the reason fling ended - */ - suspend fun performFling(velocity: Float) { - latestNonEmptyAnchorsFlow.collect { anchors -> - val lastAnchor = anchors.getOffset(currentValue)!! - val targetValue = - computeTarget( - offset = offset.value, - lastValue = lastAnchor, - anchors = anchors.keys, - thresholds = thresholds, - velocity = velocity, - velocityThreshold = velocityThreshold - ) - val targetState = anchors[targetValue] - if (targetState != null && confirmStateChange(targetState)) animateTo(targetState) - // If the user vetoed the state change, rollback to the previous state. - else animateInternalToOffset(lastAnchor, animationSpec) - } - } - - /** - * Force [swipeable] to consume drag delta provided from outside of the regular [swipeable] - * gesture flow. - * - * Note: This method performs generic drag and it won't settle to any particular anchor, * - * leaving swipeable in between anchors. When done dragging, [performFling] must be called as - * well to ensure swipeable will settle at the anchor. - * - * In general cases, [swipeable] drags by itself when being swiped. This method is to be used - * for nested scroll logic that wraps the [swipeable]. In nested scroll developer may want to - * force drag when the child scroll container reaches the bound. - * - * @param delta delta in pixels to drag by - * @return the amount of [delta] consumed - */ - fun performDrag(delta: Float): Float { - val potentiallyConsumed = absoluteOffset.value + delta - val clamped = potentiallyConsumed.coerceIn(minBound, maxBound) - val deltaToConsume = clamped - absoluteOffset.value - if (abs(deltaToConsume) > 0) { - draggableState.dispatchRawDelta(deltaToConsume) - } - return deltaToConsume - } - - companion object { - /** The default [Saver] implementation for [SwipeableState]. */ - fun Saver( - animationSpec: AnimationSpec, - confirmStateChange: (T) -> Boolean - ) = - Saver, T>( - save = { it.currentValue }, - restore = { SwipeableState(it, animationSpec, confirmStateChange) } - ) - } -} - -/** - * Collects information about the ongoing swipe or animation in [swipeable]. - * - * To access this information, use [SwipeableState.progress]. - * - * @param from The state corresponding to the anchor we are moving away from. - * @param to The state corresponding to the anchor we are moving towards. - * @param fraction The fraction that the current position represents between [from] and [to]. Must - * be between `0` and `1`. - */ -@Immutable -class SwipeProgress( - val from: T, - val to: T, - /*@FloatRange(from = 0.0, to = 1.0)*/ - val fraction: Float -) { - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is SwipeProgress<*>) return false - - if (from != other.from) return false - if (to != other.to) return false - if (fraction != other.fraction) return false - - return true - } - - override fun hashCode(): Int { - var result = from?.hashCode() ?: 0 - result = 31 * result + (to?.hashCode() ?: 0) - result = 31 * result + fraction.hashCode() - return result - } - - override fun toString(): String { - return "SwipeProgress(from=$from, to=$to, fraction=$fraction)" - } -} - -/** - * Create and [remember] a [SwipeableState] with the default animation clock. - * - * @param initialValue The initial value of the state. - * @param animationSpec The default animation that will be used to animate to a new state. - * @param confirmStateChange Optional callback invoked to confirm or veto a pending state change. - */ -@Composable -fun rememberSwipeableState( - initialValue: T, - animationSpec: AnimationSpec = AnimationSpec, - confirmStateChange: (newValue: T) -> Boolean = { true } -): SwipeableState { - return rememberSaveable( - saver = - SwipeableState.Saver( - animationSpec = animationSpec, - confirmStateChange = confirmStateChange - ) - ) { - SwipeableState( - initialValue = initialValue, - animationSpec = animationSpec, - confirmStateChange = confirmStateChange - ) - } -} - -/** - * Create and [remember] a [SwipeableState] which is kept in sync with another state, i.e.: - * 1. Whenever the [value] changes, the [SwipeableState] will be animated to that new value. - * 2. Whenever the value of the [SwipeableState] changes (e.g. after a swipe), the owner of the - * [value] will be notified to update their state to the new value of the [SwipeableState] by - * invoking [onValueChange]. If the owner does not update their state to the provided value for - * some reason, then the [SwipeableState] will perform a rollback to the previous, correct value. - */ -@Composable -internal fun rememberSwipeableStateFor( - value: T, - onValueChange: (T) -> Unit, - animationSpec: AnimationSpec = AnimationSpec -): SwipeableState { - val swipeableState = remember { - SwipeableState( - initialValue = value, - animationSpec = animationSpec, - confirmStateChange = { true } - ) - } - val forceAnimationCheck = remember { mutableStateOf(false) } - LaunchedEffect(value, forceAnimationCheck.value) { - if (value != swipeableState.currentValue) { - swipeableState.animateTo(value) - } - } - DisposableEffect(swipeableState.currentValue) { - if (value != swipeableState.currentValue) { - onValueChange(swipeableState.currentValue) - forceAnimationCheck.value = !forceAnimationCheck.value - } - onDispose {} - } - return swipeableState -} - -/** - * Enable swipe gestures between a set of predefined states. - * - * To use this, you must provide a map of anchors (in pixels) to states (of type [T]). Note that - * this map cannot be empty and cannot have two anchors mapped to the same state. - * - * When a swipe is detected, the offset of the [SwipeableState] will be updated with the swipe - * delta. You should use this offset to move your content accordingly (see `Modifier.offsetPx`). - * When the swipe ends, the offset will be animated to one of the anchors and when that anchor is - * reached, the value of the [SwipeableState] will also be updated to the state corresponding to the - * new anchor. The target anchor is calculated based on the provided positional [thresholds]. - * - * Swiping is constrained between the minimum and maximum anchors. If the user attempts to swipe - * past these bounds, a resistance effect will be applied by default. The amount of resistance at - * each edge is specified by the [resistance] config. To disable all resistance, set it to `null`. - * - * For an example of a [swipeable] with three states, see: - * - * @param T The type of the state. - * @param state The state of the [swipeable]. - * @param anchors Pairs of anchors and states, used to map anchors to states and vice versa. - * @param thresholds Specifies where the thresholds between the states are. The thresholds will be - * used to determine which state to animate to when swiping stops. This is represented as a lambda - * that takes two states and returns the threshold between them in the form of a - * [ThresholdConfig]. Note that the order of the states corresponds to the swipe direction. - * @param orientation The orientation in which the [swipeable] can be swiped. - * @param enabled Whether this [swipeable] is enabled and should react to the user's input. - * @param reverseDirection Whether to reverse the direction of the swipe, so a top to bottom swipe - * will behave like bottom to top, and a left to right swipe will behave like right to left. - * @param interactionSource Optional [MutableInteractionSource] that will passed on to the internal - * [Modifier.draggable]. - * @param resistance Controls how much resistance will be applied when swiping past the bounds. - * @param velocityThreshold The threshold (in dp per second) that the end velocity has to exceed in - * order to animate to the next state, even if the positional [thresholds] have not been reached. - * @sample androidx.compose.material.samples.SwipeableSample - */ -fun Modifier.swipeable( - state: SwipeableState, - anchors: Map, - orientation: Orientation, - enabled: Boolean = true, - reverseDirection: Boolean = false, - interactionSource: MutableInteractionSource? = null, - thresholds: (from: T, to: T) -> ThresholdConfig = { _, _ -> FixedThreshold(56.dp) }, - resistance: ResistanceConfig? = resistanceConfig(anchors.keys), - velocityThreshold: Dp = VelocityThreshold -) = - composed( - inspectorInfo = - debugInspectorInfo { - name = "swipeable" - properties["state"] = state - properties["anchors"] = anchors - properties["orientation"] = orientation - properties["enabled"] = enabled - properties["reverseDirection"] = reverseDirection - properties["interactionSource"] = interactionSource - properties["thresholds"] = thresholds - properties["resistance"] = resistance - properties["velocityThreshold"] = velocityThreshold - } - ) { - require(anchors.isNotEmpty()) { "You must have at least one anchor." } - require(anchors.values.distinct().count() == anchors.size) { - "You cannot have two anchors mapped to the same state." - } - val density = LocalDensity.current - state.ensureInit(anchors) - LaunchedEffect(anchors, state) { - val oldAnchors = state.anchors - state.anchors = anchors - state.resistance = resistance - state.thresholds = { a, b -> - val from = anchors.getValue(a) - val to = anchors.getValue(b) - with(thresholds(from, to)) { density.computeThreshold(a, b) } - } - with(density) { state.velocityThreshold = velocityThreshold.toPx() } - state.processNewAnchors(oldAnchors, anchors) - } - - Modifier.draggable( - orientation = orientation, - enabled = enabled, - reverseDirection = reverseDirection, - interactionSource = interactionSource, - startDragImmediately = state.isAnimationRunning, - onDragStopped = { velocity -> launch { state.performFling(velocity) } }, - state = state.draggableState - ) - } - -/** - * Interface to compute a threshold between two anchors/states in a [swipeable]. - * - * To define a [ThresholdConfig], consider using [FixedThreshold] and [FractionalThreshold]. - */ -@Stable -interface ThresholdConfig { - /** Compute the value of the threshold (in pixels), once the values of the anchors are known. */ - fun Density.computeThreshold(fromValue: Float, toValue: Float): Float -} - -/** - * A fixed threshold will be at an [offset] away from the first anchor. - * - * @param offset The offset (in dp) that the threshold will be at. - */ -@Immutable -data class FixedThreshold(private val offset: Dp) : ThresholdConfig { - override fun Density.computeThreshold(fromValue: Float, toValue: Float): Float { - return fromValue + offset.toPx() * sign(toValue - fromValue) - } -} - -/** - * A fractional threshold will be at a [fraction] of the way between the two anchors. - * - * @param fraction The fraction (between 0 and 1) that the threshold will be at. - */ -@Immutable -data class FractionalThreshold( - /*@FloatRange(from = 0.0, to = 1.0)*/ - private val fraction: Float -) : ThresholdConfig { - override fun Density.computeThreshold(fromValue: Float, toValue: Float): Float { - return lerp(fromValue, toValue, fraction) - } -} - -/** - * Specifies how resistance is calculated in [swipeable]. - * - * There are two things needed to calculate resistance: the resistance basis determines how much - * overflow will be consumed to achieve maximum resistance, and the resistance factor determines the - * amount of resistance (the larger the resistance factor, the stronger the resistance). - * - * The resistance basis is usually either the size of the component which [swipeable] is applied to, - * or the distance between the minimum and maximum anchors. For a constructor in which the - * resistance basis defaults to the latter, consider using [resistanceConfig]. - * - * You may specify different resistance factors for each bound. Consider using one of the default - * resistance factors in [SwipeableDefaults]: `StandardResistanceFactor` to convey that the user has - * run out of things to see, and `StiffResistanceFactor` to convey that the user cannot swipe this - * right now. Also, you can set either factor to 0 to disable resistance at that bound. - * - * @param basis Specifies the maximum amount of overflow that will be consumed. Must be positive. - * @param factorAtMin The factor by which to scale the resistance at the minimum bound. Must not be - * negative. - * @param factorAtMax The factor by which to scale the resistance at the maximum bound. Must not be - * negative. - */ -@Immutable -class ResistanceConfig( - /*@FloatRange(from = 0.0, fromInclusive = false)*/ - val basis: Float, - /*@FloatRange(from = 0.0)*/ - val factorAtMin: Float = StandardResistanceFactor, - /*@FloatRange(from = 0.0)*/ - val factorAtMax: Float = StandardResistanceFactor -) { - fun computeResistance(overflow: Float): Float { - val factor = if (overflow < 0) factorAtMin else factorAtMax - if (factor == 0f) return 0f - val progress = (overflow / basis).coerceIn(-1f, 1f) - return basis / factor * sin(progress * PI.toFloat() / 2) - } - - override fun equals(other: Any?): Boolean { - if (this === other) return true - if (other !is ResistanceConfig) return false - - if (basis != other.basis) return false - if (factorAtMin != other.factorAtMin) return false - if (factorAtMax != other.factorAtMax) return false - - return true - } - - override fun hashCode(): Int { - var result = basis.hashCode() - result = 31 * result + factorAtMin.hashCode() - result = 31 * result + factorAtMax.hashCode() - return result - } - - override fun toString(): String { - return "ResistanceConfig(basis=$basis, factorAtMin=$factorAtMin, factorAtMax=$factorAtMax)" - } -} - -/** - * Given an offset x and a set of anchors, return a list of anchors: - * 1. [ ] if the set of anchors is empty, - * 2. [ x' ] if x is equal to one of the anchors, accounting for a small rounding error, where x' is - * x rounded to the exact value of the matching anchor, - * 3. [ min ] if min is the minimum anchor and x < min, - * 4. [ max ] if max is the maximum anchor and x > max, or - * 5. [ a , b ] if a and b are anchors such that a < x < b and b - a is minimal. - */ -private fun findBounds(offset: Float, anchors: Set): List { - // Find the anchors the target lies between with a little bit of rounding error. - val a = anchors.filter { it <= offset + 0.001 }.maxOrNull() - val b = anchors.filter { it >= offset - 0.001 }.minOrNull() - - return when { - a == null -> - // case 1 or 3 - listOfNotNull(b) - b == null -> - // case 4 - listOf(a) - a == b -> - // case 2 - // Can't return offset itself here since it might not be exactly equal - // to the anchor, despite being considered an exact match. - listOf(a) - else -> - // case 5 - listOf(a, b) - } -} - -private fun computeTarget( - offset: Float, - lastValue: Float, - anchors: Set, - thresholds: (Float, Float) -> Float, - velocity: Float, - velocityThreshold: Float -): Float { - val bounds = findBounds(offset, anchors) - return when (bounds.size) { - 0 -> lastValue - 1 -> bounds[0] - else -> { - val lower = bounds[0] - val upper = bounds[1] - if (lastValue <= offset) { - // Swiping from lower to upper (positive). - if (velocity >= velocityThreshold) { - return upper - } else { - val threshold = thresholds(lower, upper) - if (offset < threshold) lower else upper - } - } else { - // Swiping from upper to lower (negative). - if (velocity <= -velocityThreshold) { - return lower - } else { - val threshold = thresholds(upper, lower) - if (offset > threshold) upper else lower - } - } - } - } -} - -private fun Map.getOffset(state: T): Float? { - return entries.firstOrNull { it.value == state }?.key -} - -/** Contains useful defaults for [swipeable] and [SwipeableState]. */ -object SwipeableDefaults { - /** The default animation used by [SwipeableState]. */ - val AnimationSpec = SpringSpec() - - /** The default velocity threshold (1.8 dp per millisecond) used by [swipeable]. */ - val VelocityThreshold = 125.dp - - /** A stiff resistance factor which indicates that swiping isn't available right now. */ - const val StiffResistanceFactor = 20f - - /** A standard resistance factor which indicates that the user has run out of things to see. */ - const val StandardResistanceFactor = 10f - - /** - * The default resistance config used by [swipeable]. - * - * This returns `null` if there is one anchor. If there are at least two anchors, it returns a - * [ResistanceConfig] with the resistance basis equal to the distance between the two bounds. - */ - fun resistanceConfig( - anchors: Set, - factorAtMin: Float = StandardResistanceFactor, - factorAtMax: Float = StandardResistanceFactor - ): ResistanceConfig? { - return if (anchors.size <= 1) { - null - } else { - val basis = anchors.maxOrNull()!! - anchors.minOrNull()!! - ResistanceConfig(basis, factorAtMin, factorAtMax) - } - } -} - -// temp default nested scroll connection for swipeables which desire as an opt in -// revisit in b/174756744 as all types will have their own specific connection probably -internal val SwipeableState.PreUpPostDownNestedScrollConnection: NestedScrollConnection - get() = - object : NestedScrollConnection { - override fun onPreScroll(available: Offset, source: NestedScrollSource): Offset { - val delta = available.toFloat() - return if (delta < 0 && source == NestedScrollSource.Drag) { - performDrag(delta).toOffset() - } else { - Offset.Zero - } - } - - override fun onPostScroll( - consumed: Offset, - available: Offset, - source: NestedScrollSource - ): Offset { - return if (source == NestedScrollSource.Drag) { - performDrag(available.toFloat()).toOffset() - } else { - Offset.Zero - } - } - - override suspend fun onPreFling(available: Velocity): Velocity { - val toFling = Offset(available.x, available.y).toFloat() - return if (toFling < 0 && offset.value > minBound) { - performFling(velocity = toFling) - // since we go to the anchor with tween settling, consume all for the best UX - available - } else { - Velocity.Zero - } - } - - override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { - performFling(velocity = Offset(available.x, available.y).toFloat()) - return available - } - - private fun Float.toOffset(): Offset = Offset(0f, this) - - private fun Offset.toFloat(): Float = this.y - } diff --git a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt index 82fe3f265384d..609ea90e9159c 100644 --- a/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt +++ b/packages/SystemUI/compose/facade/disabled/src/com/android/systemui/compose/ComposeFacade.kt @@ -21,13 +21,11 @@ import android.content.Context import android.view.View import androidx.activity.ComponentActivity import androidx.lifecycle.LifecycleOwner -import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel -import com.android.systemui.util.time.SystemClock /** The Compose facade, when Compose is *not* available. */ object ComposeFacade : BaseComposeFacade { @@ -53,14 +51,6 @@ object ComposeFacade : BaseComposeFacade { throwComposeUnavailableError() } - override fun createMultiShadeView( - context: Context, - viewModel: MultiShadeViewModel, - clock: SystemClock, - ): View { - throwComposeUnavailableError() - } - override fun createSceneContainerView( context: Context, viewModel: SceneContainerViewModel, diff --git a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt index 7926f92243471..0ee88b90bcc41 100644 --- a/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt +++ b/packages/SystemUI/compose/facade/enabled/src/com/android/systemui/compose/ComposeFacade.kt @@ -23,8 +23,6 @@ import androidx.activity.compose.setContent import androidx.compose.ui.platform.ComposeView import androidx.lifecycle.LifecycleOwner import com.android.compose.theme.PlatformTheme -import com.android.systemui.multishade.ui.composable.MultiShade -import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel import com.android.systemui.people.ui.compose.PeopleScreen import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.compose.FooterActions @@ -34,7 +32,6 @@ import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.ui.composable.ComposableScene import com.android.systemui.scene.ui.composable.SceneContainer import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel -import com.android.systemui.util.time.SystemClock /** The Compose facade, when Compose is available. */ object ComposeFacade : BaseComposeFacade { @@ -60,23 +57,6 @@ object ComposeFacade : BaseComposeFacade { } } - override fun createMultiShadeView( - context: Context, - viewModel: MultiShadeViewModel, - clock: SystemClock, - ): View { - return ComposeView(context).apply { - setContent { - PlatformTheme { - MultiShade( - viewModel = viewModel, - clock = clock, - ) - } - } - } - } - override fun createSceneContainerView( context: Context, viewModel: SceneContainerViewModel, diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt deleted file mode 100644 index 99fe26ce1f3bb..0000000000000 --- a/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/MultiShade.kt +++ /dev/null @@ -1,145 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.ui.composable - -import androidx.compose.foundation.background -import androidx.compose.foundation.gestures.detectTapGestures -import androidx.compose.foundation.gestures.detectVerticalDragGestures -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.BoxWithConstraints -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.runtime.Composable -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.remember -import androidx.compose.runtime.setValue -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.input.pointer.pointerInput -import androidx.compose.ui.layout.onSizeChanged -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.res.colorResource -import androidx.compose.ui.unit.IntSize -import com.android.systemui.R -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel -import com.android.systemui.notifications.ui.composable.Notifications -import com.android.systemui.qs.footer.ui.compose.QuickSettings -import com.android.systemui.statusbar.ui.composable.StatusBar -import com.android.systemui.util.time.SystemClock - -@Composable -fun MultiShade( - viewModel: MultiShadeViewModel, - clock: SystemClock, - modifier: Modifier = Modifier, -) { - val isScrimEnabled: Boolean by viewModel.isScrimEnabled.collectAsState() - val scrimAlpha: Float by viewModel.scrimAlpha.collectAsState() - - // TODO(b/273298030): find a different way to get the height constraint from its parent. - BoxWithConstraints(modifier = modifier) { - val maxHeightPx = with(LocalDensity.current) { maxHeight.toPx() } - - Scrim( - modifier = Modifier.fillMaxSize(), - remoteTouch = viewModel::onScrimTouched, - alpha = { scrimAlpha }, - isScrimEnabled = isScrimEnabled, - ) - Shade( - viewModel = viewModel.leftShade, - currentTimeMillis = clock::elapsedRealtime, - containerHeightPx = maxHeightPx, - modifier = Modifier.align(Alignment.TopStart), - ) { - Column { - StatusBar() - Notifications() - } - } - Shade( - viewModel = viewModel.rightShade, - currentTimeMillis = clock::elapsedRealtime, - containerHeightPx = maxHeightPx, - modifier = Modifier.align(Alignment.TopEnd), - ) { - Column { - StatusBar() - QuickSettings() - } - } - Shade( - viewModel = viewModel.singleShade, - currentTimeMillis = clock::elapsedRealtime, - containerHeightPx = maxHeightPx, - modifier = Modifier, - ) { - Column { - StatusBar() - Notifications() - QuickSettings() - } - } - } -} - -@Composable -private fun Scrim( - remoteTouch: (ProxiedInputModel) -> Unit, - alpha: () -> Float, - isScrimEnabled: Boolean, - modifier: Modifier = Modifier, -) { - var size by remember { mutableStateOf(IntSize.Zero) } - - Box( - modifier = - modifier - .graphicsLayer { this.alpha = alpha() } - .background(colorResource(R.color.opaque_scrim)) - .fillMaxSize() - .onSizeChanged { size = it } - .then( - if (isScrimEnabled) { - Modifier.pointerInput(Unit) { - detectTapGestures(onTap = { remoteTouch(ProxiedInputModel.OnTap) }) - } - .pointerInput(Unit) { - detectVerticalDragGestures( - onVerticalDrag = { change, dragAmount -> - remoteTouch( - ProxiedInputModel.OnDrag( - xFraction = change.position.x / size.width, - yDragAmountPx = dragAmount, - ) - ) - }, - onDragEnd = { remoteTouch(ProxiedInputModel.OnDragEnd) }, - onDragCancel = { remoteTouch(ProxiedInputModel.OnDragCancel) } - ) - } - } else { - Modifier - } - ) - ) -} diff --git a/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/Shade.kt b/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/Shade.kt deleted file mode 100644 index cfcc2fb251fdb..0000000000000 --- a/packages/SystemUI/compose/features/src/com/android/systemui/multishade/ui/composable/Shade.kt +++ /dev/null @@ -1,336 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.ui.composable - -import androidx.compose.foundation.gestures.Orientation -import androidx.compose.foundation.interaction.DragInteraction -import androidx.compose.foundation.interaction.InteractionSource -import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Surface -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.collectAsState -import androidx.compose.runtime.getValue -import androidx.compose.runtime.remember -import androidx.compose.runtime.snapshotFlow -import androidx.compose.ui.Modifier -import androidx.compose.ui.geometry.Offset -import androidx.compose.ui.graphics.graphicsLayer -import androidx.compose.ui.input.pointer.util.VelocityTracker -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp -import com.android.compose.modifiers.height -import com.android.compose.modifiers.padding -import com.android.compose.swipeable.FixedThreshold -import com.android.compose.swipeable.SwipeableState -import com.android.compose.swipeable.ThresholdConfig -import com.android.compose.swipeable.rememberSwipeableState -import com.android.compose.swipeable.swipeable -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.multishade.ui.viewmodel.ShadeViewModel -import kotlin.math.min -import kotlin.math.roundToInt -import kotlinx.coroutines.launch - -/** - * Renders a shade (container and content). - * - * This should be allowed to grow to fill the width and height of its container. - * - * @param viewModel The view-model for this shade. - * @param currentTimeMillis A provider for the current time, in milliseconds. - * @param containerHeightPx The height of the container that this shade is being shown in, in - * pixels. - * @param modifier The Modifier. - * @param content The content of the shade. - */ -@Composable -fun Shade( - viewModel: ShadeViewModel, - currentTimeMillis: () -> Long, - containerHeightPx: Float, - modifier: Modifier = Modifier, - content: @Composable () -> Unit = {}, -) { - val isVisible: Boolean by viewModel.isVisible.collectAsState() - if (!isVisible) { - return - } - - val interactionSource: MutableInteractionSource = remember { MutableInteractionSource() } - ReportNonProxiedInput(viewModel, interactionSource) - - val swipeableState = rememberSwipeableState(initialValue = ShadeState.FullyCollapsed) - HandleForcedCollapse(viewModel, swipeableState) - HandleProxiedInput(viewModel, swipeableState, currentTimeMillis) - ReportShadeExpansion(viewModel, swipeableState, containerHeightPx) - - val isSwipingEnabled: Boolean by viewModel.isSwipingEnabled.collectAsState() - val collapseThreshold: Float by viewModel.swipeCollapseThreshold.collectAsState() - val expandThreshold: Float by viewModel.swipeExpandThreshold.collectAsState() - - val width: ShadeViewModel.Size by viewModel.width.collectAsState() - val density = LocalDensity.current - - val anchors: Map = - remember(containerHeightPx) { swipeableAnchors(containerHeightPx) } - - ShadeContent( - shadeHeightPx = { swipeableState.offset.value }, - overstretch = { swipeableState.overflow.value / containerHeightPx }, - isSwipingEnabled = isSwipingEnabled, - swipeableState = swipeableState, - interactionSource = interactionSource, - anchors = anchors, - thresholds = { _, to -> - swipeableThresholds( - to = to, - swipeCollapseThreshold = collapseThreshold.fractionToDp(density, containerHeightPx), - swipeExpandThreshold = expandThreshold.fractionToDp(density, containerHeightPx), - ) - }, - modifier = modifier.shadeWidth(width, density), - content = content, - ) -} - -/** - * Draws the content of the shade. - * - * @param shadeHeightPx Provider for the current expansion of the shade, in pixels, where `0` is - * fully collapsed. - * @param overstretch Provider for the current amount of vertical "overstretch" that the shade - * should be rendered with. This is `0` or a positive number that is a percentage of the total - * height of the shade when fully expanded. A value of `0` means that the shade is not stretched - * at all. - * @param isSwipingEnabled Whether swiping inside the shade is enabled or not. - * @param swipeableState The state to use for the [swipeable] modifier, allowing external control in - * addition to direct control (proxied user input in addition to non-proxied/direct user input). - * @param anchors A map of [ShadeState] keyed by the vertical position, in pixels, where that state - * occurs; this is used to configure the [swipeable] modifier. - * @param thresholds Function that returns the [ThresholdConfig] for going from one [ShadeState] to - * another. This controls how the [swipeable] decides which [ShadeState] to animate to once the - * user lets go of the shade; e.g. does it animate to fully collapsed or fully expanded. - * @param content The content to render inside the shade. - * @param modifier The [Modifier]. - */ -@Composable -private fun ShadeContent( - shadeHeightPx: () -> Float, - overstretch: () -> Float, - isSwipingEnabled: Boolean, - swipeableState: SwipeableState, - interactionSource: MutableInteractionSource, - anchors: Map, - thresholds: (from: ShadeState, to: ShadeState) -> ThresholdConfig, - modifier: Modifier = Modifier, - content: @Composable () -> Unit = {}, -) { - /** - * Returns a function that takes in [Density] and returns the current padding around the shade - * content. - */ - fun padding( - shadeHeightPx: () -> Float, - ): Density.() -> Int { - return { - min( - 12.dp.toPx().roundToInt(), - shadeHeightPx().roundToInt(), - ) - } - } - - Surface( - shape = RoundedCornerShape(32.dp), - modifier = - modifier - .fillMaxWidth() - .height { shadeHeightPx().roundToInt() } - .padding( - horizontal = padding(shadeHeightPx), - vertical = padding(shadeHeightPx), - ) - .graphicsLayer { - // Applies the vertical over-stretching of the shade content that may happen if - // the user keep dragging down when the shade is already fully-expanded. - transformOrigin = transformOrigin.copy(pivotFractionY = 0f) - this.scaleY = 1 + overstretch().coerceAtLeast(0f) - } - .swipeable( - enabled = isSwipingEnabled, - state = swipeableState, - interactionSource = interactionSource, - anchors = anchors, - thresholds = thresholds, - orientation = Orientation.Vertical, - ), - content = content, - ) -} - -/** Funnels current shade expansion values into the view-model. */ -@Composable -private fun ReportShadeExpansion( - viewModel: ShadeViewModel, - swipeableState: SwipeableState, - containerHeightPx: Float, -) { - LaunchedEffect(swipeableState.offset, containerHeightPx) { - snapshotFlow { swipeableState.offset.value / containerHeightPx } - .collect { expansion -> viewModel.onExpansionChanged(expansion) } - } -} - -/** Funnels drag gesture start and end events into the view-model. */ -@Composable -private fun ReportNonProxiedInput( - viewModel: ShadeViewModel, - interactionSource: InteractionSource, -) { - LaunchedEffect(interactionSource) { - interactionSource.interactions.collect { - when (it) { - is DragInteraction.Start -> { - viewModel.onDragStarted() - } - is DragInteraction.Stop -> { - viewModel.onDragEnded() - } - } - } - } -} - -/** When told to force collapse, collapses the shade. */ -@Composable -private fun HandleForcedCollapse( - viewModel: ShadeViewModel, - swipeableState: SwipeableState, -) { - LaunchedEffect(viewModel) { - viewModel.isForceCollapsed.collect { - launch { swipeableState.animateTo(ShadeState.FullyCollapsed) } - } - } -} - -/** - * Handles proxied input (input originating outside of the UI of the shade) by driving the - * [SwipeableState] accordingly. - */ -@Composable -private fun HandleProxiedInput( - viewModel: ShadeViewModel, - swipeableState: SwipeableState, - currentTimeMillis: () -> Long, -) { - val velocityTracker: VelocityTracker = remember { VelocityTracker() } - LaunchedEffect(viewModel) { - viewModel.proxiedInput.collect { - when (it) { - is ProxiedInputModel.OnDrag -> { - velocityTracker.addPosition( - timeMillis = currentTimeMillis.invoke(), - position = Offset(0f, it.yDragAmountPx), - ) - swipeableState.performDrag(it.yDragAmountPx) - } - is ProxiedInputModel.OnDragEnd -> { - launch { - val velocity = velocityTracker.calculateVelocity().y - velocityTracker.resetTracking() - // We use a VelocityTracker to keep a record of how fast the pointer was - // moving such that we know how far to fling the shade when the gesture - // ends. Flinging the SwipeableState using performFling is required after - // one or more calls to performDrag such that the swipeable settles into one - // of the states. Without doing that, the shade would remain unmoving in an - // in-between state on the screen. - swipeableState.performFling(velocity) - } - } - is ProxiedInputModel.OnDragCancel -> { - launch { - velocityTracker.resetTracking() - swipeableState.animateTo(swipeableState.progress.from) - } - } - else -> Unit - } - } - } -} - -/** - * Converts the [Float] (which is assumed to be a fraction between `0` and `1`) to a value in dp. - * - * @param density The [Density] of the display. - * @param wholePx The whole amount that the given [Float] is a fraction of. - * @return The dp size that's a fraction of the whole amount. - */ -private fun Float.fractionToDp(density: Density, wholePx: Float): Dp { - return with(density) { (this@fractionToDp * wholePx).toDp() } -} - -private fun Modifier.shadeWidth( - size: ShadeViewModel.Size, - density: Density, -): Modifier { - return then( - when (size) { - is ShadeViewModel.Size.Fraction -> Modifier.fillMaxWidth(size.fraction) - is ShadeViewModel.Size.Pixels -> Modifier.width(with(density) { size.pixels.toDp() }) - } - ) -} - -/** Returns the pixel positions for each of the supported shade states. */ -private fun swipeableAnchors(containerHeightPx: Float): Map { - return mapOf( - 0f to ShadeState.FullyCollapsed, - containerHeightPx to ShadeState.FullyExpanded, - ) -} - -/** - * Returns the [ThresholdConfig] for how far the shade should be expanded or collapsed such that it - * actually completes the expansion or collapse after the user lifts their pointer. - */ -private fun swipeableThresholds( - to: ShadeState, - swipeExpandThreshold: Dp, - swipeCollapseThreshold: Dp, -): ThresholdConfig { - return FixedThreshold( - when (to) { - ShadeState.FullyExpanded -> swipeExpandThreshold - ShadeState.FullyCollapsed -> swipeCollapseThreshold - } - ) -} - -/** Enumerates the shade UI states for [SwipeableState]. */ -private enum class ShadeState { - FullyCollapsed, - FullyExpanded, -} diff --git a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt index b15c60e62eade..85f31e5e6b5a2 100644 --- a/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt +++ b/packages/SystemUI/src/com/android/systemui/compose/BaseComposeFacade.kt @@ -21,13 +21,11 @@ import android.content.Context import android.view.View import androidx.activity.ComponentActivity import androidx.lifecycle.LifecycleOwner -import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel import com.android.systemui.people.ui.viewmodel.PeopleViewModel import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel import com.android.systemui.scene.shared.model.Scene import com.android.systemui.scene.shared.model.SceneKey import com.android.systemui.scene.ui.viewmodel.SceneContainerViewModel -import com.android.systemui.util.time.SystemClock /** * A facade to interact with Compose, when it is available. @@ -63,13 +61,6 @@ interface BaseComposeFacade { qsVisibilityLifecycleOwner: LifecycleOwner, ): View - /** Create a [View] to represent [viewModel] on screen. */ - fun createMultiShadeView( - context: Context, - viewModel: MultiShadeViewModel, - clock: SystemClock, - ): View - /** Create a [View] to represent [viewModel] on screen. */ fun createSceneContainerView( context: Context, diff --git a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt index efa5981ef7b2d..e6820a317035f 100644 --- a/packages/SystemUI/src/com/android/systemui/flags/Flags.kt +++ b/packages/SystemUI/src/com/android/systemui/flags/Flags.kt @@ -77,7 +77,7 @@ object Flags { // TODO(b/278873737): Tracking Bug @JvmField val LOAD_NOTIFICATIONS_BEFORE_THE_USER_SWITCH_IS_COMPLETE = - releasedFlag(278873737, "load_notifications_before_the_user_switch_is_complete") + releasedFlag(278873737, "load_notifications_before_the_user_switch_is_complete") // TODO(b/277338665): Tracking Bug @JvmField @@ -92,11 +92,7 @@ object Flags { // TODO(b/288326013): Tracking Bug @JvmField val NOTIFICATION_ASYNC_HYBRID_VIEW_INFLATION = - unreleasedFlag( - 288326013, - "notification_async_hybrid_view_inflation", - teamfood = false - ) + unreleasedFlag(288326013, "notification_async_hybrid_view_inflation", teamfood = false) @JvmField val ANIMATED_NOTIFICATION_SHADE_INSETS = @@ -104,18 +100,17 @@ object Flags { // TODO(b/268005230): Tracking Bug @JvmField - val SENSITIVE_REVEAL_ANIM = - unreleasedFlag(268005230, "sensitive_reveal_anim", teamfood = true) + val SENSITIVE_REVEAL_ANIM = unreleasedFlag(268005230, "sensitive_reveal_anim", teamfood = true) // TODO(b/280783617): Tracking Bug @Keep @JvmField val BUILDER_EXTRAS_OVERRIDE = - sysPropBooleanFlag( - 128, - "persist.sysui.notification.builder_extras_override", - default = false - ) + sysPropBooleanFlag( + 128, + "persist.sysui.notification.builder_extras_override", + default = false + ) // 200 - keyguard/lockscreen // ** Flag retired ** @@ -133,16 +128,17 @@ object Flags { // TODO(b/254512676): Tracking Bug @JvmField - val LOCKSCREEN_CUSTOM_CLOCKS = resourceBooleanFlag( - 207, - R.bool.config_enableLockScreenCustomClocks, - "lockscreen_custom_clocks" - ) + val LOCKSCREEN_CUSTOM_CLOCKS = + resourceBooleanFlag( + 207, + R.bool.config_enableLockScreenCustomClocks, + "lockscreen_custom_clocks" + ) // TODO(b/275694445): Tracking Bug @JvmField - val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = releasedFlag(208, - "lockscreen_without_secure_lock_when_dreaming") + val LOCKSCREEN_WITHOUT_SECURE_LOCK_WHEN_DREAMING = + releasedFlag(208, "lockscreen_without_secure_lock_when_dreaming") // TODO(b/286092087): Tracking Bug @JvmField @@ -156,8 +152,7 @@ object Flags { * Whether the clock on a wide lock screen should use the new "stepping" animation for moving * the digits when the clock moves. */ - @JvmField - val STEP_CLOCK_ANIMATION = releasedFlag(212, "step_clock_animation") + @JvmField val STEP_CLOCK_ANIMATION = releasedFlag(212, "step_clock_animation") /** * Migration from the legacy isDozing/dozeAmount paths to the new KeyguardTransitionRepository @@ -183,13 +178,11 @@ object Flags { @JvmField val BIOMETRICS_ANIMATION_REVAMP = unreleasedFlag(221, "biometrics_animation_revamp") // TODO(b/262780002): Tracking Bug - @JvmField - val REVAMPED_WALLPAPER_UI = releasedFlag(222, "revamped_wallpaper_ui") + @JvmField val REVAMPED_WALLPAPER_UI = releasedFlag(222, "revamped_wallpaper_ui") // flag for controlling auto pin confirmation and material u shapes in bouncer @JvmField - val AUTO_PIN_CONFIRMATION = - releasedFlag(224, "auto_pin_confirmation", "auto_pin_confirmation") + val AUTO_PIN_CONFIRMATION = releasedFlag(224, "auto_pin_confirmation", "auto_pin_confirmation") // TODO(b/262859270): Tracking Bug @JvmField val FALSING_OFF_FOR_UNFOLDED = releasedFlag(225, "falsing_off_for_unfolded") @@ -206,20 +199,11 @@ object Flags { /** Whether the long-press gesture to open wallpaper picker is enabled. */ // TODO(b/266242192): Tracking Bug @JvmField - val LOCK_SCREEN_LONG_PRESS_ENABLED = - releasedFlag( - 228, - "lock_screen_long_press_enabled" - ) + val LOCK_SCREEN_LONG_PRESS_ENABLED = releasedFlag(228, "lock_screen_long_press_enabled") /** Enables UI updates for AI wallpapers in the wallpaper picker. */ // TODO(b/267722622): Tracking Bug - @JvmField - val WALLPAPER_PICKER_UI_FOR_AIWP = - releasedFlag( - 229, - "wallpaper_picker_ui_for_aiwp" - ) + @JvmField val WALLPAPER_PICKER_UI_FOR_AIWP = releasedFlag(229, "wallpaper_picker_ui_for_aiwp") /** Whether to use a new data source for intents to run on keyguard dismissal. */ // TODO(b/275069969): Tracking bug. @@ -239,27 +223,21 @@ object Flags { /** Provide new auth messages on the bouncer. */ // TODO(b/277961132): Tracking bug. - @JvmField - val REVAMPED_BOUNCER_MESSAGES = - unreleasedFlag(234, "revamped_bouncer_messages") + @JvmField val REVAMPED_BOUNCER_MESSAGES = unreleasedFlag(234, "revamped_bouncer_messages") /** Whether to delay showing bouncer UI when face auth or active unlock are enrolled. */ // TODO(b/279794160): Tracking bug. - @JvmField - val DELAY_BOUNCER = unreleasedFlag(235, "delay_bouncer", teamfood = true) - + @JvmField val DELAY_BOUNCER = unreleasedFlag(235, "delay_bouncer", teamfood = true) /** Keyguard Migration */ /** Migrate the indication area to the new keyguard root view. */ // TODO(b/280067944): Tracking bug. - @JvmField - val MIGRATE_INDICATION_AREA = releasedFlag(236, "migrate_indication_area") + @JvmField val MIGRATE_INDICATION_AREA = releasedFlag(236, "migrate_indication_area") /** - * Migrate the bottom area to the new keyguard root view. - * Because there is no such thing as a "bottom area" after this, this also breaks it up into - * many smaller, modular pieces. + * Migrate the bottom area to the new keyguard root view. Because there is no such thing as a + * "bottom area" after this, this also breaks it up into many smaller, modular pieces. */ // TODO(b/290652751): Tracking bug. @JvmField @@ -268,36 +246,29 @@ object Flags { /** Whether to listen for fingerprint authentication over keyguard occluding activities. */ // TODO(b/283260512): Tracking bug. - @JvmField - val FP_LISTEN_OCCLUDING_APPS = unreleasedFlag(237, "fp_listen_occluding_apps") + @JvmField val FP_LISTEN_OCCLUDING_APPS = unreleasedFlag(237, "fp_listen_occluding_apps") /** Flag meant to guard the talkback fix for the KeyguardIndicationTextView */ // TODO(b/286563884): Tracking bug - @JvmField - val KEYGUARD_TALKBACK_FIX = releasedFlag(238, "keyguard_talkback_fix") + @JvmField val KEYGUARD_TALKBACK_FIX = releasedFlag(238, "keyguard_talkback_fix") // TODO(b/287268101): Tracking bug. - @JvmField - val TRANSIT_CLOCK = unreleasedFlag(239, "lockscreen_custom_transit_clock") + @JvmField val TRANSIT_CLOCK = unreleasedFlag(239, "lockscreen_custom_transit_clock") /** Migrate the lock icon view to the new keyguard root view. */ // TODO(b/286552209): Tracking bug. - @JvmField - val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon", teamfood = true) + @JvmField val MIGRATE_LOCK_ICON = unreleasedFlag(240, "migrate_lock_icon", teamfood = true) // TODO(b/288276738): Tracking bug. - @JvmField - val WIDGET_ON_KEYGUARD = unreleasedFlag(241, "widget_on_keyguard") + @JvmField val WIDGET_ON_KEYGUARD = unreleasedFlag(241, "widget_on_keyguard") /** Migrate the NSSL to the a sibling to both the panel and keyguard root view. */ // TODO(b/288074305): Tracking bug. - @JvmField - val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl") + @JvmField val MIGRATE_NSSL = unreleasedFlag(242, "migrate_nssl") /** Migrate the status view from the notification panel to keyguard root view. */ // TODO(b/291767565): Tracking bug. - @JvmField - val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag(243, "migrate_keyguard_status_view") + @JvmField val MIGRATE_KEYGUARD_STATUS_VIEW = unreleasedFlag(243, "migrate_keyguard_status_view") // 300 - power menu // TODO(b/254512600): Tracking Bug @@ -316,8 +287,7 @@ object Flags { // TODO(b/270223352): Tracking Bug @JvmField - val HIDE_SMARTSPACE_ON_DREAM_OVERLAY = - releasedFlag(404, "hide_smartspace_on_dream_overlay") + val HIDE_SMARTSPACE_ON_DREAM_OVERLAY = releasedFlag(404, "hide_smartspace_on_dream_overlay") // TODO(b/271460958): Tracking Bug @JvmField @@ -357,8 +327,7 @@ object Flags { /** Enables Font Scaling Quick Settings tile */ // TODO(b/269341316): Tracking Bug - @JvmField - val ENABLE_FONT_SCALING_TILE = releasedFlag(509, "enable_font_scaling_tile") + @JvmField val ENABLE_FONT_SCALING_TILE = releasedFlag(509, "enable_font_scaling_tile") /** Enables new QS Edit Mode visual refresh */ // TODO(b/269787742): Tracking Bug @@ -367,13 +336,11 @@ object Flags { // 600- status bar - // TODO(b/265892345): Tracking Bug val PLUG_IN_STATUS_BAR_CHIP = releasedFlag(265892345, "plug_in_status_bar_chip") // TODO(b/280426085): Tracking Bug - @JvmField - val NEW_BLUETOOTH_REPOSITORY = releasedFlag(612, "new_bluetooth_repository") + @JvmField val NEW_BLUETOOTH_REPOSITORY = releasedFlag(612, "new_bluetooth_repository") // 700 - dialer/calls // TODO(b/254512734): Tracking Bug @@ -441,8 +408,8 @@ object Flags { // TODO(b/273509374): Tracking Bug @JvmField - val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS = releasedFlag(1006, - "always_show_home_controls_on_dreams") + val ALWAYS_SHOW_HOME_CONTROLS_ON_DREAMS = + releasedFlag(1006, "always_show_home_controls_on_dreams") // 1100 - windowing @Keep @@ -490,11 +457,7 @@ object Flags { @Keep @JvmField val ENABLE_PIP_KEEP_CLEAR_ALGORITHM = - sysPropBooleanFlag( - 1110, - "persist.wm.debug.enable_pip_keep_clear_algorithm", - default = true - ) + sysPropBooleanFlag(1110, "persist.wm.debug.enable_pip_keep_clear_algorithm", default = true) // TODO(b/256873975): Tracking Bug @JvmField @@ -532,7 +495,8 @@ object Flags { // TODO(b/273443374): Tracking Bug @Keep - @JvmField val LOCKSCREEN_LIVE_WALLPAPER = + @JvmField + val LOCKSCREEN_LIVE_WALLPAPER = sysPropBooleanFlag(1117, "persist.wm.debug.lockscreen_live_wallpaper", default = true) // TODO(b/281648899): Tracking bug @@ -547,7 +511,6 @@ object Flags { val ENABLE_PIP2_IMPLEMENTATION = sysPropBooleanFlag(1119, "persist.wm.debug.enable_pip2_implementation", default = false) - // 1200 - predictive back @Keep @JvmField @@ -573,8 +536,7 @@ object Flags { unreleasedFlag(1204, "persist.wm.debug.predictive_back_sysui_enable", teamfood = true) // TODO(b/270987164): Tracking Bug - @JvmField - val TRACKPAD_GESTURE_FEATURES = releasedFlag(1205, "trackpad_gesture_features") + @JvmField val TRACKPAD_GESTURE_FEATURES = releasedFlag(1205, "trackpad_gesture_features") // TODO(b/263826204): Tracking Bug @JvmField @@ -597,8 +559,7 @@ object Flags { unreleasedFlag(1209, "persist.wm.debug.predictive_back_qs_dialog_anim", teamfood = true) // TODO(b/273800936): Tracking Bug - @JvmField - val TRACKPAD_GESTURE_COMMON = releasedFlag(1210, "trackpad_gesture_common") + @JvmField val TRACKPAD_GESTURE_COMMON = releasedFlag(1210, "trackpad_gesture_common") // 1300 - screenshots // TODO(b/264916608): Tracking Bug @@ -623,16 +584,12 @@ object Flags { // 1700 - clipboard @JvmField val CLIPBOARD_REMOTE_BEHAVIOR = releasedFlag(1701, "clipboard_remote_behavior") // TODO(b/278714186) Tracking Bug - @JvmField val CLIPBOARD_IMAGE_TIMEOUT = - unreleasedFlag(1702, "clipboard_image_timeout", teamfood = true) + @JvmField + val CLIPBOARD_IMAGE_TIMEOUT = unreleasedFlag(1702, "clipboard_image_timeout", teamfood = true) // TODO(b/279405451): Tracking Bug @JvmField val CLIPBOARD_SHARED_TRANSITIONS = unreleasedFlag(1703, "clipboard_shared_transitions") - // 1800 - shade container - // TODO(b/265944639): Tracking Bug - @JvmField val DUAL_SHADE = unreleasedFlag(1801, "dual_shade") - // TODO(b/283300105): Tracking Bug @JvmField val SCENE_CONTAINER = unreleasedFlag(1802, "scene_container") @@ -642,19 +599,15 @@ object Flags { // 2000 - device controls @Keep @JvmField val USE_APP_PANELS = releasedFlag(2000, "use_app_panels") - @JvmField - val APP_PANELS_ALL_APPS_ALLOWED = - releasedFlag(2001, "app_panels_all_apps_allowed") + @JvmField val APP_PANELS_ALL_APPS_ALLOWED = releasedFlag(2001, "app_panels_all_apps_allowed") @JvmField - val CONTROLS_MANAGEMENT_NEW_FLOWS = - releasedFlag(2002, "controls_management_new_flows") + val CONTROLS_MANAGEMENT_NEW_FLOWS = releasedFlag(2002, "controls_management_new_flows") // Enables removing app from Home control panel as a part of a new flow // TODO(b/269132640): Tracking Bug @JvmField - val APP_PANELS_REMOVE_APPS_ALLOWED = - releasedFlag(2003, "app_panels_remove_apps_allowed") + val APP_PANELS_REMOVE_APPS_ALLOWED = releasedFlag(2003, "app_panels_remove_apps_allowed") // 2200 - biometrics (udfps, sfps, BiometricPrompt, etc.) // TODO(b/259264861): Tracking Bug @@ -665,11 +618,9 @@ object Flags { // 2300 - stylus @JvmField val TRACK_STYLUS_EVER_USED = releasedFlag(2300, "track_stylus_ever_used") + @JvmField val ENABLE_STYLUS_CHARGING_UI = releasedFlag(2301, "enable_stylus_charging_ui") @JvmField - val ENABLE_STYLUS_CHARGING_UI = releasedFlag(2301, "enable_stylus_charging_ui") - @JvmField - val ENABLE_USI_BATTERY_NOTIFICATIONS = - releasedFlag(2302, "enable_usi_battery_notifications") + val ENABLE_USI_BATTERY_NOTIFICATIONS = releasedFlag(2302, "enable_usi_battery_notifications") @JvmField val ENABLE_STYLUS_EDUCATION = releasedFlag(2303, "enable_stylus_education") // 2400 - performance tools and debugging info @@ -681,11 +632,10 @@ object Flags { // TODO(b/283071711): Tracking bug @JvmField val TRIM_RESOURCES_WITH_BACKGROUND_TRIM_AT_LOCK = - unreleasedFlag(2401, "trim_resources_with_background_trim_on_lock") + unreleasedFlag(2401, "trim_resources_with_background_trim_on_lock") // TODO:(b/283203305): Tracking bug - @JvmField - val TRIM_FONT_CACHES_AT_UNLOCK = unreleasedFlag(2402, "trim_font_caches_on_unlock") + @JvmField val TRIM_FONT_CACHES_AT_UNLOCK = unreleasedFlag(2402, "trim_font_caches_on_unlock") // 2700 - unfold transitions // TODO(b/265764985): Tracking Bug @@ -708,27 +658,21 @@ object Flags { @JvmField val SHORTCUT_LIST_SEARCH_LAYOUT = releasedFlag(2600, "shortcut_list_search_layout") // TODO(b/259428678): Tracking Bug - @JvmField - val KEYBOARD_BACKLIGHT_INDICATOR = releasedFlag(2601, "keyboard_backlight_indicator") + @JvmField val KEYBOARD_BACKLIGHT_INDICATOR = releasedFlag(2601, "keyboard_backlight_indicator") // TODO(b/277192623): Tracking Bug - @JvmField - val KEYBOARD_EDUCATION = - unreleasedFlag(2603, "keyboard_education", teamfood = false) + @JvmField val KEYBOARD_EDUCATION = unreleasedFlag(2603, "keyboard_education", teamfood = false) // TODO(b/277201412): Tracking Bug @JvmField - val SPLIT_SHADE_SUBPIXEL_OPTIMIZATION = - releasedFlag(2805, "split_shade_subpixel_optimization") + val SPLIT_SHADE_SUBPIXEL_OPTIMIZATION = releasedFlag(2805, "split_shade_subpixel_optimization") // TODO(b/288868056): Tracking Bug @JvmField - val PARTIAL_SCREEN_SHARING_TASK_SWITCHER = - unreleasedFlag(288868056, "pss_task_switcher") + val PARTIAL_SCREEN_SHARING_TASK_SWITCHER = unreleasedFlag(288868056, "pss_task_switcher") // TODO(b/278761837): Tracking Bug - @JvmField - val USE_NEW_ACTIVITY_STARTER = releasedFlag(2801, name = "use_new_activity_starter") + @JvmField val USE_NEW_ACTIVITY_STARTER = releasedFlag(2801, name = "use_new_activity_starter") // 2900 - Zero Jank fixes. Naming convention is: zj__ @@ -744,23 +688,20 @@ object Flags { unreleasedFlag(3000, name = "enable_lockscreen_wallpaper_dream") // TODO(b/283084712): Tracking Bug - @JvmField - val IMPROVED_HUN_ANIMATIONS = unreleasedFlag(283084712, "improved_hun_animations") + @JvmField val IMPROVED_HUN_ANIMATIONS = unreleasedFlag(283084712, "improved_hun_animations") // TODO(b/283447257): Tracking bug @JvmField val BIGPICTURE_NOTIFICATION_LAZY_LOADING = - unreleasedFlag(283447257, "bigpicture_notification_lazy_loading") + unreleasedFlag(283447257, "bigpicture_notification_lazy_loading") // TODO(b/283740863): Tracking Bug @JvmField val ENABLE_NEW_PRIVACY_DIALOG = - unreleasedFlag(283740863, "enable_new_privacy_dialog", teamfood = false) + unreleasedFlag(283740863, "enable_new_privacy_dialog", teamfood = false) // TODO(b/289573946): Tracking Bug - @JvmField - val PRECOMPUTED_TEXT = - unreleasedFlag(289573946, "precomputed_text") + @JvmField val PRECOMPUTED_TEXT = unreleasedFlag(289573946, "precomputed_text") // 2900 - CentralSurfaces-related flags @@ -773,6 +714,5 @@ object Flags { // TODO(b/290213663): Tracking Bug @JvmField - val ONE_WAY_HAPTICS_API_MIGRATION = - unreleasedFlag(3100, "oneway_haptics_api_migration") + val ONE_WAY_HAPTICS_API_MIGRATION = unreleasedFlag(3100, "oneway_haptics_api_migration") } diff --git a/packages/SystemUI/src/com/android/systemui/multishade/data/model/MultiShadeInteractionModel.kt b/packages/SystemUI/src/com/android/systemui/multishade/data/model/MultiShadeInteractionModel.kt deleted file mode 100644 index c48028c31cf0b..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/data/model/MultiShadeInteractionModel.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.data.model - -import com.android.systemui.multishade.shared.model.ShadeId - -/** Models the current interaction with one of the shades. */ -data class MultiShadeInteractionModel( - /** The ID of the shade that the user is currently interacting with. */ - val shadeId: ShadeId, - /** Whether the interaction is proxied (as in: coming from an external app or different UI). */ - val isProxied: Boolean, -) diff --git a/packages/SystemUI/src/com/android/systemui/multishade/data/remoteproxy/MultiShadeInputProxy.kt b/packages/SystemUI/src/com/android/systemui/multishade/data/remoteproxy/MultiShadeInputProxy.kt deleted file mode 100644 index 86f0c0d15b55b..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/data/remoteproxy/MultiShadeInputProxy.kt +++ /dev/null @@ -1,47 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.data.remoteproxy - -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import javax.inject.Inject -import javax.inject.Singleton -import kotlinx.coroutines.channels.BufferOverflow -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableSharedFlow -import kotlinx.coroutines.flow.asSharedFlow - -/** - * Acts as a hub for routing proxied user input into the multi shade system. - * - * "Proxied" user input is coming through a proxy; typically from an external app or different UI. - * In other words: it's not user input that's occurring directly on the shade UI itself. This class - * is that proxy. - */ -@Singleton -class MultiShadeInputProxy @Inject constructor() { - private val _proxiedTouch = - MutableSharedFlow( - replay = 1, - onBufferOverflow = BufferOverflow.DROP_OLDEST, - ) - val proxiedInput: Flow = _proxiedTouch.asSharedFlow() - - fun onProxiedInput(proxiedInput: ProxiedInputModel) { - _proxiedTouch.tryEmit(proxiedInput) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/multishade/data/repository/MultiShadeRepository.kt b/packages/SystemUI/src/com/android/systemui/multishade/data/repository/MultiShadeRepository.kt deleted file mode 100644 index 1172030127575..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/data/repository/MultiShadeRepository.kt +++ /dev/null @@ -1,157 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.data.repository - -import android.content.Context -import androidx.annotation.FloatRange -import com.android.systemui.R -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.multishade.data.model.MultiShadeInteractionModel -import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.multishade.shared.model.ShadeConfig -import com.android.systemui.multishade.shared.model.ShadeId -import com.android.systemui.multishade.shared.model.ShadeModel -import javax.inject.Inject -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.MutableStateFlow -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.asStateFlow - -/** Encapsulates application state for all shades. */ -@SysUISingleton -class MultiShadeRepository -@Inject -constructor( - @Application private val applicationContext: Context, - inputProxy: MultiShadeInputProxy, -) { - /** - * Remote input coming from sources outside of system UI (for example, swiping down on the - * Launcher or from the status bar). - */ - val proxiedInput: Flow = inputProxy.proxiedInput - - /** Width of the left-hand side shade, in pixels. */ - private val leftShadeWidthPx = - applicationContext.resources.getDimensionPixelSize(R.dimen.left_shade_width) - - /** Width of the right-hand side shade, in pixels. */ - private val rightShadeWidthPx = - applicationContext.resources.getDimensionPixelSize(R.dimen.right_shade_width) - - /** - * The amount that the user must swipe up when the shade is fully expanded to automatically - * collapse once the user lets go of the shade. If the user swipes less than this amount, the - * shade will automatically revert back to fully expanded once the user stops swiping. - * - * This is a fraction between `0` and `1`. - */ - private val swipeCollapseThreshold = - checkInBounds(applicationContext.resources.getFloat(R.dimen.shade_swipe_collapse_threshold)) - - /** - * The amount that the user must swipe down when the shade is fully collapsed to automatically - * expand once the user lets go of the shade. If the user swipes less than this amount, the - * shade will automatically revert back to fully collapsed once the user stops swiping. - * - * This is a fraction between `0` and `1`. - */ - private val swipeExpandThreshold = - checkInBounds(applicationContext.resources.getFloat(R.dimen.shade_swipe_expand_threshold)) - - /** - * Maximum opacity when the scrim that shows up behind the dual shades is fully visible. - * - * This is a fraction between `0` and `1`. - */ - private val dualShadeScrimAlpha = - checkInBounds(applicationContext.resources.getFloat(R.dimen.dual_shade_scrim_alpha)) - - /** The current configuration of the shade system. */ - val shadeConfig: StateFlow = - MutableStateFlow( - if (applicationContext.resources.getBoolean(R.bool.dual_shade_enabled)) { - ShadeConfig.DualShadeConfig( - leftShadeWidthPx = leftShadeWidthPx, - rightShadeWidthPx = rightShadeWidthPx, - swipeCollapseThreshold = swipeCollapseThreshold, - swipeExpandThreshold = swipeExpandThreshold, - splitFraction = - applicationContext.resources.getFloat( - R.dimen.dual_shade_split_fraction - ), - scrimAlpha = dualShadeScrimAlpha, - ) - } else { - ShadeConfig.SingleShadeConfig( - swipeCollapseThreshold = swipeCollapseThreshold, - swipeExpandThreshold = swipeExpandThreshold, - ) - } - ) - .asStateFlow() - - private val _forceCollapseAll = MutableStateFlow(false) - /** Whether all shades should be collapsed. */ - val forceCollapseAll: StateFlow = _forceCollapseAll.asStateFlow() - - private val _shadeInteraction = MutableStateFlow(null) - /** The current shade interaction or `null` if no shade is interacted with currently. */ - val shadeInteraction: StateFlow = _shadeInteraction.asStateFlow() - - private val stateByShade = mutableMapOf>() - - /** The model for the shade with the given ID. */ - fun getShade( - shadeId: ShadeId, - ): StateFlow { - return getMutableShade(shadeId).asStateFlow() - } - - /** Sets the expansion amount for the shade with the given ID. */ - fun setExpansion( - shadeId: ShadeId, - @FloatRange(from = 0.0, to = 1.0) expansion: Float, - ) { - getMutableShade(shadeId).let { mutableState -> - mutableState.value = mutableState.value.copy(expansion = expansion) - } - } - - /** Sets whether all shades should be immediately forced to collapse. */ - fun setForceCollapseAll(isForced: Boolean) { - _forceCollapseAll.value = isForced - } - - /** Sets the current shade interaction; use `null` if no shade is interacted with currently. */ - fun setShadeInteraction(shadeInteraction: MultiShadeInteractionModel?) { - _shadeInteraction.value = shadeInteraction - } - - private fun getMutableShade(id: ShadeId): MutableStateFlow { - return stateByShade.getOrPut(id) { MutableStateFlow(ShadeModel(id)) } - } - - /** Asserts that the given [Float] is in the range of `0` and `1`, inclusive. */ - private fun checkInBounds(float: Float): Float { - check(float in 0f..1f) { "$float isn't between 0 and 1." } - return float - } -} diff --git a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt deleted file mode 100644 index ebb8639b8922a..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractor.kt +++ /dev/null @@ -1,327 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.domain.interactor - -import androidx.annotation.FloatRange -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.multishade.data.model.MultiShadeInteractionModel -import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy -import com.android.systemui.multishade.data.repository.MultiShadeRepository -import com.android.systemui.multishade.shared.math.isZero -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.multishade.shared.model.ShadeConfig -import com.android.systemui.multishade.shared.model.ShadeId -import com.android.systemui.multishade.shared.model.ShadeModel -import javax.inject.Inject -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharedFlow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.onEach -import kotlinx.coroutines.flow.shareIn -import kotlinx.coroutines.yield - -/** Encapsulates business logic related to interactions with the multi-shade system. */ -@OptIn(ExperimentalCoroutinesApi::class) -@SysUISingleton -class MultiShadeInteractor -@Inject -constructor( - @Application private val applicationScope: CoroutineScope, - private val repository: MultiShadeRepository, - private val inputProxy: MultiShadeInputProxy, -) { - /** The current configuration of the shade system. */ - val shadeConfig: StateFlow = repository.shadeConfig - - /** The expansion of the shade that's most expanded. */ - val maxShadeExpansion: Flow = - repository.shadeConfig.flatMapLatest { shadeConfig -> - combine(allShades(shadeConfig)) { shadeModels -> - shadeModels.maxOfOrNull { it.expansion } ?: 0f - } - } - - /** Whether any shade is expanded, even a little bit. */ - val isAnyShadeExpanded: Flow = - maxShadeExpansion.map { maxExpansion -> !maxExpansion.isZero() }.distinctUntilChanged() - - /** - * A _processed_ version of the proxied input flow. - * - * All internal dependencies on the proxied input flow *must* use this one for two reasons: - * 1. It's a [SharedFlow] so we only do the upstream work once, no matter how many usages we - * actually have. - * 2. It actually does some preprocessing as the proxied input events stream through, handling - * common things like recording the current state of the system based on incoming input - * events. - */ - private val processedProxiedInput: SharedFlow = - combine( - repository.shadeConfig, - repository.proxiedInput.distinctUntilChanged(), - ::Pair, - ) - .map { (shadeConfig, proxiedInput) -> - if (proxiedInput !is ProxiedInputModel.OnTap) { - // If the user is interacting with any other gesture type (for instance, - // dragging), - // we no longer want to force collapse all shades. - repository.setForceCollapseAll(false) - } - - when (proxiedInput) { - is ProxiedInputModel.OnDrag -> { - val affectedShadeId = affectedShadeId(shadeConfig, proxiedInput.xFraction) - // This might be the start of a new drag gesture, let's update our - // application - // state to record that fact. - onUserInteractionStarted( - shadeId = affectedShadeId, - isProxied = true, - ) - } - is ProxiedInputModel.OnTap -> { - // Tapping outside any shade collapses all shades. This code path is not hit - // for - // taps that happen _inside_ a shade as that input event is directly applied - // through the UI and is, hence, not a proxied input. - collapseAll() - } - else -> Unit - } - - proxiedInput - } - .shareIn( - scope = applicationScope, - started = SharingStarted.Eagerly, - replay = 1, - ) - - /** Whether the shade with the given ID should be visible. */ - fun isVisible(shadeId: ShadeId): Flow { - return repository.shadeConfig.map { shadeConfig -> shadeConfig.shadeIds.contains(shadeId) } - } - - /** Whether direct user input is allowed on the shade with the given ID. */ - fun isNonProxiedInputAllowed(shadeId: ShadeId): Flow { - return combine( - isForceCollapsed(shadeId), - repository.shadeInteraction, - ::Pair, - ) - .map { (isForceCollapsed, shadeInteraction) -> - !isForceCollapsed && shadeInteraction?.isProxied != true - } - } - - /** Whether the shade with the given ID is forced to collapse. */ - fun isForceCollapsed(shadeId: ShadeId): Flow { - return combine( - repository.forceCollapseAll, - repository.shadeInteraction.map { it?.shadeId }, - ::Pair, - ) - .map { (collapseAll, userInteractedShadeIdOrNull) -> - val counterpartShadeIdOrNull = - when (shadeId) { - ShadeId.SINGLE -> null - ShadeId.LEFT -> ShadeId.RIGHT - ShadeId.RIGHT -> ShadeId.LEFT - } - - when { - // If all shades have been told to collapse (by a tap outside, for example), - // then this shade is collapsed. - collapseAll -> true - // A shade that doesn't have a counterpart shade cannot be force-collapsed by - // interactions on the counterpart shade. - counterpartShadeIdOrNull == null -> false - // If the current user interaction is on the counterpart shade, then this shade - // should be force-collapsed. - else -> userInteractedShadeIdOrNull == counterpartShadeIdOrNull - } - } - } - - /** - * Proxied input affecting the shade with the given ID. This is input coming from sources - * outside of system UI (for example, swiping down on the Launcher or from the status bar) or - * outside the UI of any shade (for example, the scrim that's shown behind the shades). - */ - fun proxiedInput(shadeId: ShadeId): Flow { - return combine( - processedProxiedInput, - isForceCollapsed(shadeId).distinctUntilChanged(), - repository.shadeInteraction, - ::Triple, - ) - .map { (proxiedInput, isForceCollapsed, shadeInteraction) -> - when { - // If the shade is force-collapsed, we ignored proxied input on it. - isForceCollapsed -> null - // If the proxied input does not belong to this shade, ignore it. - shadeInteraction?.shadeId != shadeId -> null - // If there is ongoing non-proxied user input on any shade, ignore the - // proxied input. - !shadeInteraction.isProxied -> null - // Otherwise, send the proxied input downstream. - else -> proxiedInput - } - } - .onEach { proxiedInput -> - // We use yield() to make sure that the following block of code happens _after_ - // downstream collectors had a chance to process the proxied input. Otherwise, we - // might change our state to clear the current UserInteraction _before_ those - // downstream collectors get a chance to process the proxied input, which will make - // them ignore it (since they ignore proxied input when the current user interaction - // doesn't match their shade). - yield() - - if ( - proxiedInput is ProxiedInputModel.OnDragEnd || - proxiedInput is ProxiedInputModel.OnDragCancel - ) { - onUserInteractionEnded(shadeId = shadeId, isProxied = true) - } - } - } - - /** Sets the expansion amount for the shade with the given ID. */ - fun setExpansion( - shadeId: ShadeId, - @FloatRange(from = 0.0, to = 1.0) expansion: Float, - ) { - repository.setExpansion(shadeId, expansion) - } - - /** Collapses all shades. */ - fun collapseAll() { - repository.setForceCollapseAll(true) - } - - /** - * Notifies that a new non-proxied interaction may have started. Safe to call multiple times for - * the same interaction as it won't overwrite an existing interaction. - * - * Existing interactions can be cleared by calling [onUserInteractionEnded]. - */ - fun onUserInteractionStarted(shadeId: ShadeId) { - onUserInteractionStarted( - shadeId = shadeId, - isProxied = false, - ) - } - - /** - * Notifies that the current non-proxied interaction has ended. - * - * Safe to call multiple times, even if there's no current interaction or even if the current - * interaction doesn't belong to the given shade or is proxied as the code is a no-op unless - * there's a match between the parameters and the current interaction. - */ - fun onUserInteractionEnded( - shadeId: ShadeId, - ) { - onUserInteractionEnded( - shadeId = shadeId, - isProxied = false, - ) - } - - fun sendProxiedInput(proxiedInput: ProxiedInputModel) { - inputProxy.onProxiedInput(proxiedInput) - } - - /** - * Notifies that a new interaction may have started. Safe to call multiple times for the same - * interaction as it won't overwrite an existing interaction. - * - * Existing interactions can be cleared by calling [onUserInteractionEnded]. - */ - private fun onUserInteractionStarted( - shadeId: ShadeId, - isProxied: Boolean, - ) { - if (repository.shadeInteraction.value != null) { - return - } - - repository.setShadeInteraction( - MultiShadeInteractionModel( - shadeId = shadeId, - isProxied = isProxied, - ) - ) - } - - /** - * Notifies that the current interaction has ended. - * - * Safe to call multiple times, even if there's no current interaction or even if the current - * interaction doesn't belong to the given shade or [isProxied] value as the code is a no-op - * unless there's a match between the parameters and the current interaction. - */ - private fun onUserInteractionEnded( - shadeId: ShadeId, - isProxied: Boolean, - ) { - repository.shadeInteraction.value?.let { (interactionShadeId, isInteractionProxied) -> - if (shadeId == interactionShadeId && isProxied == isInteractionProxied) { - repository.setShadeInteraction(null) - } - } - } - - /** - * Returns the ID of the shade that's affected by user input at a given coordinate. - * - * @param config The shade configuration being used. - * @param xFraction The horizontal position of the user input as a fraction along the width of - * its container where `0` is all the way to the left and `1` is all the way to the right. - */ - private fun affectedShadeId( - config: ShadeConfig, - @FloatRange(from = 0.0, to = 1.0) xFraction: Float, - ): ShadeId { - return if (config is ShadeConfig.DualShadeConfig) { - if (xFraction <= config.splitFraction) { - ShadeId.LEFT - } else { - ShadeId.RIGHT - } - } else { - ShadeId.SINGLE - } - } - - /** Returns the list of flows of all the shades in the given configuration. */ - private fun allShades( - config: ShadeConfig, - ): List> { - return config.shadeIds.map { shadeId -> repository.getShade(shadeId) } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt b/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt deleted file mode 100644 index 1894bc4cfeabe..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractor.kt +++ /dev/null @@ -1,288 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.domain.interactor - -import android.content.Context -import android.view.MotionEvent -import android.view.ViewConfiguration -import com.android.systemui.classifier.Classifier -import com.android.systemui.dagger.SysUISingleton -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.flags.FeatureFlags -import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.multishade.shared.math.isZero -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.plugins.FalsingManager -import com.android.systemui.shade.ShadeController -import javax.inject.Inject -import kotlin.math.abs -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn -import kotlinx.coroutines.launch - -/** - * Encapsulates business logic to handle [MotionEvent]-based user input. - * - * This class is meant purely for the legacy `View`-based system to be able to pass `MotionEvent`s - * into the newer multi-shade framework for processing. - */ -@SysUISingleton -class MultiShadeMotionEventInteractor -@Inject -constructor( - @Application private val applicationContext: Context, - @Application private val applicationScope: CoroutineScope, - private val multiShadeInteractor: MultiShadeInteractor, - featureFlags: FeatureFlags, - keyguardTransitionInteractor: KeyguardTransitionInteractor, - private val falsingManager: FalsingManager, - private val shadeController: ShadeController, -) { - init { - if (featureFlags.isEnabled(Flags.DUAL_SHADE)) { - applicationScope.launch { - multiShadeInteractor.isAnyShadeExpanded.collect { - if (!it && !shadeController.isKeyguard) { - shadeController.makeExpandedInvisible() - } else { - shadeController.makeExpandedVisible(false) - } - } - } - } - } - - private val isAnyShadeExpanded: StateFlow = - multiShadeInteractor.isAnyShadeExpanded.stateIn( - scope = applicationScope, - started = SharingStarted.Eagerly, - initialValue = false, - ) - - private val isBouncerShowing: StateFlow = - keyguardTransitionInteractor - .transitionValue(state = KeyguardState.PRIMARY_BOUNCER) - .map { !it.isZero() } - .stateIn( - scope = applicationScope, - started = SharingStarted.Eagerly, - initialValue = false, - ) - - private var interactionState: InteractionState? = null - - /** - * Returns `true` if the given [MotionEvent] and the rest of events in this gesture should be - * passed to this interactor's [onTouchEvent] method. - * - * Note: the caller should continue to pass [MotionEvent] instances into this method, even if it - * returns `false` as the gesture may be intercepted mid-stream. - */ - fun shouldIntercept(event: MotionEvent): Boolean { - if (isAnyShadeExpanded.value) { - // If any shade is expanded, we assume that touch handling outside the shades is handled - // by the scrim that appears behind the shades. No need to intercept anything here. - return false - } - - if (isBouncerShowing.value) { - return false - } - - return when (event.actionMasked) { - MotionEvent.ACTION_DOWN -> { - // Record where the pointer was placed and which pointer it was. - interactionState = - InteractionState( - initialX = event.x, - initialY = event.y, - currentY = event.y, - pointerId = event.getPointerId(0), - isDraggingHorizontally = false, - isDraggingShade = false, - ) - - false - } - MotionEvent.ACTION_MOVE -> { - onMove(event) - - // We want to intercept the rest of the gesture if we're dragging the shade. - isDraggingShade() - } - MotionEvent.ACTION_UP, - MotionEvent.ACTION_CANCEL -> - // Make sure that we intercept the up or cancel if we're dragging the shade, to - // handle drag end or cancel. - isDraggingShade() - else -> false - } - } - - /** - * Notifies that a [MotionEvent] in a series of events of a gesture that was intercepted due to - * the result of [shouldIntercept] has been received. - * - * @param event The [MotionEvent] to handle. - * @param viewWidthPx The width of the view, in pixels. - * @return `true` if the event was consumed, `false` otherwise. - */ - fun onTouchEvent(event: MotionEvent, viewWidthPx: Int): Boolean { - return when (event.actionMasked) { - MotionEvent.ACTION_MOVE -> { - interactionState?.let { - if (it.isDraggingShade) { - val pointerIndex = event.findPointerIndex(it.pointerId) - val previousY = it.currentY - val currentY = event.getY(pointerIndex) - interactionState = it.copy(currentY = currentY) - - val yDragAmountPx = currentY - previousY - - if (yDragAmountPx != 0f) { - multiShadeInteractor.sendProxiedInput( - ProxiedInputModel.OnDrag( - xFraction = event.x / viewWidthPx, - yDragAmountPx = yDragAmountPx, - ) - ) - } - true - } else { - onMove(event) - isDraggingShade() - } - } - ?: false - } - MotionEvent.ACTION_UP -> { - if (isDraggingShade()) { - // We finished dragging the shade. Record that so the multi-shade framework can - // issue a fling, if the velocity reached in the drag was high enough, for - // example. - multiShadeInteractor.sendProxiedInput(ProxiedInputModel.OnDragEnd) - - if (falsingManager.isFalseTouch(Classifier.SHADE_DRAG)) { - multiShadeInteractor.collapseAll() - } - } - - interactionState = null - true - } - MotionEvent.ACTION_POINTER_UP -> { - val removedPointerId = event.getPointerId(event.actionIndex) - if (removedPointerId == interactionState?.pointerId && event.pointerCount > 1) { - // We removed the original pointer but there must be another pointer because the - // gesture is still ongoing. Let's switch to that pointer. - interactionState = - event.firstUnremovedPointerId(removedPointerId)?.let { replacementPointerId - -> - interactionState?.copy( - pointerId = replacementPointerId, - // We want to update the currentY of our state so that the - // transition to the next pointer doesn't report a big jump between - // the Y coordinate of the removed pointer and the Y coordinate of - // the replacement pointer. - currentY = event.getY(replacementPointerId), - ) - } - } - true - } - MotionEvent.ACTION_CANCEL -> { - if (isDraggingShade()) { - // Our drag gesture was canceled by the system. This happens primarily in one of - // two occasions: (a) the parent view has decided to intercept the gesture - // itself and/or route it to a different child view or (b) the pointer has - // traveled beyond the bounds of our view and/or the touch display. Either way, - // we pass the cancellation event to the multi-shade framework to record it. - // Doing that allows the multi-shade framework to know that the gesture ended to - // allow new gestures to be accepted. - multiShadeInteractor.sendProxiedInput(ProxiedInputModel.OnDragCancel) - - if (falsingManager.isFalseTouch(Classifier.SHADE_DRAG)) { - multiShadeInteractor.collapseAll() - } - } - - interactionState = null - true - } - else -> false - } - } - - /** - * Handles [MotionEvent.ACTION_MOVE] and sets whether or not we are dragging shade in our - * current interaction - * - * @param event The [MotionEvent] to handle. - */ - private fun onMove(event: MotionEvent) { - interactionState?.let { - val pointerIndex = event.findPointerIndex(it.pointerId) - val currentX = event.getX(pointerIndex) - val currentY = event.getY(pointerIndex) - if (!it.isDraggingHorizontally && !it.isDraggingShade) { - val xDistanceTravelled = currentX - it.initialX - val yDistanceTravelled = currentY - it.initialY - val touchSlop = ViewConfiguration.get(applicationContext).scaledTouchSlop - interactionState = - when { - yDistanceTravelled > touchSlop -> it.copy(isDraggingShade = true) - abs(xDistanceTravelled) > touchSlop -> - it.copy(isDraggingHorizontally = true) - else -> interactionState - } - } - } - } - - private data class InteractionState( - val initialX: Float, - val initialY: Float, - val currentY: Float, - val pointerId: Int, - /** Whether the current gesture is dragging horizontally. */ - val isDraggingHorizontally: Boolean, - /** Whether the current gesture is dragging the shade vertically. */ - val isDraggingShade: Boolean, - ) - - private fun isDraggingShade(): Boolean { - return interactionState?.isDraggingShade ?: false - } - - /** - * Returns the index of the first pointer that is not [removedPointerId] or `null`, if there is - * no other pointer. - */ - private fun MotionEvent.firstUnremovedPointerId(removedPointerId: Int): Int? { - return (0 until pointerCount) - .firstOrNull { pointerIndex -> - val pointerId = getPointerId(pointerIndex) - pointerId != removedPointerId - } - ?.let { pointerIndex -> getPointerId(pointerIndex) } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/multishade/shared/math/Math.kt b/packages/SystemUI/src/com/android/systemui/multishade/shared/math/Math.kt deleted file mode 100644 index c2eaf72a841a2..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/shared/math/Math.kt +++ /dev/null @@ -1,27 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.shared.math - -import androidx.annotation.VisibleForTesting -import kotlin.math.abs - -/** Returns `true` if this [Float] is within [epsilon] of `0`. */ -fun Float.isZero(epsilon: Float = EPSILON): Boolean { - return abs(this) < epsilon -} - -@VisibleForTesting private const val EPSILON = 0.0001f diff --git a/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ProxiedInputModel.kt b/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ProxiedInputModel.kt deleted file mode 100644 index ee1dd65b867f6..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ProxiedInputModel.kt +++ /dev/null @@ -1,50 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.shared.model - -import androidx.annotation.FloatRange - -/** - * Models a part of an ongoing proxied user input gesture. - * - * "Proxied" user input is coming through a proxy; typically from an external app or different UI. - * In other words: it's not user input that's occurring directly on the shade UI itself. - */ -sealed class ProxiedInputModel { - /** The user is dragging their pointer. */ - data class OnDrag( - /** - * The relative position of the pointer as a fraction of its container width where `0` is - * all the way to the left and `1` is all the way to the right. - */ - @FloatRange(from = 0.0, to = 1.0) val xFraction: Float, - /** The amount that the pointer was dragged, in pixels. */ - val yDragAmountPx: Float, - ) : ProxiedInputModel() - - /** The user finished dragging by lifting up their pointer. */ - object OnDragEnd : ProxiedInputModel() - - /** - * The drag gesture has been canceled. Usually because the pointer exited the draggable area. - */ - object OnDragCancel : ProxiedInputModel() - - /** The user has tapped (clicked). */ - object OnTap : ProxiedInputModel() -} diff --git a/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ShadeConfig.kt b/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ShadeConfig.kt deleted file mode 100644 index a4cd35c8a11ab..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ShadeConfig.kt +++ /dev/null @@ -1,79 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.shared.model - -import androidx.annotation.FloatRange - -/** Enumerates the various possible configurations of the shade system. */ -sealed class ShadeConfig( - - /** IDs of the shade(s) in this configuration. */ - open val shadeIds: List, - - /** - * The amount that the user must swipe up when the shade is fully expanded to automatically - * collapse once the user lets go of the shade. If the user swipes less than this amount, the - * shade will automatically revert back to fully expanded once the user stops swiping. - */ - @FloatRange(from = 0.0, to = 1.0) open val swipeCollapseThreshold: Float, - - /** - * The amount that the user must swipe down when the shade is fully collapsed to automatically - * expand once the user lets go of the shade. If the user swipes less than this amount, the - * shade will automatically revert back to fully collapsed once the user stops swiping. - */ - @FloatRange(from = 0.0, to = 1.0) open val swipeExpandThreshold: Float, -) { - - /** There is a single shade. */ - data class SingleShadeConfig( - @FloatRange(from = 0.0, to = 1.0) override val swipeCollapseThreshold: Float, - @FloatRange(from = 0.0, to = 1.0) override val swipeExpandThreshold: Float, - ) : - ShadeConfig( - shadeIds = listOf(ShadeId.SINGLE), - swipeCollapseThreshold = swipeCollapseThreshold, - swipeExpandThreshold = swipeExpandThreshold, - ) - - /** There are two shades arranged side-by-side. */ - data class DualShadeConfig( - /** Width of the left-hand side shade. */ - val leftShadeWidthPx: Int, - /** Width of the right-hand side shade. */ - val rightShadeWidthPx: Int, - @FloatRange(from = 0.0, to = 1.0) override val swipeCollapseThreshold: Float, - @FloatRange(from = 0.0, to = 1.0) override val swipeExpandThreshold: Float, - /** - * The position of the "split" between interaction areas for each of the shades, as a - * fraction of the width of the container. - * - * Interactions that occur on the start-side (left-hand side in left-to-right languages like - * English) affect the start-side shade. Interactions that occur on the end-side (right-hand - * side in left-to-right languages like English) affect the end-side shade. - */ - @FloatRange(from = 0.0, to = 1.0) val splitFraction: Float, - /** Maximum opacity when the scrim that shows up behind the dual shades is fully visible. */ - @FloatRange(from = 0.0, to = 1.0) val scrimAlpha: Float, - ) : - ShadeConfig( - shadeIds = listOf(ShadeId.LEFT, ShadeId.RIGHT), - swipeCollapseThreshold = swipeCollapseThreshold, - swipeExpandThreshold = swipeExpandThreshold, - ) -} diff --git a/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ShadeId.kt b/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ShadeId.kt deleted file mode 100644 index 9e026576e8427..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ShadeId.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.shared.model - -/** Enumerates all known shade IDs. */ -enum class ShadeId { - /** ID of the shade on the left in dual shade configurations. */ - LEFT, - /** ID of the shade on the right in dual shade configurations. */ - RIGHT, - /** ID of the single shade in single shade configurations. */ - SINGLE, -} diff --git a/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ShadeModel.kt b/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ShadeModel.kt deleted file mode 100644 index 49ac64c58cb83..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/shared/model/ShadeModel.kt +++ /dev/null @@ -1,26 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.shared.model - -import androidx.annotation.FloatRange - -/** Models the current state of a shade. */ -data class ShadeModel( - val id: ShadeId, - @FloatRange(from = 0.0, to = 1.0) val expansion: Float = 0f, -) diff --git a/packages/SystemUI/src/com/android/systemui/multishade/ui/view/MultiShadeView.kt b/packages/SystemUI/src/com/android/systemui/multishade/ui/view/MultiShadeView.kt deleted file mode 100644 index aecec39c5c07c..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/ui/view/MultiShadeView.kt +++ /dev/null @@ -1,71 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.ui.view - -import android.content.Context -import android.util.AttributeSet -import android.widget.FrameLayout -import androidx.lifecycle.Lifecycle -import androidx.lifecycle.lifecycleScope -import androidx.lifecycle.repeatOnLifecycle -import com.android.systemui.compose.ComposeFacade -import com.android.systemui.lifecycle.repeatWhenAttached -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor -import com.android.systemui.multishade.ui.viewmodel.MultiShadeViewModel -import com.android.systemui.util.time.SystemClock -import kotlinx.coroutines.launch - -/** - * View that hosts the multi-shade system and acts as glue between legacy code and the - * implementation. - */ -class MultiShadeView( - context: Context, - attrs: AttributeSet?, -) : - FrameLayout( - context, - attrs, - ) { - - fun init( - interactor: MultiShadeInteractor, - clock: SystemClock, - ) { - repeatWhenAttached { - lifecycleScope.launch { - repeatOnLifecycle(Lifecycle.State.CREATED) { - addView( - ComposeFacade.createMultiShadeView( - context = context, - viewModel = - MultiShadeViewModel( - viewModelScope = this, - interactor = interactor, - ), - clock = clock, - ) - ) - } - - // Here when destroyed. - removeAllViews() - } - } - } -} diff --git a/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt deleted file mode 100644 index ed92c5469d236..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModel.kt +++ /dev/null @@ -1,108 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.ui.viewmodel - -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.multishade.shared.model.ShadeConfig -import com.android.systemui.multishade.shared.model.ShadeId -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.combine -import kotlinx.coroutines.flow.flatMapLatest -import kotlinx.coroutines.flow.flowOf -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn - -/** Models UI state for UI that supports multi (or single) shade. */ -@OptIn(ExperimentalCoroutinesApi::class) -class MultiShadeViewModel( - viewModelScope: CoroutineScope, - private val interactor: MultiShadeInteractor, -) { - /** Models UI state for the single shade. */ - val singleShade = - ShadeViewModel( - viewModelScope, - ShadeId.SINGLE, - interactor, - ) - - /** Models UI state for the shade on the left-hand side. */ - val leftShade = - ShadeViewModel( - viewModelScope, - ShadeId.LEFT, - interactor, - ) - - /** Models UI state for the shade on the right-hand side. */ - val rightShade = - ShadeViewModel( - viewModelScope, - ShadeId.RIGHT, - interactor, - ) - - /** The amount of alpha that the scrim should have. This is a value between `0` and `1`. */ - val scrimAlpha: StateFlow = - combine( - interactor.maxShadeExpansion, - interactor.shadeConfig - .map { it as? ShadeConfig.DualShadeConfig } - .map { dualShadeConfigOrNull -> dualShadeConfigOrNull?.scrimAlpha ?: 0f }, - ::Pair, - ) - .map { (anyShadeExpansion, scrimAlpha) -> - (anyShadeExpansion * scrimAlpha).coerceIn(0f, 1f) - } - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(), - initialValue = 0f, - ) - - /** Whether the scrim should accept touch events. */ - val isScrimEnabled: StateFlow = - interactor.shadeConfig - .flatMapLatest { shadeConfig -> - when (shadeConfig) { - // In the dual shade configuration, the scrim is enabled when the expansion is - // greater than zero on any one of the shades. - is ShadeConfig.DualShadeConfig -> interactor.isAnyShadeExpanded - // No scrim in the single shade configuration. - is ShadeConfig.SingleShadeConfig -> flowOf(false) - } - } - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) - - /** Notifies that the scrim has been touched. */ - fun onScrimTouched(proxiedInput: ProxiedInputModel) { - if (!isScrimEnabled.value) { - return - } - - interactor.sendProxiedInput(proxiedInput) - } -} diff --git a/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/ShadeViewModel.kt b/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/ShadeViewModel.kt deleted file mode 100644 index e828dbdc6c62f..0000000000000 --- a/packages/SystemUI/src/com/android/systemui/multishade/ui/viewmodel/ShadeViewModel.kt +++ /dev/null @@ -1,150 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.ui.viewmodel - -import androidx.annotation.FloatRange -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.multishade.shared.model.ShadeConfig -import com.android.systemui.multishade.shared.model.ShadeId -import kotlinx.coroutines.CoroutineScope -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.SharingStarted -import kotlinx.coroutines.flow.StateFlow -import kotlinx.coroutines.flow.distinctUntilChanged -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.flow.stateIn - -/** Models UI state for a single shade. */ -class ShadeViewModel( - viewModelScope: CoroutineScope, - private val shadeId: ShadeId, - private val interactor: MultiShadeInteractor, -) { - /** Whether the shade is visible. */ - val isVisible: StateFlow = - interactor - .isVisible(shadeId) - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) - - /** Whether swiping on the shade UI is currently enabled. */ - val isSwipingEnabled: StateFlow = - interactor - .isNonProxiedInputAllowed(shadeId) - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(), - initialValue = false, - ) - - /** Whether the shade must be collapsed immediately. */ - val isForceCollapsed: Flow = - interactor.isForceCollapsed(shadeId).distinctUntilChanged() - - /** The width of the shade. */ - val width: StateFlow = - interactor.shadeConfig - .map { shadeWidth(it) } - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(), - initialValue = shadeWidth(interactor.shadeConfig.value), - ) - - /** - * The amount that the user must swipe up when the shade is fully expanded to automatically - * collapse once the user lets go of the shade. If the user swipes less than this amount, the - * shade will automatically revert back to fully expanded once the user stops swiping. - */ - val swipeCollapseThreshold: StateFlow = - interactor.shadeConfig - .map { it.swipeCollapseThreshold } - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(), - initialValue = interactor.shadeConfig.value.swipeCollapseThreshold, - ) - - /** - * The amount that the user must swipe down when the shade is fully collapsed to automatically - * expand once the user lets go of the shade. If the user swipes less than this amount, the - * shade will automatically revert back to fully collapsed once the user stops swiping. - */ - val swipeExpandThreshold: StateFlow = - interactor.shadeConfig - .map { it.swipeExpandThreshold } - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(), - initialValue = interactor.shadeConfig.value.swipeExpandThreshold, - ) - - /** - * Proxied input affecting the shade. This is input coming from sources outside of system UI - * (for example, swiping down on the Launcher or from the status bar) or outside the UI of any - * shade (for example, the scrim that's shown behind the shades). - */ - val proxiedInput: Flow = - interactor - .proxiedInput(shadeId) - .stateIn( - scope = viewModelScope, - started = SharingStarted.WhileSubscribed(), - initialValue = null, - ) - - /** Notifies that the expansion amount for the shade has changed. */ - fun onExpansionChanged( - expansion: Float, - ) { - interactor.setExpansion(shadeId, expansion.coerceIn(0f, 1f)) - } - - /** Notifies that a drag gesture has started. */ - fun onDragStarted() { - interactor.onUserInteractionStarted(shadeId) - } - - /** Notifies that a drag gesture has ended. */ - fun onDragEnded() { - interactor.onUserInteractionEnded(shadeId = shadeId) - } - - private fun shadeWidth(shadeConfig: ShadeConfig): Size { - return when (shadeId) { - ShadeId.LEFT -> - Size.Pixels((shadeConfig as? ShadeConfig.DualShadeConfig)?.leftShadeWidthPx ?: 0) - ShadeId.RIGHT -> - Size.Pixels((shadeConfig as? ShadeConfig.DualShadeConfig)?.rightShadeWidthPx ?: 0) - ShadeId.SINGLE -> Size.Fraction(1f) - } - } - - sealed class Size { - data class Fraction( - @FloatRange(from = 0.0, to = 1.0) val fraction: Float, - ) : Size() - data class Pixels( - val pixels: Int, - ) : Size() - } -} diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java index 202d6e66d8008..e428976c5ce2f 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationPanelViewController.java @@ -147,7 +147,6 @@ import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.KeyguardMediaController; import com.android.systemui.media.controls.ui.MediaHierarchyManager; import com.android.systemui.model.SysUiState; -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationBarView; import com.android.systemui.navigationbar.NavigationModeController; @@ -365,7 +364,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private KeyguardBottomAreaView mKeyguardBottomArea; private boolean mExpanding; private boolean mSplitShadeEnabled; - private boolean mDualShadeEnabled; /** The bottom padding reserved for elements of the keyguard measuring notifications. */ private float mKeyguardNotificationBottomPadding; /** @@ -599,7 +597,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump private final KeyguardTransitionInteractor mKeyguardTransitionInteractor; private final KeyguardInteractor mKeyguardInteractor; private final KeyguardViewConfigurator mKeyguardViewConfigurator; - private final @Nullable MultiShadeInteractor mMultiShadeInteractor; private final CoroutineDispatcher mMainDispatcher; private boolean mIsAnyMultiShadeExpanded; private boolean mIsOcclusionTransitionRunning = false; @@ -735,7 +732,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump LockscreenToOccludedTransitionViewModel lockscreenToOccludedTransitionViewModel, @Main CoroutineDispatcher mainDispatcher, KeyguardTransitionInteractor keyguardTransitionInteractor, - Provider multiShadeInteractorProvider, DumpManager dumpManager, KeyguardLongPressViewModel keyguardLongPressViewModel, KeyguardInteractor keyguardInteractor, @@ -839,8 +835,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mFeatureFlags = featureFlags; mAnimateBack = mFeatureFlags.isEnabled(Flags.WM_SHADE_ANIMATE_BACK_GESTURE); mTrackpadGestureFeaturesEnabled = mFeatureFlags.isEnabled(Flags.TRACKPAD_GESTURE_FEATURES); - mDualShadeEnabled = mFeatureFlags.isEnabled(Flags.DUAL_SHADE); - mMultiShadeInteractor = mDualShadeEnabled ? multiShadeInteractorProvider.get() : null; mFalsingCollector = falsingCollector; mPowerManager = powerManager; mWakeUpCoordinator = coordinator; @@ -1079,11 +1073,6 @@ public final class NotificationPanelViewController implements ShadeSurface, Dump mNotificationPanelUnfoldAnimationController.ifPresent(controller -> controller.setup(mNotificationContainerParent)); - if (mDualShadeEnabled) { - collectFlow(mView, mMultiShadeInteractor.isAnyShadeExpanded(), - mMultiShadeExpansionConsumer, mMainDispatcher); - } - // Dreaming->Lockscreen collectFlow(mView, mKeyguardTransitionInteractor.getDreamingToLockscreenTransition(), mDreamingToLockscreenTransition, mMainDispatcher); diff --git a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java index 108ea68ae8e02..6afed1d76918e 100644 --- a/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java +++ b/packages/SystemUI/src/com/android/systemui/shade/NotificationShadeWindowViewController.java @@ -31,9 +31,6 @@ import android.view.KeyEvent; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; -import android.view.ViewStub; - -import androidx.annotation.Nullable; import com.android.internal.annotations.VisibleForTesting; import com.android.keyguard.AuthKeyguardMessageArea; @@ -46,7 +43,6 @@ import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor; import com.android.systemui.bouncer.ui.binder.KeyguardBouncerViewBinder; import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel; import com.android.systemui.classifier.FalsingCollector; -import com.android.systemui.compose.ComposeFacade; import com.android.systemui.dagger.SysUISingleton; import com.android.systemui.dock.DockManager; import com.android.systemui.flags.FeatureFlags; @@ -57,9 +53,6 @@ import com.android.systemui.keyguard.shared.model.TransitionState; import com.android.systemui.keyguard.shared.model.TransitionStep; import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel; import com.android.systemui.log.BouncerLogger; -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor; -import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor; -import com.android.systemui.multishade.ui.view.MultiShadeView; import com.android.systemui.power.domain.interactor.PowerInteractor; import com.android.systemui.shared.animation.DisableSubpixelTextTransitionListener; import com.android.systemui.statusbar.DragDownHelper; @@ -83,7 +76,6 @@ import java.util.Optional; import java.util.function.Consumer; import javax.inject.Inject; -import javax.inject.Provider; /** * Controller for {@link NotificationShadeWindowView}. @@ -135,7 +127,6 @@ public class NotificationShadeWindowViewController { step.getTransitionState() == TransitionState.RUNNING; }; private final SystemClock mClock; - private final @Nullable MultiShadeMotionEventInteractor mMultiShadeMotionEventInteractor; @Inject public NotificationShadeWindowViewController( @@ -167,9 +158,7 @@ public class NotificationShadeWindowViewController { KeyguardTransitionInteractor keyguardTransitionInteractor, PrimaryBouncerToGoneTransitionViewModel primaryBouncerToGoneTransitionViewModel, FeatureFlags featureFlags, - Provider multiShadeInteractorProvider, SystemClock clock, - Provider multiShadeMotionEventInteractorProvider, BouncerMessageInteractor bouncerMessageInteractor, BouncerLogger bouncerLogger) { mLockscreenShadeTransitionController = transitionController; @@ -219,17 +208,6 @@ public class NotificationShadeWindowViewController { progressProvider -> progressProvider.addCallback( mDisableSubpixelTextTransitionListener)); } - if (ComposeFacade.INSTANCE.isComposeAvailable() - && featureFlags.isEnabled(Flags.DUAL_SHADE)) { - mMultiShadeMotionEventInteractor = multiShadeMotionEventInteractorProvider.get(); - final ViewStub multiShadeViewStub = mView.findViewById(R.id.multi_shade_stub); - if (multiShadeViewStub != null) { - final MultiShadeView multiShadeView = (MultiShadeView) multiShadeViewStub.inflate(); - multiShadeView.init(multiShadeInteractorProvider.get(), clock); - } - } else { - mMultiShadeMotionEventInteractor = null; - } } /** @@ -395,10 +373,7 @@ public class NotificationShadeWindowViewController { return true; } - if (mMultiShadeMotionEventInteractor != null) { - // This interactor is not null only if the dual shade feature is enabled. - return mMultiShadeMotionEventInteractor.shouldIntercept(ev); - } else if (mNotificationPanelViewController.isFullyExpanded() + if (mNotificationPanelViewController.isFullyExpanded() && mDragDownHelper.isDragDownEnabled() && !mService.isBouncerShowing() && !mStatusBarStateController.isDozing()) { @@ -428,10 +403,7 @@ public class NotificationShadeWindowViewController { return true; } - if (mMultiShadeMotionEventInteractor != null) { - // This interactor is not null only if the dual shade feature is enabled. - return mMultiShadeMotionEventInteractor.onTouchEvent(ev, mView.getWidth()); - } else if (mDragDownHelper.isDragDownEnabled() + if (mDragDownHelper.isDragDownEnabled() || mDragDownHelper.isDraggingDown()) { // we still want to finish our drag down gesture when locking the screen return mDragDownHelper.onTouchEvent(ev) || handled; diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/data/repository/MultiShadeRepositoryTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/data/repository/MultiShadeRepositoryTest.kt deleted file mode 100644 index ceacaf9557ca4..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/multishade/data/repository/MultiShadeRepositoryTest.kt +++ /dev/null @@ -1,191 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.data.repository - -import android.content.Context -import androidx.test.filters.SmallTest -import com.android.systemui.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.multishade.data.model.MultiShadeInteractionModel -import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.multishade.shared.model.ShadeConfig -import com.android.systemui.multishade.shared.model.ShadeId -import com.google.common.truth.Truth.assertThat -import com.google.common.truth.Truth.assertWithMessage -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.flow.map -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -@OptIn(ExperimentalCoroutinesApi::class) -@SmallTest -@RunWith(JUnit4::class) -class MultiShadeRepositoryTest : SysuiTestCase() { - - private lateinit var inputProxy: MultiShadeInputProxy - - @Before - fun setUp() { - inputProxy = MultiShadeInputProxy() - } - - @Test - fun proxiedInput() = runTest { - val underTest = create() - val latest: ProxiedInputModel? by collectLastValue(underTest.proxiedInput) - - assertWithMessage("proxiedInput should start with null").that(latest).isNull() - - inputProxy.onProxiedInput(ProxiedInputModel.OnTap) - assertThat(latest).isEqualTo(ProxiedInputModel.OnTap) - - inputProxy.onProxiedInput(ProxiedInputModel.OnDrag(0f, 100f)) - assertThat(latest).isEqualTo(ProxiedInputModel.OnDrag(0f, 100f)) - - inputProxy.onProxiedInput(ProxiedInputModel.OnDrag(0f, 120f)) - assertThat(latest).isEqualTo(ProxiedInputModel.OnDrag(0f, 120f)) - - inputProxy.onProxiedInput(ProxiedInputModel.OnDragEnd) - assertThat(latest).isEqualTo(ProxiedInputModel.OnDragEnd) - } - - @Test - fun shadeConfig_dualShadeEnabled() = runTest { - overrideResource(R.bool.dual_shade_enabled, true) - val underTest = create() - val shadeConfig: ShadeConfig? by collectLastValue(underTest.shadeConfig) - - assertThat(shadeConfig).isInstanceOf(ShadeConfig.DualShadeConfig::class.java) - } - - @Test - fun shadeConfig_dualShadeNotEnabled() = runTest { - overrideResource(R.bool.dual_shade_enabled, false) - val underTest = create() - val shadeConfig: ShadeConfig? by collectLastValue(underTest.shadeConfig) - - assertThat(shadeConfig).isInstanceOf(ShadeConfig.SingleShadeConfig::class.java) - } - - @Test - fun forceCollapseAll() = runTest { - val underTest = create() - val forceCollapseAll: Boolean? by collectLastValue(underTest.forceCollapseAll) - - assertWithMessage("forceCollapseAll should start as false!") - .that(forceCollapseAll) - .isFalse() - - underTest.setForceCollapseAll(true) - assertThat(forceCollapseAll).isTrue() - - underTest.setForceCollapseAll(false) - assertThat(forceCollapseAll).isFalse() - } - - @Test - fun shadeInteraction() = runTest { - val underTest = create() - val shadeInteraction: MultiShadeInteractionModel? by - collectLastValue(underTest.shadeInteraction) - - assertWithMessage("shadeInteraction should start as null!").that(shadeInteraction).isNull() - - underTest.setShadeInteraction( - MultiShadeInteractionModel(shadeId = ShadeId.LEFT, isProxied = false) - ) - assertThat(shadeInteraction) - .isEqualTo(MultiShadeInteractionModel(shadeId = ShadeId.LEFT, isProxied = false)) - - underTest.setShadeInteraction( - MultiShadeInteractionModel(shadeId = ShadeId.RIGHT, isProxied = true) - ) - assertThat(shadeInteraction) - .isEqualTo(MultiShadeInteractionModel(shadeId = ShadeId.RIGHT, isProxied = true)) - - underTest.setShadeInteraction(null) - assertThat(shadeInteraction).isNull() - } - - @Test - fun expansion() = runTest { - val underTest = create() - val leftExpansion: Float? by - collectLastValue(underTest.getShade(ShadeId.LEFT).map { it.expansion }) - val rightExpansion: Float? by - collectLastValue(underTest.getShade(ShadeId.RIGHT).map { it.expansion }) - val singleExpansion: Float? by - collectLastValue(underTest.getShade(ShadeId.SINGLE).map { it.expansion }) - - assertWithMessage("expansion should start as 0!").that(leftExpansion).isZero() - assertWithMessage("expansion should start as 0!").that(rightExpansion).isZero() - assertWithMessage("expansion should start as 0!").that(singleExpansion).isZero() - - underTest.setExpansion( - shadeId = ShadeId.LEFT, - 0.4f, - ) - assertThat(leftExpansion).isEqualTo(0.4f) - assertThat(rightExpansion).isEqualTo(0f) - assertThat(singleExpansion).isEqualTo(0f) - - underTest.setExpansion( - shadeId = ShadeId.RIGHT, - 0.73f, - ) - assertThat(leftExpansion).isEqualTo(0.4f) - assertThat(rightExpansion).isEqualTo(0.73f) - assertThat(singleExpansion).isEqualTo(0f) - - underTest.setExpansion( - shadeId = ShadeId.LEFT, - 0.1f, - ) - underTest.setExpansion( - shadeId = ShadeId.SINGLE, - 0.88f, - ) - assertThat(leftExpansion).isEqualTo(0.1f) - assertThat(rightExpansion).isEqualTo(0.73f) - assertThat(singleExpansion).isEqualTo(0.88f) - } - - private fun create(): MultiShadeRepository { - return create( - context = context, - inputProxy = inputProxy, - ) - } - - companion object { - fun create( - context: Context, - inputProxy: MultiShadeInputProxy, - ): MultiShadeRepository { - return MultiShadeRepository( - applicationContext = context, - inputProxy = inputProxy, - ) - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt deleted file mode 100644 index bcc99bc8dd0ca..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeInteractorTest.kt +++ /dev/null @@ -1,323 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.domain.interactor - -import android.content.Context -import androidx.test.filters.SmallTest -import com.android.systemui.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy -import com.android.systemui.multishade.data.repository.MultiShadeRepositoryTest -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.multishade.shared.model.ShadeId -import com.google.common.truth.Truth.assertThat -import com.google.common.truth.Truth.assertWithMessage -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -@OptIn(ExperimentalCoroutinesApi::class) -@SmallTest -@RunWith(JUnit4::class) -class MultiShadeInteractorTest : SysuiTestCase() { - - private lateinit var testScope: TestScope - private lateinit var inputProxy: MultiShadeInputProxy - - @Before - fun setUp() { - testScope = TestScope() - inputProxy = MultiShadeInputProxy() - } - - @Test - fun maxShadeExpansion() = - testScope.runTest { - val underTest = create() - val maxShadeExpansion: Float? by collectLastValue(underTest.maxShadeExpansion) - assertWithMessage("maxShadeExpansion must start with 0.0!") - .that(maxShadeExpansion) - .isEqualTo(0f) - - underTest.setExpansion(shadeId = ShadeId.LEFT, expansion = 0.441f) - assertThat(maxShadeExpansion).isEqualTo(0.441f) - - underTest.setExpansion(shadeId = ShadeId.RIGHT, expansion = 0.442f) - assertThat(maxShadeExpansion).isEqualTo(0.442f) - - underTest.setExpansion(shadeId = ShadeId.RIGHT, expansion = 0f) - assertThat(maxShadeExpansion).isEqualTo(0.441f) - - underTest.setExpansion(shadeId = ShadeId.LEFT, expansion = 0f) - assertThat(maxShadeExpansion).isEqualTo(0f) - } - - @Test - fun isAnyShadeExpanded() = - testScope.runTest { - val underTest = create() - val isAnyShadeExpanded: Boolean? by collectLastValue(underTest.isAnyShadeExpanded) - assertWithMessage("isAnyShadeExpanded must start with false!") - .that(isAnyShadeExpanded) - .isFalse() - - underTest.setExpansion(shadeId = ShadeId.LEFT, expansion = 0.441f) - assertThat(isAnyShadeExpanded).isTrue() - - underTest.setExpansion(shadeId = ShadeId.RIGHT, expansion = 0.442f) - assertThat(isAnyShadeExpanded).isTrue() - - underTest.setExpansion(shadeId = ShadeId.RIGHT, expansion = 0f) - assertThat(isAnyShadeExpanded).isTrue() - - underTest.setExpansion(shadeId = ShadeId.LEFT, expansion = 0f) - assertThat(isAnyShadeExpanded).isFalse() - } - - @Test - fun isVisible_dualShadeConfig() = - testScope.runTest { - overrideResource(R.bool.dual_shade_enabled, true) - val underTest = create() - val isLeftShadeVisible: Boolean? by collectLastValue(underTest.isVisible(ShadeId.LEFT)) - val isRightShadeVisible: Boolean? by - collectLastValue(underTest.isVisible(ShadeId.RIGHT)) - val isSingleShadeVisible: Boolean? by - collectLastValue(underTest.isVisible(ShadeId.SINGLE)) - - assertThat(isLeftShadeVisible).isTrue() - assertThat(isRightShadeVisible).isTrue() - assertThat(isSingleShadeVisible).isFalse() - } - - @Test - fun isVisible_singleShadeConfig() = - testScope.runTest { - overrideResource(R.bool.dual_shade_enabled, false) - val underTest = create() - val isLeftShadeVisible: Boolean? by collectLastValue(underTest.isVisible(ShadeId.LEFT)) - val isRightShadeVisible: Boolean? by - collectLastValue(underTest.isVisible(ShadeId.RIGHT)) - val isSingleShadeVisible: Boolean? by - collectLastValue(underTest.isVisible(ShadeId.SINGLE)) - - assertThat(isLeftShadeVisible).isFalse() - assertThat(isRightShadeVisible).isFalse() - assertThat(isSingleShadeVisible).isTrue() - } - - @Test - fun isNonProxiedInputAllowed() = - testScope.runTest { - val underTest = create() - val isLeftShadeNonProxiedInputAllowed: Boolean? by - collectLastValue(underTest.isNonProxiedInputAllowed(ShadeId.LEFT)) - assertWithMessage("isNonProxiedInputAllowed should start as true!") - .that(isLeftShadeNonProxiedInputAllowed) - .isTrue() - - // Need to collect proxied input so the flows become hot as the gesture cancelation code - // logic sits in side the proxiedInput flow for each shade. - collectLastValue(underTest.proxiedInput(ShadeId.LEFT)) - collectLastValue(underTest.proxiedInput(ShadeId.RIGHT)) - - // Starting a proxied interaction on the LEFT shade disallows non-proxied interaction on - // the - // same shade. - inputProxy.onProxiedInput( - ProxiedInputModel.OnDrag(xFraction = 0f, yDragAmountPx = 123f) - ) - assertThat(isLeftShadeNonProxiedInputAllowed).isFalse() - - // Registering the end of the proxied interaction re-allows it. - inputProxy.onProxiedInput(ProxiedInputModel.OnDragEnd) - assertThat(isLeftShadeNonProxiedInputAllowed).isTrue() - - // Starting a proxied interaction on the RIGHT shade force-collapses the LEFT shade, - // disallowing non-proxied input on the LEFT shade. - inputProxy.onProxiedInput( - ProxiedInputModel.OnDrag(xFraction = 1f, yDragAmountPx = 123f) - ) - assertThat(isLeftShadeNonProxiedInputAllowed).isFalse() - - // Registering the end of the interaction on the RIGHT shade re-allows it. - inputProxy.onProxiedInput(ProxiedInputModel.OnDragEnd) - assertThat(isLeftShadeNonProxiedInputAllowed).isTrue() - } - - @Test - fun isForceCollapsed_whenOtherShadeInteractionUnderway() = - testScope.runTest { - val underTest = create() - val isLeftShadeForceCollapsed: Boolean? by - collectLastValue(underTest.isForceCollapsed(ShadeId.LEFT)) - val isRightShadeForceCollapsed: Boolean? by - collectLastValue(underTest.isForceCollapsed(ShadeId.RIGHT)) - val isSingleShadeForceCollapsed: Boolean? by - collectLastValue(underTest.isForceCollapsed(ShadeId.SINGLE)) - - assertWithMessage("isForceCollapsed should start as false!") - .that(isLeftShadeForceCollapsed) - .isFalse() - assertWithMessage("isForceCollapsed should start as false!") - .that(isRightShadeForceCollapsed) - .isFalse() - assertWithMessage("isForceCollapsed should start as false!") - .that(isSingleShadeForceCollapsed) - .isFalse() - - // Registering the start of an interaction on the RIGHT shade force-collapses the LEFT - // shade. - underTest.onUserInteractionStarted(ShadeId.RIGHT) - assertThat(isLeftShadeForceCollapsed).isTrue() - assertThat(isRightShadeForceCollapsed).isFalse() - assertThat(isSingleShadeForceCollapsed).isFalse() - - // Registering the end of the interaction on the RIGHT shade re-allows it. - underTest.onUserInteractionEnded(ShadeId.RIGHT) - assertThat(isLeftShadeForceCollapsed).isFalse() - assertThat(isRightShadeForceCollapsed).isFalse() - assertThat(isSingleShadeForceCollapsed).isFalse() - - // Registering the start of an interaction on the LEFT shade force-collapses the RIGHT - // shade. - underTest.onUserInteractionStarted(ShadeId.LEFT) - assertThat(isLeftShadeForceCollapsed).isFalse() - assertThat(isRightShadeForceCollapsed).isTrue() - assertThat(isSingleShadeForceCollapsed).isFalse() - - // Registering the end of the interaction on the LEFT shade re-allows it. - underTest.onUserInteractionEnded(ShadeId.LEFT) - assertThat(isLeftShadeForceCollapsed).isFalse() - assertThat(isRightShadeForceCollapsed).isFalse() - assertThat(isSingleShadeForceCollapsed).isFalse() - } - - @Test - fun collapseAll() = - testScope.runTest { - val underTest = create() - val isLeftShadeForceCollapsed: Boolean? by - collectLastValue(underTest.isForceCollapsed(ShadeId.LEFT)) - val isRightShadeForceCollapsed: Boolean? by - collectLastValue(underTest.isForceCollapsed(ShadeId.RIGHT)) - val isSingleShadeForceCollapsed: Boolean? by - collectLastValue(underTest.isForceCollapsed(ShadeId.SINGLE)) - - assertWithMessage("isForceCollapsed should start as false!") - .that(isLeftShadeForceCollapsed) - .isFalse() - assertWithMessage("isForceCollapsed should start as false!") - .that(isRightShadeForceCollapsed) - .isFalse() - assertWithMessage("isForceCollapsed should start as false!") - .that(isSingleShadeForceCollapsed) - .isFalse() - - underTest.collapseAll() - assertThat(isLeftShadeForceCollapsed).isTrue() - assertThat(isRightShadeForceCollapsed).isTrue() - assertThat(isSingleShadeForceCollapsed).isTrue() - - // Receiving proxied input on that's not a tap gesture, on the left-hand side resets the - // "collapse all". Note that now the RIGHT shade is force-collapsed because we're - // interacting with the LEFT shade. - inputProxy.onProxiedInput(ProxiedInputModel.OnDrag(0f, 0f)) - assertThat(isLeftShadeForceCollapsed).isFalse() - assertThat(isRightShadeForceCollapsed).isTrue() - assertThat(isSingleShadeForceCollapsed).isFalse() - } - - @Test - fun onTapOutside_collapsesAll() = - testScope.runTest { - val underTest = create() - val isLeftShadeForceCollapsed: Boolean? by - collectLastValue(underTest.isForceCollapsed(ShadeId.LEFT)) - val isRightShadeForceCollapsed: Boolean? by - collectLastValue(underTest.isForceCollapsed(ShadeId.RIGHT)) - val isSingleShadeForceCollapsed: Boolean? by - collectLastValue(underTest.isForceCollapsed(ShadeId.SINGLE)) - - assertWithMessage("isForceCollapsed should start as false!") - .that(isLeftShadeForceCollapsed) - .isFalse() - assertWithMessage("isForceCollapsed should start as false!") - .that(isRightShadeForceCollapsed) - .isFalse() - assertWithMessage("isForceCollapsed should start as false!") - .that(isSingleShadeForceCollapsed) - .isFalse() - - inputProxy.onProxiedInput(ProxiedInputModel.OnTap) - assertThat(isLeftShadeForceCollapsed).isTrue() - assertThat(isRightShadeForceCollapsed).isTrue() - assertThat(isSingleShadeForceCollapsed).isTrue() - } - - @Test - fun proxiedInput_ignoredWhileNonProxiedGestureUnderway() = - testScope.runTest { - val underTest = create() - val proxiedInput: ProxiedInputModel? by - collectLastValue(underTest.proxiedInput(ShadeId.RIGHT)) - underTest.onUserInteractionStarted(shadeId = ShadeId.RIGHT) - - inputProxy.onProxiedInput(ProxiedInputModel.OnDrag(0.9f, 100f)) - assertThat(proxiedInput).isNull() - - inputProxy.onProxiedInput(ProxiedInputModel.OnDrag(0.8f, 110f)) - assertThat(proxiedInput).isNull() - - underTest.onUserInteractionEnded(shadeId = ShadeId.RIGHT) - - inputProxy.onProxiedInput(ProxiedInputModel.OnDrag(0.9f, 100f)) - assertThat(proxiedInput).isNotNull() - } - - private fun create(): MultiShadeInteractor { - return create( - testScope = testScope, - context = context, - inputProxy = inputProxy, - ) - } - - companion object { - fun create( - testScope: TestScope, - context: Context, - inputProxy: MultiShadeInputProxy, - ): MultiShadeInteractor { - return MultiShadeInteractor( - applicationScope = testScope.backgroundScope, - repository = - MultiShadeRepositoryTest.create( - context = context, - inputProxy = inputProxy, - ), - inputProxy = inputProxy, - ) - } - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt deleted file mode 100644 index 5890cbd06476a..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/multishade/domain/interactor/MultiShadeMotionEventInteractorTest.kt +++ /dev/null @@ -1,530 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.domain.interactor - -import android.view.MotionEvent -import android.view.ViewConfiguration -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.android.systemui.classifier.FalsingManagerFake -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.flags.FakeFeatureFlags -import com.android.systemui.flags.Flags -import com.android.systemui.keyguard.data.repository.FakeKeyguardTransitionRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory -import com.android.systemui.keyguard.shared.model.KeyguardState -import com.android.systemui.keyguard.shared.model.TransitionState -import com.android.systemui.keyguard.shared.model.TransitionStep -import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy -import com.android.systemui.multishade.data.repository.MultiShadeRepository -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.multishade.shared.model.ShadeId -import com.android.systemui.shade.ShadeController -import com.android.systemui.util.mockito.whenever -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.currentTime -import kotlinx.coroutines.test.runCurrent -import kotlinx.coroutines.test.runTest -import org.junit.After -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 -import org.mockito.Mock -import org.mockito.Mockito.anyBoolean -import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations - -@OptIn(ExperimentalCoroutinesApi::class) -@SmallTest -@RunWith(JUnit4::class) -class MultiShadeMotionEventInteractorTest : SysuiTestCase() { - - private lateinit var underTest: MultiShadeMotionEventInteractor - - private lateinit var testScope: TestScope - private lateinit var motionEvents: MutableSet - private lateinit var repository: MultiShadeRepository - private lateinit var interactor: MultiShadeInteractor - private val touchSlop: Int = ViewConfiguration.get(context).scaledTouchSlop - private lateinit var keyguardTransitionRepository: FakeKeyguardTransitionRepository - private lateinit var falsingManager: FalsingManagerFake - @Mock private lateinit var shadeController: ShadeController - - @Before - fun setUp() { - MockitoAnnotations.initMocks(this) - testScope = TestScope() - motionEvents = mutableSetOf() - - val inputProxy = MultiShadeInputProxy() - repository = - MultiShadeRepository( - applicationContext = context, - inputProxy = inputProxy, - ) - interactor = - MultiShadeInteractor( - applicationScope = testScope.backgroundScope, - repository = repository, - inputProxy = inputProxy, - ) - val featureFlags = FakeFeatureFlags() - featureFlags.set(Flags.DUAL_SHADE, true) - keyguardTransitionRepository = FakeKeyguardTransitionRepository() - falsingManager = FalsingManagerFake() - - underTest = - MultiShadeMotionEventInteractor( - applicationContext = context, - applicationScope = testScope.backgroundScope, - multiShadeInteractor = interactor, - featureFlags = featureFlags, - keyguardTransitionInteractor = - KeyguardTransitionInteractorFactory.create( - scope = TestScope().backgroundScope, - repository = keyguardTransitionRepository, - ) - .keyguardTransitionInteractor, - falsingManager = falsingManager, - shadeController = shadeController, - ) - } - - @After - fun tearDown() { - motionEvents.forEach { motionEvent -> motionEvent.recycle() } - } - - @Test - fun listenForIsAnyShadeExpanded_expanded_makesWindowViewVisible() = - testScope.runTest { - whenever(shadeController.isKeyguard).thenReturn(false) - repository.setExpansion(ShadeId.LEFT, 0.1f) - val expanded by collectLastValue(interactor.isAnyShadeExpanded) - assertThat(expanded).isTrue() - - verify(shadeController).makeExpandedVisible(anyBoolean()) - } - - @Test - fun listenForIsAnyShadeExpanded_collapsed_makesWindowViewInvisible() = - testScope.runTest { - whenever(shadeController.isKeyguard).thenReturn(false) - repository.setForceCollapseAll(true) - val expanded by collectLastValue(interactor.isAnyShadeExpanded) - assertThat(expanded).isFalse() - - verify(shadeController).makeExpandedInvisible() - } - - @Test - fun listenForIsAnyShadeExpanded_collapsedOnKeyguard_makesWindowViewVisible() = - testScope.runTest { - whenever(shadeController.isKeyguard).thenReturn(true) - repository.setForceCollapseAll(true) - val expanded by collectLastValue(interactor.isAnyShadeExpanded) - assertThat(expanded).isFalse() - - verify(shadeController).makeExpandedVisible(anyBoolean()) - } - - @Test - fun shouldIntercept_initialDown_returnsFalse() = - testScope.runTest { - assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN))).isFalse() - } - - @Test - fun shouldIntercept_moveBelowTouchSlop_returnsFalse() = - testScope.runTest { - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - - assertThat( - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_MOVE, - y = touchSlop - 1f, - ) - ) - ) - .isFalse() - } - - @Test - fun shouldIntercept_moveAboveTouchSlop_returnsTrue() = - testScope.runTest { - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - - assertThat( - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_MOVE, - y = touchSlop + 1f, - ) - ) - ) - .isTrue() - } - - @Test - fun shouldIntercept_moveAboveTouchSlop_butHorizontalFirst_returnsFalse() = - testScope.runTest { - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - - assertThat( - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_MOVE, - x = touchSlop + 1f, - ) - ) - ) - .isFalse() - assertThat( - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_MOVE, - y = touchSlop + 1f, - ) - ) - ) - .isFalse() - } - - @Test - fun shouldIntercept_up_afterMovedAboveTouchSlop_returnsTrue() = - testScope.runTest { - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_MOVE, y = touchSlop + 1f)) - - assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))).isTrue() - } - - @Test - fun shouldIntercept_cancel_afterMovedAboveTouchSlop_returnsTrue() = - testScope.runTest { - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_MOVE, y = touchSlop + 1f)) - - assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_CANCEL))).isTrue() - } - - @Test - fun shouldIntercept_moveAboveTouchSlopAndUp_butShadeExpanded_returnsFalse() = - testScope.runTest { - repository.setExpansion(ShadeId.LEFT, 0.1f) - runCurrent() - - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - - assertThat( - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_MOVE, - y = touchSlop + 1f, - ) - ) - ) - .isFalse() - assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))).isFalse() - } - - @Test - fun shouldIntercept_moveAboveTouchSlopAndCancel_butShadeExpanded_returnsFalse() = - testScope.runTest { - repository.setExpansion(ShadeId.LEFT, 0.1f) - runCurrent() - - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - - assertThat( - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_MOVE, - y = touchSlop + 1f, - ) - ) - ) - .isFalse() - assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_CANCEL))).isFalse() - } - - @Test - fun shouldIntercept_moveAboveTouchSlopAndUp_butBouncerShowing_returnsFalse() = - testScope.runTest { - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.PRIMARY_BOUNCER, - value = 0.1f, - transitionState = TransitionState.STARTED, - ) - ) - runCurrent() - - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - - assertThat( - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_MOVE, - y = touchSlop + 1f, - ) - ) - ) - .isFalse() - assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP))).isFalse() - } - - @Test - fun shouldIntercept_moveAboveTouchSlopAndCancel_butBouncerShowing_returnsFalse() = - testScope.runTest { - keyguardTransitionRepository.sendTransitionStep( - TransitionStep( - from = KeyguardState.LOCKSCREEN, - to = KeyguardState.PRIMARY_BOUNCER, - value = 0.1f, - transitionState = TransitionState.STARTED, - ) - ) - runCurrent() - - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - - assertThat( - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_MOVE, - y = touchSlop + 1f, - ) - ) - ) - .isFalse() - assertThat(underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_CANCEL))).isFalse() - } - - @Test - fun tap_doesNotSendProxiedInput() = - testScope.runTest { - val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT)) - val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT)) - val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE)) - - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP)) - - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - } - - @Test - fun dragBelowTouchSlop_doesNotSendProxiedInput() = - testScope.runTest { - val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT)) - val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT)) - val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE)) - - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_DOWN)) - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_MOVE, y = touchSlop - 1f)) - underTest.shouldIntercept(motionEvent(MotionEvent.ACTION_UP)) - - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - } - - @Test - fun dragShadeAboveTouchSlopAndUp() = - testScope.runTest { - val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT)) - val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT)) - val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE)) - - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_DOWN, - x = 100f, // left shade - ) - ) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - - val yDragAmountPx = touchSlop + 1f - val moveEvent = - motionEvent( - MotionEvent.ACTION_MOVE, - x = 100f, // left shade - y = yDragAmountPx, - ) - assertThat(underTest.shouldIntercept(moveEvent)).isTrue() - underTest.onTouchEvent(moveEvent, viewWidthPx = 1000) - assertThat(leftShadeProxiedInput) - .isEqualTo( - ProxiedInputModel.OnDrag( - xFraction = 0.1f, - yDragAmountPx = yDragAmountPx, - ) - ) - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - - val upEvent = motionEvent(MotionEvent.ACTION_UP) - assertThat(underTest.shouldIntercept(upEvent)).isTrue() - underTest.onTouchEvent(upEvent, viewWidthPx = 1000) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - } - - @Test - fun dragShadeAboveTouchSlopAndCancel() = - testScope.runTest { - val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT)) - val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT)) - val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE)) - - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_DOWN, - x = 900f, // right shade - ) - ) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - - val yDragAmountPx = touchSlop + 1f - val moveEvent = - motionEvent( - MotionEvent.ACTION_MOVE, - x = 900f, // right shade - y = yDragAmountPx, - ) - assertThat(underTest.shouldIntercept(moveEvent)).isTrue() - underTest.onTouchEvent(moveEvent, viewWidthPx = 1000) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput) - .isEqualTo( - ProxiedInputModel.OnDrag( - xFraction = 0.9f, - yDragAmountPx = yDragAmountPx, - ) - ) - assertThat(singleShadeProxiedInput).isNull() - - val cancelEvent = motionEvent(MotionEvent.ACTION_CANCEL) - assertThat(underTest.shouldIntercept(cancelEvent)).isTrue() - underTest.onTouchEvent(cancelEvent, viewWidthPx = 1000) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - } - - @Test - fun dragUp_withUp_doesNotShowShade() = - testScope.runTest { - val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT)) - val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT)) - val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE)) - - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_DOWN, - x = 100f, // left shade - ) - ) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - - val yDragAmountPx = -(touchSlop + 1f) // dragging up - val moveEvent = - motionEvent( - MotionEvent.ACTION_MOVE, - x = 100f, // left shade - y = yDragAmountPx, - ) - assertThat(underTest.shouldIntercept(moveEvent)).isFalse() - underTest.onTouchEvent(moveEvent, viewWidthPx = 1000) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - - val upEvent = motionEvent(MotionEvent.ACTION_UP) - assertThat(underTest.shouldIntercept(upEvent)).isFalse() - underTest.onTouchEvent(upEvent, viewWidthPx = 1000) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - } - - @Test - fun dragUp_withCancel_falseTouch_showsThenHidesBouncer() = - testScope.runTest { - val leftShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.LEFT)) - val rightShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.RIGHT)) - val singleShadeProxiedInput by collectLastValue(interactor.proxiedInput(ShadeId.SINGLE)) - - underTest.shouldIntercept( - motionEvent( - MotionEvent.ACTION_DOWN, - x = 900f, // right shade - ) - ) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - - val yDragAmountPx = -(touchSlop + 1f) // drag up - val moveEvent = - motionEvent( - MotionEvent.ACTION_MOVE, - x = 900f, // right shade - y = yDragAmountPx, - ) - assertThat(underTest.shouldIntercept(moveEvent)).isFalse() - underTest.onTouchEvent(moveEvent, viewWidthPx = 1000) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - - falsingManager.setIsFalseTouch(true) - val cancelEvent = motionEvent(MotionEvent.ACTION_CANCEL) - assertThat(underTest.shouldIntercept(cancelEvent)).isFalse() - underTest.onTouchEvent(cancelEvent, viewWidthPx = 1000) - assertThat(leftShadeProxiedInput).isNull() - assertThat(rightShadeProxiedInput).isNull() - assertThat(singleShadeProxiedInput).isNull() - } - - private fun TestScope.motionEvent( - action: Int, - downTime: Long = currentTime, - eventTime: Long = currentTime, - x: Float = 0f, - y: Float = 0f, - ): MotionEvent { - val motionEvent = MotionEvent.obtain(downTime, eventTime, action, x, y, 0) - motionEvents.add(motionEvent) - return motionEvent - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/shared/math/MathTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/shared/math/MathTest.kt deleted file mode 100644 index 8935309829267..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/multishade/shared/math/MathTest.kt +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.shared.math - -import androidx.test.filters.SmallTest -import com.android.systemui.SysuiTestCase -import com.google.common.truth.Truth.assertThat -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -@SmallTest -@RunWith(JUnit4::class) -class MathTest : SysuiTestCase() { - - @Test - fun isZero_zero_true() { - assertThat(0f.isZero(epsilon = EPSILON)).isTrue() - } - - @Test - fun isZero_belowPositiveEpsilon_true() { - assertThat((EPSILON * 0.999999f).isZero(epsilon = EPSILON)).isTrue() - } - - @Test - fun isZero_aboveNegativeEpsilon_true() { - assertThat((EPSILON * -0.999999f).isZero(epsilon = EPSILON)).isTrue() - } - - @Test - fun isZero_positiveEpsilon_false() { - assertThat(EPSILON.isZero(epsilon = EPSILON)).isFalse() - } - - @Test - fun isZero_negativeEpsilon_false() { - assertThat((-EPSILON).isZero(epsilon = EPSILON)).isFalse() - } - - @Test - fun isZero_positive_false() { - assertThat(1f.isZero(epsilon = EPSILON)).isFalse() - } - - @Test - fun isZero_negative_false() { - assertThat((-1f).isZero(epsilon = EPSILON)).isFalse() - } - - companion object { - private const val EPSILON = 0.0001f - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModelTest.kt deleted file mode 100644 index 0484515e38bd1..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/multishade/ui/viewmodel/MultiShadeViewModelTest.kt +++ /dev/null @@ -1,127 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.ui.viewmodel - -import androidx.test.filters.SmallTest -import com.android.systemui.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractorTest -import com.google.common.truth.Truth.assertThat -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -@OptIn(ExperimentalCoroutinesApi::class) -@SmallTest -@RunWith(JUnit4::class) -class MultiShadeViewModelTest : SysuiTestCase() { - - private lateinit var testScope: TestScope - private lateinit var inputProxy: MultiShadeInputProxy - - @Before - fun setUp() { - testScope = TestScope() - inputProxy = MultiShadeInputProxy() - } - - @Test - fun scrim_whenDualShadeCollapsed() = - testScope.runTest { - val alpha = 0.5f - overrideResource(R.dimen.dual_shade_scrim_alpha, alpha) - overrideResource(R.bool.dual_shade_enabled, true) - - val underTest = create() - val scrimAlpha: Float? by collectLastValue(underTest.scrimAlpha) - val isScrimEnabled: Boolean? by collectLastValue(underTest.isScrimEnabled) - - assertThat(scrimAlpha).isZero() - assertThat(isScrimEnabled).isFalse() - } - - @Test - fun scrim_whenDualShadeExpanded() = - testScope.runTest { - val alpha = 0.5f - overrideResource(R.dimen.dual_shade_scrim_alpha, alpha) - overrideResource(R.bool.dual_shade_enabled, true) - val underTest = create() - val scrimAlpha: Float? by collectLastValue(underTest.scrimAlpha) - val isScrimEnabled: Boolean? by collectLastValue(underTest.isScrimEnabled) - assertThat(scrimAlpha).isZero() - assertThat(isScrimEnabled).isFalse() - - underTest.leftShade.onExpansionChanged(0.5f) - assertThat(scrimAlpha).isEqualTo(alpha * 0.5f) - assertThat(isScrimEnabled).isTrue() - - underTest.rightShade.onExpansionChanged(1f) - assertThat(scrimAlpha).isEqualTo(alpha * 1f) - assertThat(isScrimEnabled).isTrue() - } - - @Test - fun scrim_whenSingleShadeCollapsed() = - testScope.runTest { - val alpha = 0.5f - overrideResource(R.dimen.dual_shade_scrim_alpha, alpha) - overrideResource(R.bool.dual_shade_enabled, false) - - val underTest = create() - val scrimAlpha: Float? by collectLastValue(underTest.scrimAlpha) - val isScrimEnabled: Boolean? by collectLastValue(underTest.isScrimEnabled) - - assertThat(scrimAlpha).isZero() - assertThat(isScrimEnabled).isFalse() - } - - @Test - fun scrim_whenSingleShadeExpanded() = - testScope.runTest { - val alpha = 0.5f - overrideResource(R.dimen.dual_shade_scrim_alpha, alpha) - overrideResource(R.bool.dual_shade_enabled, false) - val underTest = create() - val scrimAlpha: Float? by collectLastValue(underTest.scrimAlpha) - val isScrimEnabled: Boolean? by collectLastValue(underTest.isScrimEnabled) - - underTest.singleShade.onExpansionChanged(0.95f) - - assertThat(scrimAlpha).isZero() - assertThat(isScrimEnabled).isFalse() - } - - private fun create(): MultiShadeViewModel { - return MultiShadeViewModel( - viewModelScope = testScope.backgroundScope, - interactor = - MultiShadeInteractorTest.create( - testScope = testScope, - context = context, - inputProxy = inputProxy, - ), - ) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/multishade/ui/viewmodel/ShadeViewModelTest.kt b/packages/SystemUI/tests/src/com/android/systemui/multishade/ui/viewmodel/ShadeViewModelTest.kt deleted file mode 100644 index e32aac596e5bd..0000000000000 --- a/packages/SystemUI/tests/src/com/android/systemui/multishade/ui/viewmodel/ShadeViewModelTest.kt +++ /dev/null @@ -1,226 +0,0 @@ -/* - * Copyright (C) 2023 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.multishade.ui.viewmodel - -import androidx.test.filters.SmallTest -import com.android.systemui.R -import com.android.systemui.SysuiTestCase -import com.android.systemui.coroutines.collectLastValue -import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractorTest -import com.android.systemui.multishade.shared.model.ProxiedInputModel -import com.android.systemui.multishade.shared.model.ShadeId -import com.google.common.truth.Truth.assertThat -import com.google.common.truth.Truth.assertWithMessage -import kotlinx.coroutines.ExperimentalCoroutinesApi -import kotlinx.coroutines.test.TestScope -import kotlinx.coroutines.test.runTest -import org.junit.Before -import org.junit.Test -import org.junit.runner.RunWith -import org.junit.runners.JUnit4 - -@OptIn(ExperimentalCoroutinesApi::class) -@SmallTest -@RunWith(JUnit4::class) -class ShadeViewModelTest : SysuiTestCase() { - - private lateinit var testScope: TestScope - private lateinit var inputProxy: MultiShadeInputProxy - private var interactor: MultiShadeInteractor? = null - - @Before - fun setUp() { - testScope = TestScope() - inputProxy = MultiShadeInputProxy() - } - - @Test - fun isVisible_dualShadeConfig() = - testScope.runTest { - overrideResource(R.bool.dual_shade_enabled, true) - val isLeftShadeVisible: Boolean? by collectLastValue(create(ShadeId.LEFT).isVisible) - val isRightShadeVisible: Boolean? by collectLastValue(create(ShadeId.RIGHT).isVisible) - val isSingleShadeVisible: Boolean? by collectLastValue(create(ShadeId.SINGLE).isVisible) - - assertThat(isLeftShadeVisible).isTrue() - assertThat(isRightShadeVisible).isTrue() - assertThat(isSingleShadeVisible).isFalse() - } - - @Test - fun isVisible_singleShadeConfig() = - testScope.runTest { - overrideResource(R.bool.dual_shade_enabled, false) - val isLeftShadeVisible: Boolean? by collectLastValue(create(ShadeId.LEFT).isVisible) - val isRightShadeVisible: Boolean? by collectLastValue(create(ShadeId.RIGHT).isVisible) - val isSingleShadeVisible: Boolean? by collectLastValue(create(ShadeId.SINGLE).isVisible) - - assertThat(isLeftShadeVisible).isFalse() - assertThat(isRightShadeVisible).isFalse() - assertThat(isSingleShadeVisible).isTrue() - } - - @Test - fun isSwipingEnabled() = - testScope.runTest { - val underTest = create(ShadeId.LEFT) - val isSwipingEnabled: Boolean? by collectLastValue(underTest.isSwipingEnabled) - assertWithMessage("isSwipingEnabled should start as true!") - .that(isSwipingEnabled) - .isTrue() - - // Need to collect proxied input so the flows become hot as the gesture cancelation code - // logic sits in side the proxiedInput flow for each shade. - collectLastValue(underTest.proxiedInput) - collectLastValue(create(ShadeId.RIGHT).proxiedInput) - - // Starting a proxied interaction on the LEFT shade disallows non-proxied interaction on - // the - // same shade. - inputProxy.onProxiedInput( - ProxiedInputModel.OnDrag(xFraction = 0f, yDragAmountPx = 123f) - ) - assertThat(isSwipingEnabled).isFalse() - - // Registering the end of the proxied interaction re-allows it. - inputProxy.onProxiedInput(ProxiedInputModel.OnDragEnd) - assertThat(isSwipingEnabled).isTrue() - - // Starting a proxied interaction on the RIGHT shade force-collapses the LEFT shade, - // disallowing non-proxied input on the LEFT shade. - inputProxy.onProxiedInput( - ProxiedInputModel.OnDrag(xFraction = 1f, yDragAmountPx = 123f) - ) - assertThat(isSwipingEnabled).isFalse() - - // Registering the end of the interaction on the RIGHT shade re-allows it. - inputProxy.onProxiedInput(ProxiedInputModel.OnDragEnd) - assertThat(isSwipingEnabled).isTrue() - } - - @Test - fun isForceCollapsed_whenOtherShadeInteractionUnderway() = - testScope.runTest { - val leftShade = create(ShadeId.LEFT) - val rightShade = create(ShadeId.RIGHT) - val isLeftShadeForceCollapsed: Boolean? by collectLastValue(leftShade.isForceCollapsed) - val isRightShadeForceCollapsed: Boolean? by - collectLastValue(rightShade.isForceCollapsed) - val isSingleShadeForceCollapsed: Boolean? by - collectLastValue(create(ShadeId.SINGLE).isForceCollapsed) - - assertWithMessage("isForceCollapsed should start as false!") - .that(isLeftShadeForceCollapsed) - .isFalse() - assertWithMessage("isForceCollapsed should start as false!") - .that(isRightShadeForceCollapsed) - .isFalse() - assertWithMessage("isForceCollapsed should start as false!") - .that(isSingleShadeForceCollapsed) - .isFalse() - - // Registering the start of an interaction on the RIGHT shade force-collapses the LEFT - // shade. - rightShade.onDragStarted() - assertThat(isLeftShadeForceCollapsed).isTrue() - assertThat(isRightShadeForceCollapsed).isFalse() - assertThat(isSingleShadeForceCollapsed).isFalse() - - // Registering the end of the interaction on the RIGHT shade re-allows it. - rightShade.onDragEnded() - assertThat(isLeftShadeForceCollapsed).isFalse() - assertThat(isRightShadeForceCollapsed).isFalse() - assertThat(isSingleShadeForceCollapsed).isFalse() - - // Registering the start of an interaction on the LEFT shade force-collapses the RIGHT - // shade. - leftShade.onDragStarted() - assertThat(isLeftShadeForceCollapsed).isFalse() - assertThat(isRightShadeForceCollapsed).isTrue() - assertThat(isSingleShadeForceCollapsed).isFalse() - - // Registering the end of the interaction on the LEFT shade re-allows it. - leftShade.onDragEnded() - assertThat(isLeftShadeForceCollapsed).isFalse() - assertThat(isRightShadeForceCollapsed).isFalse() - assertThat(isSingleShadeForceCollapsed).isFalse() - } - - @Test - fun onTapOutside_collapsesAll() = - testScope.runTest { - val isLeftShadeForceCollapsed: Boolean? by - collectLastValue(create(ShadeId.LEFT).isForceCollapsed) - val isRightShadeForceCollapsed: Boolean? by - collectLastValue(create(ShadeId.RIGHT).isForceCollapsed) - val isSingleShadeForceCollapsed: Boolean? by - collectLastValue(create(ShadeId.SINGLE).isForceCollapsed) - - assertWithMessage("isForceCollapsed should start as false!") - .that(isLeftShadeForceCollapsed) - .isFalse() - assertWithMessage("isForceCollapsed should start as false!") - .that(isRightShadeForceCollapsed) - .isFalse() - assertWithMessage("isForceCollapsed should start as false!") - .that(isSingleShadeForceCollapsed) - .isFalse() - - inputProxy.onProxiedInput(ProxiedInputModel.OnTap) - assertThat(isLeftShadeForceCollapsed).isTrue() - assertThat(isRightShadeForceCollapsed).isTrue() - assertThat(isSingleShadeForceCollapsed).isTrue() - } - - @Test - fun proxiedInput_ignoredWhileNonProxiedGestureUnderway() = - testScope.runTest { - val underTest = create(ShadeId.RIGHT) - val proxiedInput: ProxiedInputModel? by collectLastValue(underTest.proxiedInput) - underTest.onDragStarted() - - inputProxy.onProxiedInput(ProxiedInputModel.OnDrag(0.9f, 100f)) - assertThat(proxiedInput).isNull() - - inputProxy.onProxiedInput(ProxiedInputModel.OnDrag(0.8f, 110f)) - assertThat(proxiedInput).isNull() - - underTest.onDragEnded() - - inputProxy.onProxiedInput(ProxiedInputModel.OnDrag(0.9f, 100f)) - assertThat(proxiedInput).isNotNull() - } - - private fun create( - shadeId: ShadeId, - ): ShadeViewModel { - return ShadeViewModel( - viewModelScope = testScope.backgroundScope, - shadeId = shadeId, - interactor = interactor - ?: MultiShadeInteractorTest.create( - testScope = testScope, - context = context, - inputProxy = inputProxy, - ) - .also { interactor = it }, - ) - } -} diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java index ecd3308d48d97..202c7bf0d5fdf 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationPanelViewControllerBaseTest.java @@ -108,7 +108,6 @@ import com.android.systemui.media.controls.pipeline.MediaDataManager; import com.android.systemui.media.controls.ui.KeyguardMediaController; import com.android.systemui.media.controls.ui.MediaHierarchyManager; import com.android.systemui.model.SysUiState; -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor; import com.android.systemui.navigationbar.NavigationBarController; import com.android.systemui.navigationbar.NavigationModeController; import com.android.systemui.plugins.ActivityStarter; @@ -299,7 +298,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { @Mock protected GoneToDreamingTransitionViewModel mGoneToDreamingTransitionViewModel; @Mock protected KeyguardTransitionInteractor mKeyguardTransitionInteractor; - @Mock protected MultiShadeInteractor mMultiShadeInteractor; @Mock protected KeyguardLongPressViewModel mKeyuardLongPressViewModel; @Mock protected AlternateBouncerInteractor mAlternateBouncerInteractor; @Mock protected MotionEvent mDownMotionEvent; @@ -615,7 +613,6 @@ public class NotificationPanelViewControllerBaseTest extends SysuiTestCase { mLockscreenToOccludedTransitionViewModel, mMainDispatcher, mKeyguardTransitionInteractor, - () -> mMultiShadeInteractor, mDumpManager, mKeyuardLongPressViewModel, mKeyguardInteractor, diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt index 2a398c55560c7..893123d57c990 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewControllerTest.kt @@ -34,21 +34,15 @@ import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.classifier.FalsingCollectorFake -import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.dock.DockManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.keyguard.shared.model.TransitionStep import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel import com.android.systemui.log.BouncerLogger -import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy -import com.android.systemui.multishade.data.repository.MultiShadeRepository -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor -import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.LockscreenShadeTransitionController @@ -67,6 +61,7 @@ import com.android.systemui.user.data.repository.FakeUserRepository import com.android.systemui.util.mockito.any import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat +import java.util.Optional import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.TestScope @@ -80,9 +75,8 @@ import org.mockito.Mockito.anyFloat import org.mockito.Mockito.mock import org.mockito.Mockito.never import org.mockito.Mockito.verify -import org.mockito.MockitoAnnotations -import java.util.Optional import org.mockito.Mockito.`when` as whenever +import org.mockito.MockitoAnnotations @OptIn(ExperimentalCoroutinesApi::class) @SmallTest @@ -117,8 +111,9 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { @Mock lateinit var keyguardBouncerComponentFactory: KeyguardBouncerComponent.Factory @Mock lateinit var keyguardBouncerComponent: KeyguardBouncerComponent @Mock lateinit var keyguardSecurityContainerController: KeyguardSecurityContainerController - @Mock private lateinit var unfoldTransitionProgressProvider: - Optional + @Mock + private lateinit var unfoldTransitionProgressProvider: + Optional @Mock lateinit var keyguardTransitionInteractor: KeyguardTransitionInteractor @Mock lateinit var primaryBouncerToGoneTransitionViewModel: PrimaryBouncerToGoneTransitionViewModel @@ -146,23 +141,11 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { val featureFlags = FakeFeatureFlags() featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true) featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false) - featureFlags.set(Flags.DUAL_SHADE, false) featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) - val inputProxy = MultiShadeInputProxy() testScope = TestScope() - val multiShadeInteractor = - MultiShadeInteractor( - applicationScope = testScope.backgroundScope, - repository = - MultiShadeRepository( - applicationContext = context, - inputProxy = inputProxy, - ), - inputProxy = inputProxy, - ) underTest = NotificationShadeWindowViewController( lockscreenShadeTransitionController, @@ -193,25 +176,14 @@ class NotificationShadeWindowViewControllerTest : SysuiTestCase() { keyguardTransitionInteractor, primaryBouncerToGoneTransitionViewModel, featureFlags, - { multiShadeInteractor }, FakeSystemClock(), - { - MultiShadeMotionEventInteractor( - applicationContext = context, - applicationScope = testScope.backgroundScope, - multiShadeInteractor = multiShadeInteractor, - featureFlags = featureFlags, - keyguardTransitionInteractor = - KeyguardTransitionInteractorFactory.create( - scope = TestScope().backgroundScope, - ).keyguardTransitionInteractor, - falsingManager = FalsingManagerFake(), - shadeController = shadeController, - ) - }, - BouncerMessageInteractor(FakeBouncerMessageRepository(), - mock(BouncerMessageFactory::class.java), - FakeUserRepository(), CountDownTimerUtil(), featureFlags), + BouncerMessageInteractor( + FakeBouncerMessageRepository(), + mock(BouncerMessageFactory::class.java), + FakeUserRepository(), + CountDownTimerUtil(), + featureFlags + ), BouncerLogger(logcatLogBuffer("BouncerLog")) ) underTest.setupExpandedStatusBar() diff --git a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt index d9eb9b9166b3c..ed4ac35c72726 100644 --- a/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt +++ b/packages/SystemUI/tests/src/com/android/systemui/shade/NotificationShadeWindowViewTest.kt @@ -34,20 +34,14 @@ import com.android.systemui.bouncer.domain.interactor.BouncerMessageInteractor import com.android.systemui.bouncer.domain.interactor.CountDownTimerUtil import com.android.systemui.bouncer.ui.viewmodel.KeyguardBouncerViewModel import com.android.systemui.classifier.FalsingCollectorFake -import com.android.systemui.classifier.FalsingManagerFake import com.android.systemui.dock.DockManager import com.android.systemui.dump.logcatLogBuffer import com.android.systemui.flags.FakeFeatureFlags import com.android.systemui.flags.Flags import com.android.systemui.keyguard.KeyguardUnlockAnimationController import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractor -import com.android.systemui.keyguard.domain.interactor.KeyguardTransitionInteractorFactory import com.android.systemui.keyguard.ui.viewmodel.PrimaryBouncerToGoneTransitionViewModel import com.android.systemui.log.BouncerLogger -import com.android.systemui.multishade.data.remoteproxy.MultiShadeInputProxy -import com.android.systemui.multishade.data.repository.MultiShadeRepository -import com.android.systemui.multishade.domain.interactor.MultiShadeInteractor -import com.android.systemui.multishade.domain.interactor.MultiShadeMotionEventInteractor import com.android.systemui.power.domain.interactor.PowerInteractor import com.android.systemui.shade.NotificationShadeWindowView.InteractionEventHandler import com.android.systemui.statusbar.DragDownHelper @@ -70,7 +64,6 @@ import com.android.systemui.util.mockito.whenever import com.android.systemui.util.time.FakeSystemClock import com.google.common.truth.Truth.assertThat import java.util.Optional -import kotlinx.coroutines.ExperimentalCoroutinesApi import kotlinx.coroutines.flow.emptyFlow import kotlinx.coroutines.test.TestScope import kotlinx.coroutines.test.runTest @@ -85,7 +78,6 @@ import org.mockito.Mockito.spy import org.mockito.Mockito.verify import org.mockito.MockitoAnnotations -@OptIn(ExperimentalCoroutinesApi::class) @RunWith(AndroidTestingRunner::class) @RunWithLooper(setAsMainLooper = true) @SmallTest @@ -160,22 +152,10 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { val featureFlags = FakeFeatureFlags() featureFlags.set(Flags.TRACKPAD_GESTURE_COMMON, true) featureFlags.set(Flags.TRACKPAD_GESTURE_FEATURES, false) - featureFlags.set(Flags.DUAL_SHADE, false) featureFlags.set(Flags.SPLIT_SHADE_SUBPIXEL_OPTIMIZATION, true) featureFlags.set(Flags.REVAMPED_BOUNCER_MESSAGES, true) featureFlags.set(Flags.LOCKSCREEN_WALLPAPER_DREAM_ENABLED, false) - val inputProxy = MultiShadeInputProxy() testScope = TestScope() - val multiShadeInteractor = - MultiShadeInteractor( - applicationScope = testScope.backgroundScope, - repository = - MultiShadeRepository( - applicationContext = context, - inputProxy = inputProxy, - ), - inputProxy = inputProxy, - ) controller = NotificationShadeWindowViewController( lockscreenShadeTransitionController, @@ -206,23 +186,7 @@ class NotificationShadeWindowViewTest : SysuiTestCase() { keyguardTransitionInteractor, primaryBouncerToGoneTransitionViewModel, featureFlags, - { multiShadeInteractor }, FakeSystemClock(), - { - MultiShadeMotionEventInteractor( - applicationContext = context, - applicationScope = testScope.backgroundScope, - multiShadeInteractor = multiShadeInteractor, - featureFlags = featureFlags, - keyguardTransitionInteractor = - KeyguardTransitionInteractorFactory.create( - scope = TestScope().backgroundScope, - ) - .keyguardTransitionInteractor, - falsingManager = FalsingManagerFake(), - shadeController = shadeController, - ) - }, BouncerMessageInteractor( FakeBouncerMessageRepository(), Mockito.mock(BouncerMessageFactory::class.java),