diff options
49 files changed, 1363 insertions, 2052 deletions
diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt new file mode 100644 index 000000000000..19624e605be8 --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/Pager.kt @@ -0,0 +1,356 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.compose.layout.pager + +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.DecayAnimationSpec +import androidx.compose.animation.rememberSplineBasedDecay +import androidx.compose.foundation.gestures.FlingBehavior +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Box +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.wrapContentSize +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.LazyRow +import androidx.compose.runtime.Composable +import androidx.compose.runtime.LaunchedEffect +import androidx.compose.runtime.Stable +import androidx.compose.runtime.remember +import androidx.compose.runtime.snapshotFlow +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.input.nestedscroll.NestedScrollConnection +import androidx.compose.ui.input.nestedscroll.NestedScrollSource +import androidx.compose.ui.input.nestedscroll.nestedScroll +import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.Velocity +import androidx.compose.ui.unit.dp +import kotlinx.coroutines.flow.collect +import kotlinx.coroutines.flow.filter + +/** Library-wide switch to turn on debug logging. */ +internal const val DebugLog = false + +@RequiresOptIn(message = "Accompanist Pager is experimental. The API may be changed in the future.") +@Retention(AnnotationRetention.BINARY) +annotation class ExperimentalPagerApi + +/** Contains the default values used by [HorizontalPager] and [VerticalPager]. */ +@ExperimentalPagerApi +object PagerDefaults { + /** + * Remember the default [FlingBehavior] that represents the scroll curve. + * + * @param state The [PagerState] to update. + * @param decayAnimationSpec The decay animation spec to use for decayed flings. + * @param snapAnimationSpec The animation spec to use when snapping. + */ + @Composable + fun flingBehavior( + state: PagerState, + decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(), + snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec, + ): FlingBehavior = + rememberSnappingFlingBehavior( + lazyListState = state.lazyListState, + decayAnimationSpec = decayAnimationSpec, + snapAnimationSpec = snapAnimationSpec, + ) + + @Deprecated( + "Replaced with PagerDefaults.flingBehavior()", + ReplaceWith("PagerDefaults.flingBehavior(state, decayAnimationSpec, snapAnimationSpec)") + ) + @Composable + fun rememberPagerFlingConfig( + state: PagerState, + decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(), + snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec, + ): FlingBehavior = flingBehavior(state, decayAnimationSpec, snapAnimationSpec) +} + +/** + * A horizontally scrolling layout that allows users to flip between items to the left and right. + * + * @sample com.google.accompanist.sample.pager.HorizontalPagerSample + * + * @param count the number of pages. + * @param modifier the modifier to apply to this layout. + * @param state the state object to be used to control or observe the pager's state. + * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be + * composed from the end to the start and [PagerState.currentPage] == 0 will mean the first item is + * located at the end. + * @param itemSpacing horizontal spacing to add between items. + * @param flingBehavior logic describing fling behavior. + * @param key the scroll position will be maintained based on the key, which means if you add/remove + * items before the current visible item the item with the given key will be kept as the first + * visible one. + * @param content a block which describes the content. Inside this block you can reference + * [PagerScope.currentPage] and other properties in [PagerScope]. + */ +@ExperimentalPagerApi +@Composable +fun HorizontalPager( + count: Int, + modifier: Modifier = Modifier, + state: PagerState = rememberPagerState(), + reverseLayout: Boolean = false, + itemSpacing: Dp = 0.dp, + flingBehavior: FlingBehavior = PagerDefaults.flingBehavior(state), + verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, + key: ((page: Int) -> Any)? = null, + contentPadding: PaddingValues = PaddingValues(0.dp), + content: @Composable PagerScope.(page: Int) -> Unit, +) { + Pager( + count = count, + state = state, + modifier = modifier, + isVertical = false, + reverseLayout = reverseLayout, + itemSpacing = itemSpacing, + verticalAlignment = verticalAlignment, + flingBehavior = flingBehavior, + key = key, + contentPadding = contentPadding, + content = content + ) +} + +/** + * A vertically scrolling layout that allows users to flip between items to the top and bottom. + * + * @sample com.google.accompanist.sample.pager.VerticalPagerSample + * + * @param count the number of pages. + * @param modifier the modifier to apply to this layout. + * @param state the state object to be used to control or observe the pager's state. + * @param reverseLayout reverse the direction of scrolling and layout, when `true` items will be + * composed from the bottom to the top and [PagerState.currentPage] == 0 will mean the first item is + * located at the bottom. + * @param itemSpacing vertical spacing to add between items. + * @param flingBehavior logic describing fling behavior. + * @param key the scroll position will be maintained based on the key, which means if you add/remove + * items before the current visible item the item with the given key will be kept as the first + * visible one. + * @param content a block which describes the content. Inside this block you can reference + * [PagerScope.currentPage] and other properties in [PagerScope]. + */ +@ExperimentalPagerApi +@Composable +fun VerticalPager( + count: Int, + modifier: Modifier = Modifier, + state: PagerState = rememberPagerState(), + reverseLayout: Boolean = false, + itemSpacing: Dp = 0.dp, + flingBehavior: FlingBehavior = PagerDefaults.flingBehavior(state), + horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, + key: ((page: Int) -> Any)? = null, + contentPadding: PaddingValues = PaddingValues(0.dp), + content: @Composable PagerScope.(page: Int) -> Unit, +) { + Pager( + count = count, + state = state, + modifier = modifier, + isVertical = true, + reverseLayout = reverseLayout, + itemSpacing = itemSpacing, + horizontalAlignment = horizontalAlignment, + flingBehavior = flingBehavior, + key = key, + contentPadding = contentPadding, + content = content + ) +} + +@ExperimentalPagerApi +@Composable +internal fun Pager( + count: Int, + modifier: Modifier, + state: PagerState, + reverseLayout: Boolean, + itemSpacing: Dp, + isVertical: Boolean, + flingBehavior: FlingBehavior, + key: ((page: Int) -> Any)?, + contentPadding: PaddingValues, + verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, + horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, + content: @Composable PagerScope.(page: Int) -> Unit, +) { + require(count >= 0) { "pageCount must be >= 0" } + + // Provide our PagerState with access to the SnappingFlingBehavior animation target + // TODO: can this be done in a better way? + state.flingAnimationTarget = { (flingBehavior as? SnappingFlingBehavior)?.animationTarget } + + LaunchedEffect(count) { + state.currentPage = minOf(count - 1, state.currentPage).coerceAtLeast(0) + } + + // Once a fling (scroll) has finished, notify the state + LaunchedEffect(state) { + // When a 'scroll' has finished, notify the state + snapshotFlow { state.isScrollInProgress } + .filter { !it } + .collect { state.onScrollFinished() } + } + + val pagerScope = remember(state) { PagerScopeImpl(state) } + + // We only consume nested flings in the main-axis, allowing cross-axis flings to propagate + // as normal + val consumeFlingNestedScrollConnection = + ConsumeFlingNestedScrollConnection( + consumeHorizontal = !isVertical, + consumeVertical = isVertical, + ) + + if (isVertical) { + LazyColumn( + state = state.lazyListState, + verticalArrangement = Arrangement.spacedBy(itemSpacing, verticalAlignment), + horizontalAlignment = horizontalAlignment, + flingBehavior = flingBehavior, + reverseLayout = reverseLayout, + contentPadding = contentPadding, + modifier = modifier, + ) { + items( + count = count, + key = key, + ) { page -> + Box( + Modifier + // We don't any nested flings to continue in the pager, so we add a + // connection which consumes them. + // See: https://github.com/google/accompanist/issues/347 + .nestedScroll(connection = consumeFlingNestedScrollConnection) + // Constraint the content to be <= than the size of the pager. + .fillParentMaxHeight() + .wrapContentSize() + ) { pagerScope.content(page) } + } + } + } else { + LazyRow( + state = state.lazyListState, + verticalAlignment = verticalAlignment, + horizontalArrangement = Arrangement.spacedBy(itemSpacing, horizontalAlignment), + flingBehavior = flingBehavior, + reverseLayout = reverseLayout, + contentPadding = contentPadding, + modifier = modifier, + ) { + items( + count = count, + key = key, + ) { page -> + Box( + Modifier + // We don't any nested flings to continue in the pager, so we add a + // connection which consumes them. + // See: https://github.com/google/accompanist/issues/347 + .nestedScroll(connection = consumeFlingNestedScrollConnection) + // Constraint the content to be <= than the size of the pager. + .fillParentMaxWidth() + .wrapContentSize() + ) { pagerScope.content(page) } + } + } + } +} + +private class ConsumeFlingNestedScrollConnection( + private val consumeHorizontal: Boolean, + private val consumeVertical: Boolean, +) : NestedScrollConnection { + override fun onPostScroll( + consumed: Offset, + available: Offset, + source: NestedScrollSource + ): Offset = + when (source) { + // We can consume all resting fling scrolls so that they don't propagate up to the + // Pager + NestedScrollSource.Fling -> available.consume(consumeHorizontal, consumeVertical) + else -> Offset.Zero + } + + override suspend fun onPostFling(consumed: Velocity, available: Velocity): Velocity { + // We can consume all post fling velocity on the main-axis + // so that it doesn't propagate up to the Pager + return available.consume(consumeHorizontal, consumeVertical) + } +} + +private fun Offset.consume( + consumeHorizontal: Boolean, + consumeVertical: Boolean, +): Offset = + Offset( + x = if (consumeHorizontal) this.x else 0f, + y = if (consumeVertical) this.y else 0f, + ) + +private fun Velocity.consume( + consumeHorizontal: Boolean, + consumeVertical: Boolean, +): Velocity = + Velocity( + x = if (consumeHorizontal) this.x else 0f, + y = if (consumeVertical) this.y else 0f, + ) + +/** Scope for [HorizontalPager] content. */ +@ExperimentalPagerApi +@Stable +interface PagerScope { + /** Returns the current selected page */ + val currentPage: Int + + /** The current offset from the start of [currentPage], as a ratio of the page width. */ + val currentPageOffset: Float +} + +@ExperimentalPagerApi +private class PagerScopeImpl( + private val state: PagerState, +) : PagerScope { + override val currentPage: Int + get() = state.currentPage + override val currentPageOffset: Float + get() = state.currentPageOffset +} + +/** + * Calculate the offset for the given [page] from the current scroll position. This is useful when + * using the scroll position to apply effects or animations to items. + * + * The returned offset can positive or negative, depending on whether which direction the [page] is + * compared to the current scroll position. + * + * @sample com.google.accompanist.sample.pager.HorizontalPagerWithOffsetTransition + */ +@ExperimentalPagerApi +fun PagerScope.calculateCurrentOffsetForPage(page: Int): Float { + return (currentPage + currentPageOffset) - page +} diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt new file mode 100644 index 000000000000..288c26eb1199 --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/PagerState.kt @@ -0,0 +1,348 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.compose.layout.pager + +import androidx.annotation.FloatRange +import androidx.annotation.IntRange +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.spring +import androidx.compose.foundation.MutatePriority +import androidx.compose.foundation.gestures.ScrollScope +import androidx.compose.foundation.gestures.ScrollableState +import androidx.compose.foundation.interaction.InteractionSource +import androidx.compose.foundation.lazy.LazyListItemInfo +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.Stable +import androidx.compose.runtime.derivedStateOf +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.saveable.Saver +import androidx.compose.runtime.saveable.listSaver +import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.runtime.setValue +import kotlin.math.absoluteValue +import kotlin.math.roundToInt + +@Deprecated( + "Replaced with rememberPagerState(initialPage) and count parameter on Pager composables", + ReplaceWith("rememberPagerState(initialPage)"), + level = DeprecationLevel.ERROR, +) +@Suppress("UNUSED_PARAMETER", "NOTHING_TO_INLINE") +@ExperimentalPagerApi +@Composable +inline fun rememberPagerState( + @IntRange(from = 0) pageCount: Int, + @IntRange(from = 0) initialPage: Int = 0, + @FloatRange(from = 0.0, to = 1.0) initialPageOffset: Float = 0f, + @IntRange(from = 1) initialOffscreenLimit: Int = 1, + infiniteLoop: Boolean = false +): PagerState { + return rememberPagerState(initialPage = initialPage) +} + +/** + * Creates a [PagerState] that is remembered across compositions. + * + * Changes to the provided values for [initialPage] will **not** result in the state being recreated + * or changed in any way if it has already been created. + * + * @param initialPage the initial value for [PagerState.currentPage] + */ +@ExperimentalPagerApi +@Composable +fun rememberPagerState( + @IntRange(from = 0) initialPage: Int = 0, +): PagerState = + rememberSaveable(saver = PagerState.Saver) { + PagerState( + currentPage = initialPage, + ) + } + +/** + * A state object that can be hoisted to control and observe scrolling for [HorizontalPager]. + * + * In most cases, this will be created via [rememberPagerState]. + * + * @param currentPage the initial value for [PagerState.currentPage] + */ +@ExperimentalPagerApi +@Stable +class PagerState( + @IntRange(from = 0) currentPage: Int = 0, +) : ScrollableState { + // Should this be public? + internal val lazyListState = LazyListState(firstVisibleItemIndex = currentPage) + + private var _currentPage by mutableStateOf(currentPage) + + private val currentLayoutPageInfo: LazyListItemInfo? + get() = + lazyListState.layoutInfo.visibleItemsInfo + .asSequence() + .filter { it.offset <= 0 && it.offset + it.size > 0 } + .lastOrNull() + + private val currentLayoutPageOffset: Float + get() = + currentLayoutPageInfo?.let { current -> + // We coerce since itemSpacing can make the offset > 1f. + // We don't want to count spacing in the offset so cap it to 1f + (-current.offset / current.size.toFloat()).coerceIn(0f, 1f) + } + ?: 0f + + /** + * [InteractionSource] that will be used to dispatch drag events when this list is being + * dragged. If you want to know whether the fling (or animated scroll) is in progress, use + * [isScrollInProgress]. + */ + val interactionSource: InteractionSource + get() = lazyListState.interactionSource + + /** The number of pages to display. */ + @get:IntRange(from = 0) + val pageCount: Int by derivedStateOf { lazyListState.layoutInfo.totalItemsCount } + + /** + * The index of the currently selected page. This may not be the page which is currently + * displayed on screen. + * + * To update the scroll position, use [scrollToPage] or [animateScrollToPage]. + */ + @get:IntRange(from = 0) + var currentPage: Int + get() = _currentPage + internal set(value) { + if (value != _currentPage) { + _currentPage = value + } + } + + /** + * The current offset from the start of [currentPage], as a ratio of the page width. + * + * To update the scroll position, use [scrollToPage] or [animateScrollToPage]. + */ + val currentPageOffset: Float by derivedStateOf { + currentLayoutPageInfo?.let { + // The current page offset is the current layout page delta from `currentPage` + // (which is only updated after a scroll/animation). + // We calculate this by looking at the current layout page + it's offset, + // then subtracting the 'current page'. + it.index + currentLayoutPageOffset - _currentPage + } + ?: 0f + } + + /** The target page for any on-going animations. */ + private var animationTargetPage: Int? by mutableStateOf(null) + + internal var flingAnimationTarget: (() -> Int?)? by mutableStateOf(null) + + /** + * The target page for any on-going animations or scrolls by the user. Returns the current page + * if a scroll or animation is not currently in progress. + */ + val targetPage: Int + get() = + animationTargetPage + ?: flingAnimationTarget?.invoke() + ?: when { + // If a scroll isn't in progress, return the current page + !isScrollInProgress -> currentPage + // If the offset is 0f (or very close), return the current page + currentPageOffset.absoluteValue < 0.001f -> currentPage + // If we're offset towards the start, guess the previous page + currentPageOffset < -0.5f -> (currentPage - 1).coerceAtLeast(0) + // If we're offset towards the end, guess the next page + else -> (currentPage + 1).coerceAtMost(pageCount - 1) + } + + @Deprecated( + "Replaced with animateScrollToPage(page, pageOffset)", + ReplaceWith("animateScrollToPage(page = page, pageOffset = pageOffset)") + ) + @Suppress("UNUSED_PARAMETER") + suspend fun animateScrollToPage( + @IntRange(from = 0) page: Int, + @FloatRange(from = 0.0, to = 1.0) pageOffset: Float = 0f, + animationSpec: AnimationSpec<Float> = spring(), + initialVelocity: Float = 0f, + skipPages: Boolean = true, + ) { + animateScrollToPage(page = page, pageOffset = pageOffset) + } + + /** + * Animate (smooth scroll) to the given page to the middle of the viewport. + * + * Cancels the currently running scroll, if any, and suspends until the cancellation is + * complete. + * + * @param page the page to animate to. Must be between 0 and [pageCount] (inclusive). + * @param pageOffset the percentage of the page width to offset, from the start of [page]. Must + * be in the range 0f..1f. + */ + suspend fun animateScrollToPage( + @IntRange(from = 0) page: Int, + @FloatRange(from = 0.0, to = 1.0) pageOffset: Float = 0f, + ) { + requireCurrentPage(page, "page") + requireCurrentPageOffset(pageOffset, "pageOffset") + try { + animationTargetPage = page + + if (pageOffset <= 0.005f) { + // If the offset is (close to) zero, just call animateScrollToItem and we're done + lazyListState.animateScrollToItem(index = page) + } else { + // Else we need to figure out what the offset is in pixels... + + var target = + lazyListState.layoutInfo.visibleItemsInfo.firstOrNull { it.index == page } + + if (target != null) { + // If we have access to the target page layout, we can calculate the pixel + // offset from the size + lazyListState.animateScrollToItem( + index = page, + scrollOffset = (target.size * pageOffset).roundToInt() + ) + } else { + // If we don't, we use the current page size as a guide + val currentSize = currentLayoutPageInfo!!.size + lazyListState.animateScrollToItem( + index = page, + scrollOffset = (currentSize * pageOffset).roundToInt() + ) + + // The target should be visible now + target = lazyListState.layoutInfo.visibleItemsInfo.first { it.index == page } + + if (target.size != currentSize) { + // If the size we used for calculating the offset differs from the actual + // target page size, we need to scroll again. This doesn't look great, + // but there's not much else we can do. + lazyListState.animateScrollToItem( + index = page, + scrollOffset = (target.size * pageOffset).roundToInt() + ) + } + } + } + } finally { + // We need to manually call this, as the `animateScrollToItem` call above will happen + // in 1 frame, which is usually too fast for the LaunchedEffect in Pager to detect + // the change. This is especially true when running unit tests. + onScrollFinished() + } + } + + /** + * Instantly brings the item at [page] to the middle of the viewport. + * + * Cancels the currently running scroll, if any, and suspends until the cancellation is + * complete. + * + * @param page the page to snap to. Must be between 0 and [pageCount] (inclusive). + */ + suspend fun scrollToPage( + @IntRange(from = 0) page: Int, + @FloatRange(from = 0.0, to = 1.0) pageOffset: Float = 0f, + ) { + requireCurrentPage(page, "page") + requireCurrentPageOffset(pageOffset, "pageOffset") + try { + animationTargetPage = page + + // First scroll to the given page. It will now be laid out at offset 0 + lazyListState.scrollToItem(index = page) + + // If we have a start spacing, we need to offset (scroll) by that too + if (pageOffset > 0.0001f) { + scroll { currentLayoutPageInfo?.let { scrollBy(it.size * pageOffset) } } + } + } finally { + // We need to manually call this, as the `scroll` call above will happen in 1 frame, + // which is usually too fast for the LaunchedEffect in Pager to detect the change. + // This is especially true when running unit tests. + onScrollFinished() + } + } + + internal fun onScrollFinished() { + // Then update the current page to our layout page + currentPage = currentLayoutPageInfo?.index ?: 0 + // Clear the animation target page + animationTargetPage = null + } + + override suspend fun scroll( + scrollPriority: MutatePriority, + block: suspend ScrollScope.() -> Unit + ) = lazyListState.scroll(scrollPriority, block) + + override fun dispatchRawDelta(delta: Float): Float { + return lazyListState.dispatchRawDelta(delta) + } + + override val isScrollInProgress: Boolean + get() = lazyListState.isScrollInProgress + + override fun toString(): String = + "PagerState(" + + "pageCount=$pageCount, " + + "currentPage=$currentPage, " + + "currentPageOffset=$currentPageOffset" + + ")" + + private fun requireCurrentPage(value: Int, name: String) { + if (pageCount == 0) { + require(value == 0) { "$name must be 0 when pageCount is 0" } + } else { + require(value in 0 until pageCount) { "$name[$value] must be >= 0 and < pageCount" } + } + } + + private fun requireCurrentPageOffset(value: Float, name: String) { + if (pageCount == 0) { + require(value == 0f) { "$name must be 0f when pageCount is 0" } + } else { + require(value in 0f..1f) { "$name must be >= 0 and <= 1" } + } + } + + companion object { + /** The default [Saver] implementation for [PagerState]. */ + val Saver: Saver<PagerState, *> = + listSaver( + save = { + listOf<Any>( + it.currentPage, + ) + }, + restore = { + PagerState( + currentPage = it[0] as Int, + ) + } + ) + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt new file mode 100644 index 000000000000..0b53f5324a7d --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/layout/pager/SnappingFlingBehavior.kt @@ -0,0 +1,270 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.compose.layout.pager + +import androidx.compose.animation.core.AnimationSpec +import androidx.compose.animation.core.AnimationState +import androidx.compose.animation.core.DecayAnimationSpec +import androidx.compose.animation.core.animateDecay +import androidx.compose.animation.core.animateTo +import androidx.compose.animation.core.calculateTargetValue +import androidx.compose.animation.core.spring +import androidx.compose.animation.rememberSplineBasedDecay +import androidx.compose.foundation.gestures.FlingBehavior +import androidx.compose.foundation.gestures.ScrollScope +import androidx.compose.foundation.lazy.LazyListItemInfo +import androidx.compose.foundation.lazy.LazyListState +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue +import kotlin.math.abs + +/** Default values used for [SnappingFlingBehavior] & [rememberSnappingFlingBehavior]. */ +internal object SnappingFlingBehaviorDefaults { + /** TODO */ + val snapAnimationSpec: AnimationSpec<Float> = spring(stiffness = 600f) +} + +/** + * Create and remember a snapping [FlingBehavior] to be used with [LazyListState]. + * + * TODO: move this to a new module and make it public + * + * @param lazyListState The [LazyListState] to update. + * @param decayAnimationSpec The decay animation spec to use for decayed flings. + * @param snapAnimationSpec The animation spec to use when snapping. + */ +@Composable +internal fun rememberSnappingFlingBehavior( + lazyListState: LazyListState, + decayAnimationSpec: DecayAnimationSpec<Float> = rememberSplineBasedDecay(), + snapAnimationSpec: AnimationSpec<Float> = SnappingFlingBehaviorDefaults.snapAnimationSpec, +): SnappingFlingBehavior = + remember(lazyListState, decayAnimationSpec, snapAnimationSpec) { + SnappingFlingBehavior( + lazyListState = lazyListState, + decayAnimationSpec = decayAnimationSpec, + snapAnimationSpec = snapAnimationSpec, + ) + } + +/** + * A snapping [FlingBehavior] for [LazyListState]. Typically this would be created via + * [rememberSnappingFlingBehavior]. + * + * @param lazyListState The [LazyListState] to update. + * @param decayAnimationSpec The decay animation spec to use for decayed flings. + * @param snapAnimationSpec The animation spec to use when snapping. + */ +internal class SnappingFlingBehavior( + private val lazyListState: LazyListState, + private val decayAnimationSpec: DecayAnimationSpec<Float>, + private val snapAnimationSpec: AnimationSpec<Float>, +) : FlingBehavior { + /** The target item index for any on-going animations. */ + var animationTarget: Int? by mutableStateOf(null) + private set + + override suspend fun ScrollScope.performFling(initialVelocity: Float): Float { + val itemInfo = currentItemInfo ?: return initialVelocity + + // If the decay fling can scroll past the current item, fling with decay + return if (decayAnimationSpec.canFlingPastCurrentItem(itemInfo, initialVelocity)) { + performDecayFling(initialVelocity, itemInfo) + } else { + // Otherwise we 'spring' to current/next item + performSpringFling( + index = + when { + // If the velocity is greater than 1 item per second (velocity is px/s), + // spring + // in the relevant direction + initialVelocity > itemInfo.size -> { + (itemInfo.index + 1).coerceAtMost( + lazyListState.layoutInfo.totalItemsCount - 1 + ) + } + initialVelocity < -itemInfo.size -> itemInfo.index + // If the velocity is 0 (or less than the size of the item), spring to + // whichever item is closest to the snap point + itemInfo.offset < -itemInfo.size / 2 -> itemInfo.index + 1 + else -> itemInfo.index + }, + initialVelocity = initialVelocity, + ) + } + } + + private suspend fun ScrollScope.performDecayFling( + initialVelocity: Float, + startItem: LazyListItemInfo, + ): Float { + val index = + when { + initialVelocity > 0 -> startItem.index + 1 + else -> startItem.index + } + val forward = index > (currentItemInfo?.index ?: return initialVelocity) + + // Update the animationTarget + animationTarget = index + + var velocityLeft = initialVelocity + var lastValue = 0f + AnimationState( + initialValue = 0f, + initialVelocity = initialVelocity, + ) + .animateDecay(decayAnimationSpec) { + val delta = value - lastValue + val consumed = scrollBy(delta) + lastValue = value + velocityLeft = this.velocity + + val current = currentItemInfo + if (current == null) { + cancelAnimation() + return@animateDecay + } + + if ( + !forward && + (current.index < index || current.index == index && current.offset >= 0) + ) { + // 'snap back' to the item as we may have scrolled past it + scrollBy(lazyListState.calculateScrollOffsetToItem(index).toFloat()) + cancelAnimation() + } else if ( + forward && + (current.index > index || current.index == index && current.offset <= 0) + ) { + // 'snap back' to the item as we may have scrolled past it + scrollBy(lazyListState.calculateScrollOffsetToItem(index).toFloat()) + cancelAnimation() + } else if (abs(delta - consumed) > 0.5f) { + // avoid rounding errors and stop if anything is unconsumed + cancelAnimation() + } + } + animationTarget = null + return velocityLeft + } + + private suspend fun ScrollScope.performSpringFling( + index: Int, + scrollOffset: Int = 0, + initialVelocity: Float = 0f, + ): Float { + // If we don't have a current layout, we can't snap + val initialItem = currentItemInfo ?: return initialVelocity + + val forward = index > initialItem.index + // We add 10% on to the size of the current item, to compensate for any item spacing, etc + val target = (if (forward) initialItem.size else -initialItem.size) * 1.1f + + // Update the animationTarget + animationTarget = index + + var velocityLeft = initialVelocity + var lastValue = 0f + AnimationState( + initialValue = 0f, + initialVelocity = initialVelocity, + ) + .animateTo( + targetValue = target, + animationSpec = snapAnimationSpec, + ) { + // Springs can overshoot their target, clamp to the desired range + val coercedValue = + if (forward) { + value.coerceAtMost(target) + } else { + value.coerceAtLeast(target) + } + val delta = coercedValue - lastValue + val consumed = scrollBy(delta) + lastValue = coercedValue + velocityLeft = this.velocity + + val current = currentItemInfo + if (current == null) { + cancelAnimation() + return@animateTo + } + + if (scrolledPastItem(initialVelocity, current, index, scrollOffset)) { + // If we've scrolled to/past the item, stop the animation. We may also need to + // 'snap back' to the item as we may have scrolled past it + scrollBy(lazyListState.calculateScrollOffsetToItem(index).toFloat()) + cancelAnimation() + } else if (abs(delta - consumed) > 0.5f) { + // avoid rounding errors and stop if anything is unconsumed + cancelAnimation() + } + } + animationTarget = null + return velocityLeft + } + + private fun LazyListState.calculateScrollOffsetToItem(index: Int): Int { + return layoutInfo.visibleItemsInfo.firstOrNull { it.index == index }?.offset ?: 0 + } + + private val currentItemInfo: LazyListItemInfo? + get() = + lazyListState.layoutInfo.visibleItemsInfo + .asSequence() + .filter { it.offset <= 0 && it.offset + it.size > 0 } + .lastOrNull() +} + +private fun scrolledPastItem( + initialVelocity: Float, + currentItem: LazyListItemInfo, + targetIndex: Int, + targetScrollOffset: Int = 0, +): Boolean { + return if (initialVelocity > 0) { + // forward + currentItem.index > targetIndex || + (currentItem.index == targetIndex && currentItem.offset <= targetScrollOffset) + } else { + // backwards + currentItem.index < targetIndex || + (currentItem.index == targetIndex && currentItem.offset >= targetScrollOffset) + } +} + +private fun DecayAnimationSpec<Float>.canFlingPastCurrentItem( + currentItem: LazyListItemInfo, + initialVelocity: Float, +): Boolean { + val targetValue = + calculateTargetValue( + initialValue = currentItem.offset.toFloat(), + initialVelocity = initialVelocity, + ) + return when { + // forward. We add 10% onto the size to cater for any item spacing + initialVelocity > 0 -> targetValue <= -(currentItem.size * 1.1f) + // backwards. We add 10% onto the size to cater for any item spacing + else -> targetValue >= (currentItem.size * 0.1f) + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt new file mode 100644 index 000000000000..3b13c0b78cbe --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Padding.kt @@ -0,0 +1,142 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.android.systemui.compose.modifiers + +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.LayoutModifier +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.platform.InspectorInfo +import androidx.compose.ui.platform.InspectorValueInfo +import androidx.compose.ui.platform.debugInspectorInfo +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.constrainHeight +import androidx.compose.ui.unit.constrainWidth +import androidx.compose.ui.unit.offset + +// This file was mostly copy/pasted from by androidx.compose.foundation.layout.Padding.kt and +// contains modifiers with lambda parameters to change the padding of a Composable without +// triggering recomposition when the paddings change. +// +// These should be used instead of the traditional size modifiers when the size changes often, for +// instance when it is animated. +// +// TODO(b/247473910): Remove these modifiers once they can be fully replaced by layout animations +// APIs. + +/** @see androidx.compose.foundation.layout.padding */ +fun Modifier.padding( + start: Density.() -> Int = PaddingUnspecified, + top: Density.() -> Int = PaddingUnspecified, + end: Density.() -> Int = PaddingUnspecified, + bottom: Density.() -> Int = PaddingUnspecified, +) = + this.then( + PaddingModifier( + start, + top, + end, + bottom, + rtlAware = true, + inspectorInfo = + debugInspectorInfo { + name = "padding" + properties["start"] = start + properties["top"] = top + properties["end"] = end + properties["bottom"] = bottom + } + ) + ) + +/** @see androidx.compose.foundation.layout.padding */ +fun Modifier.padding( + horizontal: Density.() -> Int = PaddingUnspecified, + vertical: Density.() -> Int = PaddingUnspecified, +): Modifier { + return this.then( + PaddingModifier( + start = horizontal, + top = vertical, + end = horizontal, + bottom = vertical, + rtlAware = true, + inspectorInfo = + debugInspectorInfo { + name = "padding" + properties["horizontal"] = horizontal + properties["vertical"] = vertical + } + ) + ) +} + +private val PaddingUnspecified: Density.() -> Int = { 0 } + +private class PaddingModifier( + val start: Density.() -> Int, + val top: Density.() -> Int, + val end: Density.() -> Int, + val bottom: Density.() -> Int, + val rtlAware: Boolean, + inspectorInfo: InspectorInfo.() -> Unit +) : LayoutModifier, InspectorValueInfo(inspectorInfo) { + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + val start = start() + val top = top() + val end = end() + val bottom = bottom() + + val horizontal = start + end + val vertical = top + bottom + + val placeable = measurable.measure(constraints.offset(-horizontal, -vertical)) + + val width = constraints.constrainWidth(placeable.width + horizontal) + val height = constraints.constrainHeight(placeable.height + vertical) + return layout(width, height) { + if (rtlAware) { + placeable.placeRelative(start, top) + } else { + placeable.place(start, top) + } + } + } + + override fun hashCode(): Int { + var result = start.hashCode() + result = 31 * result + top.hashCode() + result = 31 * result + end.hashCode() + result = 31 * result + bottom.hashCode() + result = 31 * result + rtlAware.hashCode() + return result + } + + override fun equals(other: Any?): Boolean { + val otherModifier = other as? PaddingModifier ?: return false + return start == otherModifier.start && + top == otherModifier.top && + end == otherModifier.end && + bottom == otherModifier.bottom && + rtlAware == otherModifier.rtlAware + } +} diff --git a/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt new file mode 100644 index 000000000000..570d24312c80 --- /dev/null +++ b/packages/SystemUI/compose/core/src/com/android/systemui/compose/modifiers/Size.kt @@ -0,0 +1,247 @@ +/* + * Copyright (C) 2022 The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + * + */ + +package com.android.systemui.compose.modifiers + +import androidx.compose.ui.Modifier +import androidx.compose.ui.layout.IntrinsicMeasurable +import androidx.compose.ui.layout.IntrinsicMeasureScope +import androidx.compose.ui.layout.LayoutModifier +import androidx.compose.ui.layout.Measurable +import androidx.compose.ui.layout.MeasureResult +import androidx.compose.ui.layout.MeasureScope +import androidx.compose.ui.platform.InspectorInfo +import androidx.compose.ui.platform.InspectorValueInfo +import androidx.compose.ui.platform.debugInspectorInfo +import androidx.compose.ui.unit.Constraints +import androidx.compose.ui.unit.Density +import androidx.compose.ui.unit.constrain +import androidx.compose.ui.unit.constrainHeight +import androidx.compose.ui.unit.constrainWidth + +// This file was mostly copy pasted from androidx.compose.foundation.layout.Size.kt and contains +// modifiers with lambda parameters to change the (min/max) size of a Composable without triggering +// recomposition when the sizes change. +// +// These should be used instead of the traditional size modifiers when the size changes often, for +// instance when it is animated. +// +// TODO(b/247473910): Remove these modifiers once they can be fully replaced by layout animations +// APIs. + +/** @see androidx.compose.foundation.layout.width */ +fun Modifier.width(width: Density.() -> Int) = + this.then( + SizeModifier( + minWidth = width, + maxWidth = width, + enforceIncoming = true, + inspectorInfo = + debugInspectorInfo { + name = "width" + value = width + } + ) + ) + +/** @see androidx.compose.foundation.layout.height */ +fun Modifier.height(height: Density.() -> Int) = + this.then( + SizeModifier( + minHeight = height, + maxHeight = height, + enforceIncoming = true, + inspectorInfo = + debugInspectorInfo { + name = "height" + value = height + } + ) + ) + +/** @see androidx.compose.foundation.layout.size */ +fun Modifier.size(width: Density.() -> Int, height: Density.() -> Int) = + this.then( + SizeModifier( + minWidth = width, + maxWidth = width, + minHeight = height, + maxHeight = height, + enforceIncoming = true, + inspectorInfo = + debugInspectorInfo { + name = "size" + properties["width"] = width + properties["height"] = height + } + ) + ) + +private val SizeUnspecified: Density.() -> Int = { 0 } + +private class SizeModifier( + private val minWidth: Density.() -> Int = SizeUnspecified, + private val minHeight: Density.() -> Int = SizeUnspecified, + private val maxWidth: Density.() -> Int = SizeUnspecified, + private val maxHeight: Density.() -> Int = SizeUnspecified, + private val enforceIncoming: Boolean, + inspectorInfo: InspectorInfo.() -> Unit +) : LayoutModifier, InspectorValueInfo(inspectorInfo) { + private val Density.targetConstraints: Constraints + get() { + val maxWidth = + if (maxWidth != SizeUnspecified) { + maxWidth().coerceAtLeast(0) + } else { + Constraints.Infinity + } + val maxHeight = + if (maxHeight != SizeUnspecified) { + maxHeight().coerceAtLeast(0) + } else { + Constraints.Infinity + } + val minWidth = + if (minWidth != SizeUnspecified) { + minWidth().coerceAtMost(maxWidth).coerceAtLeast(0).let { + if (it != Constraints.Infinity) it else 0 + } + } else { + 0 + } + val minHeight = + if (minHeight != SizeUnspecified) { + minHeight().coerceAtMost(maxHeight).coerceAtLeast(0).let { + if (it != Constraints.Infinity) it else 0 + } + } else { + 0 + } + return Constraints( + minWidth = minWidth, + minHeight = minHeight, + maxWidth = maxWidth, + maxHeight = maxHeight + ) + } + + override fun MeasureScope.measure( + measurable: Measurable, + constraints: Constraints + ): MeasureResult { + val wrappedConstraints = + targetConstraints.let { targetConstraints -> + if (enforceIncoming) { + constraints.constrain(targetConstraints) + } else { + val resolvedMinWidth = + if (minWidth != SizeUnspecified) { + targetConstraints.minWidth + } else { + constraints.minWidth.coerceAtMost(targetConstraints.maxWidth) + } + val resolvedMaxWidth = + if (maxWidth != SizeUnspecified) { + targetConstraints.maxWidth + } else { + constraints.maxWidth.coerceAtLeast(targetConstraints.minWidth) + } + val resolvedMinHeight = + if (minHeight != SizeUnspecified) { + targetConstraints.minHeight + } else { + constraints.minHeight.coerceAtMost(targetConstraints.maxHeight) + } + val resolvedMaxHeight = + if (maxHeight != SizeUnspecified) { + targetConstraints.maxHeight + } else { + constraints.maxHeight.coerceAtLeast(targetConstraints.minHeight) + } + Constraints( + resolvedMinWidth, + resolvedMaxWidth, + resolvedMinHeight, + resolvedMaxHeight + ) + } + } + val placeable = measurable.measure(wrappedConstraints) + return layout(placeable.width, placeable.height) { placeable.placeRelative(0, 0) } + } + + override fun IntrinsicMeasureScope.minIntrinsicWidth( + measurable: IntrinsicMeasurable, + height: Int + ): Int { + val constraints = targetConstraints + return if (constraints.hasFixedWidth) { + constraints.maxWidth + } else { + constraints.constrainWidth(measurable.minIntrinsicWidth(height)) + } + } + + override fun IntrinsicMeasureScope.minIntrinsicHeight( + measurable: IntrinsicMeasurable, + width: Int + ): Int { + val constraints = targetConstraints + return if (constraints.hasFixedHeight) { + constraints.maxHeight + } else { + constraints.constrainHeight(measurable.minIntrinsicHeight(width)) + } + } + + override fun IntrinsicMeasureScope.maxIntrinsicWidth( + measurable: IntrinsicMeasurable, + height: Int + ): Int { + val constraints = targetConstraints + return if (constraints.hasFixedWidth) { + constraints.maxWidth + } else { + constraints.constrainWidth(measurable.maxIntrinsicWidth(height)) + } + } + + override fun IntrinsicMeasureScope.maxIntrinsicHeight( + measurable: IntrinsicMeasurable, + width: Int + ): Int { + val constraints = targetConstraints + return if (constraints.hasFixedHeight) { + constraints.maxHeight + } else { + constraints.constrainHeight(measurable.maxIntrinsicHeight(width)) + } + } + + override fun equals(other: Any?): Boolean { + if (other !is SizeModifier) return false + return minWidth == other.minWidth && + minHeight == other.minHeight && + maxWidth == other.maxWidth && + maxHeight == other.maxHeight && + enforceIncoming == other.enforceIncoming + } + + override fun hashCode() = + (((((minWidth.hashCode() * 31 + minHeight.hashCode()) * 31) + maxWidth.hashCode()) * 31) + + maxHeight.hashCode()) * 31 +} diff --git a/packages/SystemUI/compose/gallery/Android.bp b/packages/SystemUI/compose/gallery/Android.bp deleted file mode 100644 index 5a7a1e1807a3..000000000000 --- a/packages/SystemUI/compose/gallery/Android.bp +++ /dev/null @@ -1,86 +0,0 @@ -// Copyright (C) 2022 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], -} - -android_library { - name: "SystemUIComposeGalleryLib", - manifest: "AndroidManifest.xml", - - srcs: [ - "src/**/*.kt", - ":SystemUI-tests-utils", - ], - - resource_dirs: [ - "res", - ], - - static_libs: [ - "SystemUI-core", - "SystemUIComposeCore", - "SystemUIComposeFeatures", - - "androidx.compose.runtime_runtime", - "androidx.compose.material3_material3", - "androidx.compose.material_material-icons-extended", - "androidx.activity_activity-compose", - "androidx.navigation_navigation-compose", - - "androidx.appcompat_appcompat", - - // TODO(b/240431193): Remove the dependencies and depend on - // SystemUI-test-utils directly. - "androidx.test.runner", - "mockito-target-extended-minus-junit4", - "testables", - "truth-prebuilt", - "androidx.test.uiautomator", - "kotlinx_coroutines_test", - ], - - libs: [ - "android.test.mock", - ], - - kotlincflags: ["-Xjvm-default=all"], -} - -android_app { - name: "SystemUIComposeGallery", - defaults: ["platform_app_defaults"], - manifest: "app/AndroidManifest.xml", - - static_libs: [ - "SystemUIComposeGalleryLib", - ], - - platform_apis: true, - system_ext_specific: true, - certificate: "platform", - privileged: true, - - optimize: { - proguard_flags_files: ["proguard-rules.pro"], - }, - - dxflags: ["--multi-dex"], -} diff --git a/packages/SystemUI/compose/gallery/AndroidManifest.xml b/packages/SystemUI/compose/gallery/AndroidManifest.xml deleted file mode 100644 index 2f30651a6acf..000000000000 --- a/packages/SystemUI/compose/gallery/AndroidManifest.xml +++ /dev/null @@ -1,55 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="com.android.systemui.compose.gallery"> - <!-- To emulate a display size and density. --> - <uses-permission android:name="android.permission.WRITE_SECURE_SETTINGS" /> - - <application - android:name="android.app.Application" - android:appComponentFactory="androidx.core.app.AppComponentFactory" - tools:replace="android:name,android:appComponentFactory"> - <!-- Disable providers from SystemUI --> - <provider android:name="com.android.systemui.keyguard.KeyguardSliceProvider" - android:authorities="com.android.systemui.test.keyguard.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="com.google.android.systemui.keyguard.KeyguardSliceProviderGoogle" - android:authorities="com.android.systemui.test.keyguard.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="com.android.keyguard.clock.ClockOptionsProvider" - android:authorities="com.android.systemui.test.keyguard.clock.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="com.android.systemui.people.PeopleProvider" - android:authorities="com.android.systemui.test.people.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove" /> - <provider android:name="androidx.core.content.FileProvider" - android:authorities="com.android.systemui.test.fileprovider.disabled" - android:enabled="false" - tools:replace="android:authorities" - tools:node="remove"/> - </application> -</manifest> diff --git a/packages/SystemUI/compose/gallery/TEST_MAPPING b/packages/SystemUI/compose/gallery/TEST_MAPPING deleted file mode 100644 index c7f8a9216418..000000000000 --- a/packages/SystemUI/compose/gallery/TEST_MAPPING +++ /dev/null @@ -1,15 +0,0 @@ -{ - "presubmit": [ - { - "name": "SystemUIComposeGalleryTests", - "options": [ - { - "exclude-annotation": "org.junit.Ignore" - }, - { - "exclude-annotation": "androidx.test.filters.FlakyTest" - } - ] - } - ] -}
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml b/packages/SystemUI/compose/gallery/app/AndroidManifest.xml deleted file mode 100644 index 1f3fd8c312d9..000000000000 --- a/packages/SystemUI/compose/gallery/app/AndroidManifest.xml +++ /dev/null @@ -1,39 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:tools="http://schemas.android.com/tools" - package="com.android.systemui.compose.gallery.app"> - <application - android:allowBackup="true" - android:icon="@mipmap/ic_launcher" - android:label="@string/app_name" - android:roundIcon="@mipmap/ic_launcher_round" - android:supportsRtl="true" - android:theme="@style/Theme.SystemUI.Gallery" - tools:replace="android:icon,android:theme,android:label"> - <activity - android:name="com.android.systemui.compose.gallery.GalleryActivity" - android:exported="true" - android:label="@string/app_name"> - <intent-filter> - <action android:name="android.intent.action.MAIN" /> - <category android:name="android.intent.category.LAUNCHER" /> - </intent-filter> - </activity> - </application> -</manifest> diff --git a/packages/SystemUI/compose/gallery/proguard-rules.pro b/packages/SystemUI/compose/gallery/proguard-rules.pro deleted file mode 100644 index 481bb4348141..000000000000 --- a/packages/SystemUI/compose/gallery/proguard-rules.pro +++ /dev/null @@ -1,21 +0,0 @@ -# Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. -# -# For more details, see -# http://developer.android.com/guide/developing/tools/proguard.html - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-keepclassmembers class fqcn.of.javascript.interface.for.webview { -# public *; -#} - -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable - -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/drawable-v24/ic_launcher_foreground.xml b/packages/SystemUI/compose/gallery/res/drawable-v24/ic_launcher_foreground.xml deleted file mode 100644 index 966abaff2074..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable-v24/ic_launcher_foreground.xml +++ /dev/null @@ -1,30 +0,0 @@ -<vector xmlns:android="http://schemas.android.com/apk/res/android" - xmlns:aapt="http://schemas.android.com/aapt" - android:width="108dp" - android:height="108dp" - android:viewportHeight="108" - android:viewportWidth="108"> - <path android:pathData="M31,63.928c0,0 6.4,-11 12.1,-13.1c7.2,-2.6 26,-1.4 26,-1.4l38.1,38.1L107,108.928l-32,-1L31,63.928z"> - <aapt:attr name="android:fillColor"> - <gradient - android:endX="85.84757" - android:endY="92.4963" - android:startX="42.9492" - android:startY="49.59793" - android:type="linear"> - <item - android:color="#44000000" - android:offset="0.0" /> - <item - android:color="#00000000" - android:offset="1.0" /> - </gradient> - </aapt:attr> - </path> - <path - android:fillColor="#FFFFFF" - android:fillType="nonZero" - android:pathData="M65.3,45.828l3.8,-6.6c0.2,-0.4 0.1,-0.9 -0.3,-1.1c-0.4,-0.2 -0.9,-0.1 -1.1,0.3l-3.9,6.7c-6.3,-2.8 -13.4,-2.8 -19.7,0l-3.9,-6.7c-0.2,-0.4 -0.7,-0.5 -1.1,-0.3C38.8,38.328 38.7,38.828 38.9,39.228l3.8,6.6C36.2,49.428 31.7,56.028 31,63.928h46C76.3,56.028 71.8,49.428 65.3,45.828zM43.4,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2c-0.3,-0.7 -0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C45.3,56.528 44.5,57.328 43.4,57.328L43.4,57.328zM64.6,57.328c-0.8,0 -1.5,-0.5 -1.8,-1.2s-0.1,-1.5 0.4,-2.1c0.5,-0.5 1.4,-0.7 2.1,-0.4c0.7,0.3 1.2,1 1.2,1.8C66.5,56.528 65.6,57.328 64.6,57.328L64.6,57.328z" - android:strokeColor="#00000000" - android:strokeWidth="1" /> -</vector>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/drawable/ic_launcher_background.xml b/packages/SystemUI/compose/gallery/res/drawable/ic_launcher_background.xml deleted file mode 100644 index 61bb79edb709..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/ic_launcher_background.xml +++ /dev/null @@ -1,170 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<vector xmlns:android="http://schemas.android.com/apk/res/android" - android:width="108dp" - android:height="108dp" - android:viewportHeight="108" - android:viewportWidth="108"> - <path - android:fillColor="#3DDC84" - android:pathData="M0,0h108v108h-108z" /> - <path - android:fillColor="#00000000" - android:pathData="M9,0L9,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,0L19,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M29,0L29,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M39,0L39,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M49,0L49,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M59,0L59,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M69,0L69,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M79,0L79,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M89,0L89,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M99,0L99,108" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,9L108,9" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,19L108,19" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,29L108,29" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,39L108,39" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,49L108,49" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,59L108,59" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,69L108,69" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,79L108,79" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,89L108,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M0,99L108,99" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,29L89,29" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,39L89,39" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,49L89,49" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,59L89,59" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,69L89,69" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M19,79L89,79" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M29,19L29,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M39,19L39,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M49,19L49,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M59,19L59,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M69,19L69,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> - <path - android:fillColor="#00000000" - android:pathData="M79,19L79,89" - android:strokeColor="#33FFFFFF" - android:strokeWidth="0.8" /> -</vector> diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten1.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten1.jpeg Binary files differdeleted file mode 100644 index 6241b0b44bb6..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten1.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten2.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten2.jpeg Binary files differdeleted file mode 100644 index 870ef13ee2d9..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten2.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten3.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten3.jpeg Binary files differdeleted file mode 100644 index bb7261c10033..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten3.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten4.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten4.jpeg Binary files differdeleted file mode 100644 index e34b7ddf58ce..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten4.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten5.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten5.jpeg Binary files differdeleted file mode 100644 index 9cde24be59ef..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten5.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/drawable/kitten6.jpeg b/packages/SystemUI/compose/gallery/res/drawable/kitten6.jpeg Binary files differdeleted file mode 100644 index 17825b639a26..000000000000 --- a/packages/SystemUI/compose/gallery/res/drawable/kitten6.jpeg +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher.xml b/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher.xml deleted file mode 100644 index 03eed2533da2..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background" /> - <foreground android:drawable="@drawable/ic_launcher_foreground" /> -</adaptive-icon>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher_round.xml b/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher_round.xml deleted file mode 100644 index 03eed2533da2..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-anydpi-v26/ic_launcher_round.xml +++ /dev/null @@ -1,5 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<adaptive-icon xmlns:android="http://schemas.android.com/apk/res/android"> - <background android:drawable="@drawable/ic_launcher_background" /> - <foreground android:drawable="@drawable/ic_launcher_foreground" /> -</adaptive-icon>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher.webp b/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher.webp Binary files differdeleted file mode 100644 index c209e78ecd37..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher_round.webp b/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher_round.webp Binary files differdeleted file mode 100644 index b2dfe3d1ba5c..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-hdpi/ic_launcher_round.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher.webp b/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher.webp Binary files differdeleted file mode 100644 index 4f0f1d64e58b..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher_round.webp b/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher_round.webp Binary files differdeleted file mode 100644 index 62b611da0816..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-mdpi/ic_launcher_round.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher.webp b/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher.webp Binary files differdeleted file mode 100644 index 948a3070fe34..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher_round.webp b/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher_round.webp Binary files differdeleted file mode 100644 index 1b9a6956b3ac..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xhdpi/ic_launcher_round.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher.webp b/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher.webp Binary files differdeleted file mode 100644 index 28d4b77f9f03..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher_round.webp b/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher_round.webp Binary files differdeleted file mode 100644 index 9287f5083623..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xxhdpi/ic_launcher_round.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher.webp b/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher.webp Binary files differdeleted file mode 100644 index aa7d6427e6fa..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher_round.webp b/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher_round.webp Binary files differdeleted file mode 100644 index 9126ae37cbc3..000000000000 --- a/packages/SystemUI/compose/gallery/res/mipmap-xxxhdpi/ic_launcher_round.webp +++ /dev/null diff --git a/packages/SystemUI/compose/gallery/res/values/colors.xml b/packages/SystemUI/compose/gallery/res/values/colors.xml deleted file mode 100644 index a2fcbffc26c0..000000000000 --- a/packages/SystemUI/compose/gallery/res/values/colors.xml +++ /dev/null @@ -1,19 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <color name="ic_launcher_background">#FFFFFF</color> -</resources>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/values/strings.xml b/packages/SystemUI/compose/gallery/res/values/strings.xml deleted file mode 100644 index 86bdb0568837..000000000000 --- a/packages/SystemUI/compose/gallery/res/values/strings.xml +++ /dev/null @@ -1,20 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources> - <!-- Application name [CHAR LIMIT=NONE] --> - <string name="app_name">SystemUI Gallery</string> -</resources>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/res/values/themes.xml b/packages/SystemUI/compose/gallery/res/values/themes.xml deleted file mode 100644 index 45fa1f5dfb5c..000000000000 --- a/packages/SystemUI/compose/gallery/res/values/themes.xml +++ /dev/null @@ -1,30 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- - Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> -<resources xmlns:tools="http://schemas.android.com/tools"> - <style name="Theme.SystemUI.Gallery"> - <item name="android:windowActionBar">false</item> - <item name="android:windowNoTitle">true</item> - - <item name="android:statusBarColor" tools:targetApi="l"> - @android:color/transparent - </item> - <item name="android:navigationBarColor" tools:targetApi="l"> - @android:color/transparent - </item> - <item name="android:windowLightStatusBar">true</item> - </style> -</resources> diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt deleted file mode 100644 index 881a1def113a..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ButtonsScreen.kt +++ /dev/null @@ -1,77 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -@file:OptIn(ExperimentalMaterial3Api::class) - -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.layout.Column -import androidx.compose.material3.ExperimentalMaterial3Api -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.android.systemui.compose.SysUiButton -import com.android.systemui.compose.SysUiOutlinedButton -import com.android.systemui.compose.SysUiTextButton - -@Composable -fun ButtonsScreen( - modifier: Modifier = Modifier, -) { - Column( - modifier = modifier, - ) { - SysUiButton( - onClick = {}, - ) { - Text("SysUiButton") - } - - SysUiButton( - onClick = {}, - enabled = false, - ) { - Text("SysUiButton - disabled") - } - - SysUiOutlinedButton( - onClick = {}, - ) { - Text("SysUiOutlinedButton") - } - - SysUiOutlinedButton( - onClick = {}, - enabled = false, - ) { - Text("SysUiOutlinedButton - disabled") - } - - SysUiTextButton( - onClick = {}, - ) { - Text("SysUiTextButton") - } - - SysUiTextButton( - onClick = {}, - enabled = false, - ) { - Text("SysUiTextButton - disabled") - } - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt deleted file mode 100644 index dfa1b26f464e..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ColorsScreen.kt +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.background -import androidx.compose.foundation.border -import androidx.compose.foundation.layout.Row -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.size -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.Color -import androidx.compose.ui.unit.dp -import com.android.systemui.compose.theme.LocalAndroidColorScheme - -/** The screen that shows all the Material 3 colors. */ -@Composable -fun MaterialColorsScreen() { - val colors = MaterialTheme.colorScheme - ColorsScreen( - listOf( - "primary" to colors.primary, - "onPrimary" to colors.onPrimary, - "primaryContainer" to colors.primaryContainer, - "onPrimaryContainer" to colors.onPrimaryContainer, - "inversePrimary" to colors.inversePrimary, - "secondary" to colors.secondary, - "onSecondary" to colors.onSecondary, - "secondaryContainer" to colors.secondaryContainer, - "onSecondaryContainer" to colors.onSecondaryContainer, - "tertiary" to colors.tertiary, - "onTertiary" to colors.onTertiary, - "tertiaryContainer" to colors.tertiaryContainer, - "onTertiaryContainer" to colors.onTertiaryContainer, - "background" to colors.background, - "onBackground" to colors.onBackground, - "surface" to colors.surface, - "onSurface" to colors.onSurface, - "surfaceVariant" to colors.surfaceVariant, - "onSurfaceVariant" to colors.onSurfaceVariant, - "inverseSurface" to colors.inverseSurface, - "inverseOnSurface" to colors.inverseOnSurface, - "error" to colors.error, - "onError" to colors.onError, - "errorContainer" to colors.errorContainer, - "onErrorContainer" to colors.onErrorContainer, - "outline" to colors.outline, - ) - ) -} - -/** The screen that shows all the Android colors. */ -@Composable -fun AndroidColorsScreen() { - val colors = LocalAndroidColorScheme.current - ColorsScreen( - listOf( - "colorPrimary" to colors.colorPrimary, - "colorPrimaryDark" to colors.colorPrimaryDark, - "colorAccent" to colors.colorAccent, - "colorAccentPrimary" to colors.colorAccentPrimary, - "colorAccentSecondary" to colors.colorAccentSecondary, - "colorAccentTertiary" to colors.colorAccentTertiary, - "colorAccentPrimaryVariant" to colors.colorAccentPrimaryVariant, - "colorAccentSecondaryVariant" to colors.colorAccentSecondaryVariant, - "colorAccentTertiaryVariant" to colors.colorAccentTertiaryVariant, - "colorSurface" to colors.colorSurface, - "colorSurfaceHighlight" to colors.colorSurfaceHighlight, - "colorSurfaceVariant" to colors.colorSurfaceVariant, - "colorSurfaceHeader" to colors.colorSurfaceHeader, - "colorError" to colors.colorError, - "colorBackground" to colors.colorBackground, - "colorBackgroundFloating" to colors.colorBackgroundFloating, - "panelColorBackground" to colors.panelColorBackground, - "textColorPrimary" to colors.textColorPrimary, - "textColorSecondary" to colors.textColorSecondary, - "textColorTertiary" to colors.textColorTertiary, - "textColorPrimaryInverse" to colors.textColorPrimaryInverse, - "textColorSecondaryInverse" to colors.textColorSecondaryInverse, - "textColorTertiaryInverse" to colors.textColorTertiaryInverse, - "textColorOnAccent" to colors.textColorOnAccent, - "colorForeground" to colors.colorForeground, - "colorForegroundInverse" to colors.colorForegroundInverse, - ) - ) -} - -@Composable -private fun ColorsScreen( - colors: List<Pair<String, Color>>, -) { - LazyColumn( - Modifier.fillMaxWidth(), - ) { - colors.forEach { (name, color) -> item { ColorTile(color, name) } } - } -} - -@Composable -private fun ColorTile( - color: Color, - name: String, -) { - Row( - Modifier.padding(16.dp), - verticalAlignment = Alignment.CenterVertically, - ) { - val shape = RoundedCornerShape(16.dp) - Spacer( - Modifier.border(1.dp, MaterialTheme.colorScheme.onBackground, shape) - .background(color, shape) - .size(64.dp) - ) - Spacer(Modifier.width(16.dp)) - Text(name) - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt deleted file mode 100644 index 990d060207df..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ConfigurationControls.kt +++ /dev/null @@ -1,210 +0,0 @@ -package com.android.systemui.compose.gallery - -import android.graphics.Point -import android.os.UserHandle -import android.view.Display -import android.view.WindowManagerGlobal -import androidx.compose.foundation.layout.RowScope -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.width -import androidx.compose.foundation.lazy.LazyRow -import androidx.compose.material.icons.Icons -import androidx.compose.material.icons.filled.DarkMode -import androidx.compose.material.icons.filled.FormatSize -import androidx.compose.material.icons.filled.FormatTextdirectionLToR -import androidx.compose.material.icons.filled.FormatTextdirectionRToL -import androidx.compose.material.icons.filled.InvertColors -import androidx.compose.material.icons.filled.LightMode -import androidx.compose.material.icons.filled.Smartphone -import androidx.compose.material.icons.filled.Tablet -import androidx.compose.material3.Button -import androidx.compose.material3.ButtonDefaults -import androidx.compose.material3.Icon -import androidx.compose.material3.Text -import androidx.compose.material3.TextButton -import androidx.compose.runtime.Composable -import androidx.compose.runtime.LaunchedEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.graphics.vector.ImageVector -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.dp -import kotlin.math.max -import kotlin.math.min - -enum class FontScale(val scale: Float) { - Small(0.85f), - Normal(1f), - Big(1.15f), - Bigger(1.30f), -} - -/** A configuration panel that allows to toggle the theme, font scale and layout direction. */ -@Composable -fun ConfigurationControls( - theme: Theme, - fontScale: FontScale, - layoutDirection: LayoutDirection, - onChangeTheme: () -> Unit, - onChangeLayoutDirection: () -> Unit, - onChangeFontScale: () -> Unit, - modifier: Modifier = Modifier, -) { - // The display we are emulating, if any. - var emulatedDisplayName by rememberSaveable { mutableStateOf<String?>(null) } - val emulatedDisplay = - emulatedDisplayName?.let { name -> EmulatedDisplays.firstOrNull { it.name == name } } - - LaunchedEffect(emulatedDisplay) { - val wm = WindowManagerGlobal.getWindowManagerService() - - val defaultDisplayId = Display.DEFAULT_DISPLAY - if (emulatedDisplay == null) { - wm.clearForcedDisplayDensityForUser(defaultDisplayId, UserHandle.myUserId()) - wm.clearForcedDisplaySize(defaultDisplayId) - } else { - val density = emulatedDisplay.densityDpi - - // Emulate the display and make sure that we use the maximum available space possible. - val initialSize = Point() - wm.getInitialDisplaySize(defaultDisplayId, initialSize) - val width = emulatedDisplay.width - val height = emulatedDisplay.height - val minOfSize = min(width, height) - val maxOfSize = max(width, height) - if (initialSize.x < initialSize.y) { - wm.setForcedDisplaySize(defaultDisplayId, minOfSize, maxOfSize) - } else { - wm.setForcedDisplaySize(defaultDisplayId, maxOfSize, minOfSize) - } - wm.setForcedDisplayDensityForUser(defaultDisplayId, density, UserHandle.myUserId()) - } - } - - // TODO(b/231131244): Fork FlowRow from Accompanist and use that instead to make sure that users - // don't miss any available configuration. - LazyRow(modifier) { - // Dark/light theme. - item { - TextButton(onChangeTheme) { - val text: String - val icon: ImageVector - - when (theme) { - Theme.System -> { - icon = Icons.Default.InvertColors - text = "System" - } - Theme.Dark -> { - icon = Icons.Default.DarkMode - text = "Dark" - } - Theme.Light -> { - icon = Icons.Default.LightMode - text = "Light" - } - } - - Icon(icon, null) - Spacer(Modifier.width(8.dp)) - Text(text) - } - } - - // Font scale. - item { - TextButton(onChangeFontScale) { - Icon(Icons.Default.FormatSize, null) - Spacer(Modifier.width(8.dp)) - - Text(fontScale.name) - } - } - - // Layout direction. - item { - TextButton(onChangeLayoutDirection) { - when (layoutDirection) { - LayoutDirection.Ltr -> { - Icon(Icons.Default.FormatTextdirectionLToR, null) - Spacer(Modifier.width(8.dp)) - Text("LTR") - } - LayoutDirection.Rtl -> { - Icon(Icons.Default.FormatTextdirectionRToL, null) - Spacer(Modifier.width(8.dp)) - Text("RTL") - } - } - } - } - - // Display emulation. - EmulatedDisplays.forEach { display -> - item { - DisplayButton( - display, - emulatedDisplay == display, - { emulatedDisplayName = it?.name }, - ) - } - } - } -} - -@Composable -private fun DisplayButton( - display: EmulatedDisplay, - selected: Boolean, - onChangeEmulatedDisplay: (EmulatedDisplay?) -> Unit, -) { - val onClick = { - if (selected) { - onChangeEmulatedDisplay(null) - } else { - onChangeEmulatedDisplay(display) - } - } - - val content: @Composable RowScope.() -> Unit = { - Icon(display.icon, null) - Spacer(Modifier.width(8.dp)) - Text(display.name) - } - - if (selected) { - Button(onClick, contentPadding = ButtonDefaults.TextButtonContentPadding, content = content) - } else { - TextButton(onClick, content = content) - } -} - -/** The displays that can be emulated from this Gallery app. */ -private val EmulatedDisplays = - listOf( - EmulatedDisplay( - "Phone", - Icons.Default.Smartphone, - width = 1440, - height = 3120, - densityDpi = 560, - ), - EmulatedDisplay( - "Tablet", - Icons.Default.Tablet, - width = 2560, - height = 1600, - densityDpi = 320, - ), - ) - -private data class EmulatedDisplay( - val name: String, - val icon: ImageVector, - val width: Int, - val height: Int, - val densityDpi: Int, -) diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ExampleFeatureScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ExampleFeatureScreen.kt deleted file mode 100644 index 6e1721490f98..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/ExampleFeatureScreen.kt +++ /dev/null @@ -1,28 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.layout.Column -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import com.android.systemui.ExampleFeature - -/** The screen that shows ExampleFeature. */ -@Composable -fun ExampleFeatureScreen(modifier: Modifier = Modifier) { - Column(modifier) { ExampleFeature("This is an example feature!") } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt deleted file mode 100644 index bb2d2feba39f..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryActivity.kt +++ /dev/null @@ -1,80 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import android.app.UiModeManager -import android.content.Context -import android.os.Bundle -import androidx.activity.ComponentActivity -import androidx.activity.compose.setContent -import androidx.compose.foundation.isSystemInDarkTheme -import androidx.compose.runtime.SideEffect -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.graphics.Color -import androidx.core.view.WindowCompat -import com.android.systemui.compose.rememberSystemUiController - -class GalleryActivity : ComponentActivity() { - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - WindowCompat.setDecorFitsSystemWindows(window, false) - val uiModeManager = getSystemService(Context.UI_MODE_SERVICE) as UiModeManager - - setContent { - var theme by rememberSaveable { mutableStateOf(Theme.System) } - val onChangeTheme = { - // Change to the next theme for a toggle behavior. - theme = - when (theme) { - Theme.System -> Theme.Dark - Theme.Dark -> Theme.Light - Theme.Light -> Theme.System - } - } - - val isSystemInDarkTheme = isSystemInDarkTheme() - val isDark = theme == Theme.Dark || (theme == Theme.System && isSystemInDarkTheme) - val useDarkIcons = !isDark - val systemUiController = rememberSystemUiController() - SideEffect { - systemUiController.setSystemBarsColor( - color = Color.Transparent, - darkIcons = useDarkIcons, - ) - - uiModeManager.setApplicationNightMode( - when (theme) { - Theme.System -> UiModeManager.MODE_NIGHT_AUTO - Theme.Dark -> UiModeManager.MODE_NIGHT_YES - Theme.Light -> UiModeManager.MODE_NIGHT_NO - } - ) - } - - GalleryApp(theme, onChangeTheme) - } - } -} - -enum class Theme { - System, - Dark, - Light, -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt deleted file mode 100644 index 6805bf83dff4..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/GalleryApp.kt +++ /dev/null @@ -1,202 +0,0 @@ -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.layout.Box -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.Spacer -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.layout.height -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.layout.systemBarsPadding -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.runtime.Composable -import androidx.compose.runtime.CompositionLocalProvider -import androidx.compose.runtime.getValue -import androidx.compose.runtime.mutableStateOf -import androidx.compose.runtime.saveable.rememberSaveable -import androidx.compose.runtime.setValue -import androidx.compose.ui.Modifier -import androidx.compose.ui.platform.LocalContext -import androidx.compose.ui.platform.LocalDensity -import androidx.compose.ui.platform.LocalLayoutDirection -import androidx.compose.ui.unit.Density -import androidx.compose.ui.unit.LayoutDirection -import androidx.compose.ui.unit.dp -import androidx.navigation.compose.NavHost -import androidx.navigation.compose.rememberNavController -import com.android.systemui.compose.theme.SystemUITheme - -/** The gallery app screens. */ -object GalleryAppScreens { - private val Typography = ChildScreen("typography") { TypographyScreen() } - private val MaterialColors = ChildScreen("material_colors") { MaterialColorsScreen() } - private val AndroidColors = ChildScreen("android_colors") { AndroidColorsScreen() } - private val Buttons = ChildScreen("buttons") { ButtonsScreen() } - private val ExampleFeature = ChildScreen("example_feature") { ExampleFeatureScreen() } - - private val PeopleEmpty = - ChildScreen("people_empty") { navController -> - EmptyPeopleScreen(onResult = { navController.popBackStack() }) - } - private val PeopleFew = - ChildScreen("people_few") { navController -> - FewPeopleScreen(onResult = { navController.popBackStack() }) - } - private val PeopleFull = - ChildScreen("people_full") { navController -> - FullPeopleScreen(onResult = { navController.popBackStack() }) - } - private val People = - ParentScreen( - "people", - mapOf( - "Empty" to PeopleEmpty, - "Few" to PeopleFew, - "Full" to PeopleFull, - ) - ) - private val UserSwitcherSingleUser = - ChildScreen("user_switcher_single") { navController -> - UserSwitcherScreen( - userCount = 1, - onFinished = navController::popBackStack, - ) - } - private val UserSwitcherThreeUsers = - ChildScreen("user_switcher_three") { navController -> - UserSwitcherScreen( - userCount = 3, - onFinished = navController::popBackStack, - ) - } - private val UserSwitcherFourUsers = - ChildScreen("user_switcher_four") { navController -> - UserSwitcherScreen( - userCount = 4, - onFinished = navController::popBackStack, - ) - } - private val UserSwitcherFiveUsers = - ChildScreen("user_switcher_five") { navController -> - UserSwitcherScreen( - userCount = 5, - onFinished = navController::popBackStack, - ) - } - private val UserSwitcherSixUsers = - ChildScreen("user_switcher_six") { navController -> - UserSwitcherScreen( - userCount = 6, - onFinished = navController::popBackStack, - ) - } - private val UserSwitcher = - ParentScreen( - "user_switcher", - mapOf( - "Single" to UserSwitcherSingleUser, - "Three" to UserSwitcherThreeUsers, - "Four" to UserSwitcherFourUsers, - "Five" to UserSwitcherFiveUsers, - "Six" to UserSwitcherSixUsers, - ) - ) - - val Home = - ParentScreen( - "home", - mapOf( - "Typography" to Typography, - "Material colors" to MaterialColors, - "Android colors" to AndroidColors, - "Example feature" to ExampleFeature, - "Buttons" to Buttons, - "People" to People, - "User Switcher" to UserSwitcher, - ) - ) -} - -/** The main content of the app, that shows [GalleryAppScreens.Home] by default. */ -@Composable -private fun MainContent(onControlToggleRequested: () -> Unit) { - Box(Modifier.fillMaxSize()) { - val navController = rememberNavController() - NavHost( - navController = navController, - startDestination = GalleryAppScreens.Home.identifier, - ) { - screen(GalleryAppScreens.Home, navController, onControlToggleRequested) - } - } -} - -/** - * The top-level composable shown when starting the app. This composable always shows a - * [ConfigurationControls] at the top of the screen, above the [MainContent]. - */ -@Composable -fun GalleryApp( - theme: Theme, - onChangeTheme: () -> Unit, -) { - val systemFontScale = LocalDensity.current.fontScale - var fontScale: FontScale by rememberSaveable { - mutableStateOf( - FontScale.values().firstOrNull { it.scale == systemFontScale } ?: FontScale.Normal - ) - } - val context = LocalContext.current - val density = Density(context.resources.displayMetrics.density, fontScale.scale) - val onChangeFontScale = { - fontScale = - when (fontScale) { - FontScale.Small -> FontScale.Normal - FontScale.Normal -> FontScale.Big - FontScale.Big -> FontScale.Bigger - FontScale.Bigger -> FontScale.Small - } - } - - val systemLayoutDirection = LocalLayoutDirection.current - var layoutDirection by rememberSaveable { mutableStateOf(systemLayoutDirection) } - val onChangeLayoutDirection = { - layoutDirection = - when (layoutDirection) { - LayoutDirection.Ltr -> LayoutDirection.Rtl - LayoutDirection.Rtl -> LayoutDirection.Ltr - } - } - - CompositionLocalProvider( - LocalDensity provides density, - LocalLayoutDirection provides layoutDirection, - ) { - SystemUITheme { - Surface( - Modifier.fillMaxSize(), - color = MaterialTheme.colorScheme.background, - ) { - Column(Modifier.fillMaxSize().systemBarsPadding()) { - var showControls by rememberSaveable { mutableStateOf(true) } - - if (showControls) { - ConfigurationControls( - theme, - fontScale, - layoutDirection, - onChangeTheme, - onChangeLayoutDirection, - onChangeFontScale, - Modifier.padding(horizontal = 16.dp), - ) - - Spacer(Modifier.height(4.dp)) - } - - MainContent(onControlToggleRequested = { showControls = !showControls }) - } - } - } - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/PeopleScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/PeopleScreen.kt deleted file mode 100644 index 2f0df7790ffd..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/PeopleScreen.kt +++ /dev/null @@ -1,46 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext -import com.android.systemui.people.emptyPeopleSpaceViewModel -import com.android.systemui.people.fewPeopleSpaceViewModel -import com.android.systemui.people.fullPeopleSpaceViewModel -import com.android.systemui.people.ui.compose.PeopleScreen -import com.android.systemui.people.ui.viewmodel.PeopleViewModel - -@Composable -fun EmptyPeopleScreen(onResult: (PeopleViewModel.Result) -> Unit) { - val context = LocalContext.current.applicationContext - val viewModel = emptyPeopleSpaceViewModel(context) - PeopleScreen(viewModel, onResult) -} - -@Composable -fun FewPeopleScreen(onResult: (PeopleViewModel.Result) -> Unit) { - val context = LocalContext.current.applicationContext - val viewModel = fewPeopleSpaceViewModel(context) - PeopleScreen(viewModel, onResult) -} - -@Composable -fun FullPeopleScreen(onResult: (PeopleViewModel.Result) -> Unit) { - val context = LocalContext.current.applicationContext - val viewModel = fullPeopleSpaceViewModel(context) - PeopleScreen(viewModel, onResult) -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt deleted file mode 100644 index d7d0d721b01c..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/Screen.kt +++ /dev/null @@ -1,126 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.clickable -import androidx.compose.foundation.layout.Arrangement -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxWidth -import androidx.compose.foundation.layout.padding -import androidx.compose.foundation.lazy.LazyColumn -import androidx.compose.foundation.shape.CircleShape -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Surface -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Alignment -import androidx.compose.ui.Modifier -import androidx.compose.ui.unit.dp -import androidx.navigation.NavController -import androidx.navigation.NavGraphBuilder -import androidx.navigation.compose.composable -import androidx.navigation.compose.navigation - -/** - * A screen in an app. It is either an [ParentScreen] which lists its child screens to navigate to - * them or a [ChildScreen] which shows some content. - */ -sealed class Screen(val identifier: String) - -class ParentScreen( - identifier: String, - val children: Map<String, Screen>, -) : Screen(identifier) - -class ChildScreen( - identifier: String, - val content: @Composable (NavController) -> Unit, -) : Screen(identifier) - -/** Create the navigation graph for [screen]. */ -fun NavGraphBuilder.screen( - screen: Screen, - navController: NavController, - onControlToggleRequested: () -> Unit, -) { - when (screen) { - is ChildScreen -> composable(screen.identifier) { screen.content(navController) } - is ParentScreen -> { - val menuRoute = "${screen.identifier}_menu" - navigation(startDestination = menuRoute, route = screen.identifier) { - // The menu to navigate to one of the children screens. - composable(menuRoute) { - ScreenMenu(screen, navController, onControlToggleRequested) - } - - // The content of the child screens. - screen.children.forEach { (_, child) -> - screen( - child, - navController, - onControlToggleRequested, - ) - } - } - } - } -} - -@Composable -private fun ScreenMenu( - screen: ParentScreen, - navController: NavController, - onControlToggleRequested: () -> Unit, -) { - LazyColumn( - Modifier.padding(horizontal = 16.dp), - verticalArrangement = Arrangement.spacedBy(8.dp), - ) { - item { - Surface( - Modifier.fillMaxWidth(), - color = MaterialTheme.colorScheme.tertiaryContainer, - shape = CircleShape, - ) { - Column( - Modifier.clickable(onClick = onControlToggleRequested).padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text("Toggle controls") - } - } - } - - screen.children.forEach { (name, child) -> - item { - Surface( - Modifier.fillMaxWidth(), - color = MaterialTheme.colorScheme.secondaryContainer, - shape = CircleShape, - ) { - Column( - Modifier.clickable { navController.navigate(child.identifier) } - .padding(16.dp), - horizontalAlignment = Alignment.CenterHorizontally, - ) { - Text(name) - } - } - } - } - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/TypographyScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/TypographyScreen.kt deleted file mode 100644 index 147025ed1d60..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/TypographyScreen.kt +++ /dev/null @@ -1,67 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.foundation.horizontalScroll -import androidx.compose.foundation.layout.Column -import androidx.compose.foundation.layout.fillMaxSize -import androidx.compose.foundation.rememberScrollState -import androidx.compose.foundation.verticalScroll -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text -import androidx.compose.runtime.Composable -import androidx.compose.ui.Modifier -import androidx.compose.ui.text.TextStyle -import androidx.compose.ui.text.style.TextOverflow - -/** The screen that shows the Material text styles. */ -@Composable -fun TypographyScreen() { - val typography = MaterialTheme.typography - - Column( - Modifier.fillMaxSize() - .horizontalScroll(rememberScrollState()) - .verticalScroll(rememberScrollState()), - ) { - FontLine("displayLarge", typography.displayLarge) - FontLine("displayMedium", typography.displayMedium) - FontLine("displaySmall", typography.displaySmall) - FontLine("headlineLarge", typography.headlineLarge) - FontLine("headlineMedium", typography.headlineMedium) - FontLine("headlineSmall", typography.headlineSmall) - FontLine("titleLarge", typography.titleLarge) - FontLine("titleMedium", typography.titleMedium) - FontLine("titleSmall", typography.titleSmall) - FontLine("bodyLarge", typography.bodyLarge) - FontLine("bodyMedium", typography.bodyMedium) - FontLine("bodySmall", typography.bodySmall) - FontLine("labelLarge", typography.labelLarge) - FontLine("labelMedium", typography.labelMedium) - FontLine("labelSmall", typography.labelSmall) - } -} - -@Composable -private fun FontLine(name: String, style: TextStyle) { - Text( - "$name (${style.fontSize}/${style.lineHeight}, W${style.fontWeight?.weight})", - style = style, - maxLines = 1, - overflow = TextOverflow.Visible, - ) -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/UserSwitcherScreen.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/UserSwitcherScreen.kt deleted file mode 100644 index fe9707d22684..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/compose/gallery/UserSwitcherScreen.kt +++ /dev/null @@ -1,35 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.runtime.Composable -import androidx.compose.ui.platform.LocalContext -import com.android.systemui.user.Fakes.fakeUserSwitcherViewModel -import com.android.systemui.user.ui.compose.UserSwitcherScreen - -@Composable -fun UserSwitcherScreen( - userCount: Int, - onFinished: () -> Unit, -) { - val context = LocalContext.current.applicationContext - UserSwitcherScreen( - viewModel = fakeUserSwitcherViewModel(context, userCount = userCount), - onFinished = onFinished, - ) -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/people/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/people/Fakes.kt deleted file mode 100644 index 0966c3233ad5..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/people/Fakes.kt +++ /dev/null @@ -1,156 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.people - -import android.content.Context -import android.graphics.Bitmap -import android.graphics.Canvas -import android.graphics.Color -import android.graphics.Paint -import android.graphics.drawable.Icon -import androidx.core.graphics.drawable.toIcon -import com.android.systemui.R -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.people.data.model.PeopleTileModel -import com.android.systemui.people.ui.viewmodel.PeopleViewModel -import com.android.systemui.people.widget.PeopleTileKey - -/** A [PeopleViewModel] that does not have any conversations. */ -fun emptyPeopleSpaceViewModel(@Application context: Context): PeopleViewModel { - return fakePeopleSpaceViewModel(context, emptyList(), emptyList()) -} - -/** A [PeopleViewModel] that has a few conversations. */ -fun fewPeopleSpaceViewModel(@Application context: Context): PeopleViewModel { - return fakePeopleSpaceViewModel( - context, - priorityTiles = - listOf( - fakeTile(context, id = "0", Color.RED, "Priority"), - fakeTile(context, id = "1", Color.BLUE, "Priority NewStory", hasNewStory = true), - ), - recentTiles = - listOf( - fakeTile(context, id = "2", Color.GREEN, "Recent Important", isImportant = true), - fakeTile(context, id = "3", Color.CYAN, "Recent DndBlocking", isDndBlocking = true), - ), - ) -} - -/** A [PeopleViewModel] that has a lot of conversations. */ -fun fullPeopleSpaceViewModel(@Application context: Context): PeopleViewModel { - return fakePeopleSpaceViewModel( - context, - priorityTiles = - listOf( - fakeTile(context, id = "0", Color.RED, "Priority"), - fakeTile(context, id = "1", Color.BLUE, "Priority NewStory", hasNewStory = true), - fakeTile(context, id = "2", Color.GREEN, "Priority Important", isImportant = true), - fakeTile( - context, - id = "3", - Color.CYAN, - "Priority DndBlocking", - isDndBlocking = true, - ), - fakeTile( - context, - id = "4", - Color.MAGENTA, - "Priority NewStory Important", - hasNewStory = true, - isImportant = true, - ), - ), - recentTiles = - listOf( - fakeTile( - context, - id = "5", - Color.RED, - "Recent NewStory DndBlocking", - hasNewStory = true, - isDndBlocking = true, - ), - fakeTile( - context, - id = "6", - Color.BLUE, - "Recent Important DndBlocking", - isImportant = true, - isDndBlocking = true, - ), - fakeTile( - context, - id = "7", - Color.GREEN, - "Recent NewStory Important DndBlocking", - hasNewStory = true, - isImportant = true, - isDndBlocking = true, - ), - fakeTile(context, id = "8", Color.CYAN, "Recent"), - fakeTile(context, id = "9", Color.MAGENTA, "Recent"), - ), - ) -} - -private fun fakePeopleSpaceViewModel( - @Application context: Context, - priorityTiles: List<PeopleTileModel>, - recentTiles: List<PeopleTileModel>, -): PeopleViewModel { - return PeopleViewModel( - context, - FakePeopleTileRepository(priorityTiles, recentTiles), - FakePeopleWidgetRepository(), - ) -} - -private fun fakeTile( - @Application context: Context, - id: String, - iconColor: Int, - username: String, - hasNewStory: Boolean = false, - isImportant: Boolean = false, - isDndBlocking: Boolean = false -): PeopleTileModel { - return PeopleTileModel( - PeopleTileKey(id, /* userId= */ 0, /* packageName */ ""), - username, - fakeUserIcon(context, iconColor), - hasNewStory, - isImportant, - isDndBlocking, - ) -} - -private fun fakeUserIcon(@Application context: Context, color: Int): Icon { - val size = context.resources.getDimensionPixelSize(R.dimen.avatar_size_for_medium) - val bitmap = - Bitmap.createBitmap( - size, - size, - Bitmap.Config.ARGB_8888, - ) - val canvas = Canvas(bitmap) - val paint = Paint().apply { this.color = color } - val radius = size / 2f - canvas.drawCircle(/* cx= */ radius, /* cy= */ radius, /* radius= */ radius, paint) - return bitmap.toIcon() -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt deleted file mode 100644 index 6588e22721fb..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/qs/footer/Fakes.kt +++ /dev/null @@ -1,164 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.qs.footer - -import android.content.Context -import android.os.UserHandle -import android.view.View -import com.android.internal.util.UserIcons -import com.android.systemui.R -import com.android.systemui.animation.Expandable -import com.android.systemui.classifier.FalsingManagerFake -import com.android.systemui.common.shared.model.Icon -import com.android.systemui.dagger.qualifiers.Application -import com.android.systemui.globalactions.GlobalActionsDialogLite -import com.android.systemui.qs.footer.data.model.UserSwitcherStatusModel -import com.android.systemui.qs.footer.domain.interactor.FooterActionsInteractor -import com.android.systemui.qs.footer.domain.model.SecurityButtonConfig -import com.android.systemui.qs.footer.ui.viewmodel.FooterActionsViewModel -import com.android.systemui.util.mockito.mock -import kotlinx.coroutines.flow.Flow -import kotlinx.coroutines.flow.flowOf - -/** A list of fake [FooterActionsViewModel] to be used in screenshot tests and the gallery. */ -fun fakeFooterActionsViewModels( - @Application context: Context, -): List<FooterActionsViewModel> { - return listOf( - fakeFooterActionsViewModel(context), - fakeFooterActionsViewModel(context, showPowerButton = false, isGuestUser = true), - fakeFooterActionsViewModel(context, showUserSwitcher = false), - fakeFooterActionsViewModel(context, showUserSwitcher = false, foregroundServices = 4), - fakeFooterActionsViewModel( - context, - foregroundServices = 4, - hasNewForegroundServices = true, - userId = 1, - ), - fakeFooterActionsViewModel( - context, - securityText = "Security", - foregroundServices = 4, - showUserSwitcher = false, - ), - fakeFooterActionsViewModel( - context, - securityText = "Security (not clickable)", - securityClickable = false, - foregroundServices = 4, - hasNewForegroundServices = true, - userId = 2, - ), - ) -} - -private fun fakeFooterActionsViewModel( - @Application context: Context, - securityText: String? = null, - securityClickable: Boolean = true, - foregroundServices: Int = 0, - hasNewForegroundServices: Boolean = false, - showUserSwitcher: Boolean = true, - showPowerButton: Boolean = true, - userId: Int = UserHandle.USER_OWNER, - isGuestUser: Boolean = false, -): FooterActionsViewModel { - val interactor = - FakeFooterActionsInteractor( - securityButtonConfig = - flowOf( - securityText?.let { text -> - SecurityButtonConfig( - icon = - Icon.Resource( - R.drawable.ic_info_outline, - contentDescription = null, - ), - text = text, - isClickable = securityClickable, - ) - } - ), - foregroundServicesCount = flowOf(foregroundServices), - hasNewForegroundServices = flowOf(hasNewForegroundServices), - userSwitcherStatus = - flowOf( - if (showUserSwitcher) { - UserSwitcherStatusModel.Enabled( - currentUserName = "foo", - currentUserImage = - UserIcons.getDefaultUserIcon( - context.resources, - userId, - /* light= */ false, - ), - isGuestUser = isGuestUser, - ) - } else { - UserSwitcherStatusModel.Disabled - } - ), - deviceMonitoringDialogRequests = flowOf(), - ) - - return FooterActionsViewModel( - context, - interactor, - FalsingManagerFake(), - globalActionsDialogLite = mock(), - showPowerButton = showPowerButton, - ) -} - -private class FakeFooterActionsInteractor( - override val securityButtonConfig: Flow<SecurityButtonConfig?> = flowOf(null), - override val foregroundServicesCount: Flow<Int> = flowOf(0), - override val hasNewForegroundServices: Flow<Boolean> = flowOf(false), - override val userSwitcherStatus: Flow<UserSwitcherStatusModel> = - flowOf(UserSwitcherStatusModel.Disabled), - override val deviceMonitoringDialogRequests: Flow<Unit> = flowOf(), - private val onShowDeviceMonitoringDialogFromView: (View) -> Unit = {}, - private val onShowDeviceMonitoringDialog: (Context) -> Unit = {}, - private val onShowForegroundServicesDialog: (View) -> Unit = {}, - private val onShowPowerMenuDialog: (GlobalActionsDialogLite, View) -> Unit = { _, _ -> }, - private val onShowSettings: (Expandable) -> Unit = {}, - private val onShowUserSwitcher: (View) -> Unit = {}, -) : FooterActionsInteractor { - override fun showDeviceMonitoringDialog(view: View) { - onShowDeviceMonitoringDialogFromView(view) - } - - override fun showDeviceMonitoringDialog(quickSettingsContext: Context) { - onShowDeviceMonitoringDialog(quickSettingsContext) - } - - override fun showForegroundServicesDialog(view: View) { - onShowForegroundServicesDialog(view) - } - - override fun showPowerMenuDialog(globalActionsDialogLite: GlobalActionsDialogLite, view: View) { - onShowPowerMenuDialog(globalActionsDialogLite, view) - } - - override fun showSettings(expandable: Expandable) { - onShowSettings(expandable) - } - - override fun showUserSwitcher(view: View) { - onShowUserSwitcher(view) - } -} diff --git a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt b/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt deleted file mode 100644 index 91a73ea16dc4..000000000000 --- a/packages/SystemUI/compose/gallery/src/com/android/systemui/user/Fakes.kt +++ /dev/null @@ -1,116 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - * - */ - -package com.android.systemui.user - -import android.content.Context -import androidx.appcompat.content.res.AppCompatResources -import com.android.systemui.common.shared.model.Text -import com.android.systemui.compose.gallery.R -import com.android.systemui.keyguard.data.repository.FakeKeyguardRepository -import com.android.systemui.keyguard.domain.interactor.KeyguardInteractor -import com.android.systemui.power.data.repository.FakePowerRepository -import com.android.systemui.power.domain.interactor.PowerInteractor -import com.android.systemui.user.data.repository.FakeUserRepository -import com.android.systemui.user.domain.interactor.UserInteractor -import com.android.systemui.user.shared.model.UserActionModel -import com.android.systemui.user.shared.model.UserModel -import com.android.systemui.user.ui.viewmodel.UserSwitcherViewModel -import com.android.systemui.util.mockito.mock - -object Fakes { - private val USER_TINT_COLORS = - arrayOf( - 0x000000, - 0x0000ff, - 0x00ff00, - 0x00ffff, - 0xff0000, - 0xff00ff, - 0xffff00, - 0xffffff, - ) - - fun fakeUserSwitcherViewModel( - context: Context, - userCount: Int, - ): UserSwitcherViewModel { - return UserSwitcherViewModel.Factory( - userInteractor = - UserInteractor( - repository = - FakeUserRepository().apply { - setUsers( - (0 until userCount).map { index -> - UserModel( - id = index, - name = - Text.Loaded( - when (index % 6) { - 0 -> "Ross Geller" - 1 -> "Phoebe Buffay" - 2 -> "Monica Geller" - 3 -> "Rachel Greene" - 4 -> "Chandler Bing" - else -> "Joey Tribbiani" - } - ), - image = - checkNotNull( - AppCompatResources.getDrawable( - context, - when (index % 6) { - 0 -> R.drawable.kitten1 - 1 -> R.drawable.kitten2 - 2 -> R.drawable.kitten3 - 3 -> R.drawable.kitten4 - 4 -> R.drawable.kitten5 - else -> R.drawable.kitten6 - }, - ) - ), - isSelected = index == 0, - isSelectable = true, - ) - } - ) - setActions( - UserActionModel.values().mapNotNull { - if (it == UserActionModel.NAVIGATE_TO_USER_MANAGEMENT) { - null - } else { - it - } - } - ) - }, - controller = mock(), - activityStarter = mock(), - keyguardInteractor = - KeyguardInteractor( - repository = - FakeKeyguardRepository().apply { setKeyguardShowing(false) }, - ), - ), - powerInteractor = - PowerInteractor( - repository = FakePowerRepository(), - ) - ) - .create(UserSwitcherViewModel::class.java) - } -} diff --git a/packages/SystemUI/compose/gallery/tests/Android.bp b/packages/SystemUI/compose/gallery/tests/Android.bp deleted file mode 100644 index 3e01f7d2c431..000000000000 --- a/packages/SystemUI/compose/gallery/tests/Android.bp +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright (C) 2022 The Android Open Source Project -// -// Licensed under the Apache License, Version 2.0 (the "License"); -// you may not use this file except in compliance with the License. -// You may obtain a copy of the License at -// -// http://www.apache.org/licenses/LICENSE-2.0 -// -// Unless required by applicable law or agreed to in writing, software -// distributed under the License is distributed on an "AS IS" BASIS, -// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -// See the License for the specific language governing permissions and -// limitations under the License. - -package { - // See: http://go/android-license-faq - // A large-scale-change added 'default_applicable_licenses' to import - // all of the 'license_kinds' from "frameworks_base_packages_SystemUI_license" - // to get the below license kinds: - // SPDX-license-identifier-Apache-2.0 - default_applicable_licenses: ["frameworks_base_packages_SystemUI_license"], -} - -android_test { - name: "SystemUIComposeGalleryTests", - manifest: "AndroidManifest.xml", - test_suites: ["device-tests"], - sdk_version: "current", - certificate: "platform", - - srcs: [ - "src/**/*.kt", - ], - - static_libs: [ - "SystemUIComposeGalleryLib", - - "androidx.test.runner", - "androidx.test.ext.junit", - - "androidx.compose.runtime_runtime", - "androidx.compose.ui_ui-test-junit4", - "androidx.compose.ui_ui-test-manifest", - ], - - kotlincflags: ["-Xjvm-default=enable"], -} diff --git a/packages/SystemUI/compose/gallery/tests/AndroidManifest.xml b/packages/SystemUI/compose/gallery/tests/AndroidManifest.xml deleted file mode 100644 index 5eeb3ad24e5a..000000000000 --- a/packages/SystemUI/compose/gallery/tests/AndroidManifest.xml +++ /dev/null @@ -1,28 +0,0 @@ -<?xml version="1.0" encoding="utf-8"?> -<!-- Copyright (C) 2022 The Android Open Source Project - - Licensed under the Apache License, Version 2.0 (the "License"); - you may not use this file except in compliance with the License. - You may obtain a copy of the License at - - http://www.apache.org/licenses/LICENSE-2.0 - - Unless required by applicable law or agreed to in writing, software - distributed under the License is distributed on an "AS IS" BASIS, - WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - See the License for the specific language governing permissions and - limitations under the License. ---> - -<manifest xmlns:android="http://schemas.android.com/apk/res/android" - package="com.android.systemui.compose.gallery.tests" > - - <application> - <uses-library android:name="android.test.runner" /> - </application> - - <instrumentation android:name="androidx.test.runner.AndroidJUnitRunner" - android:targetPackage="com.android.systemui.compose.gallery.tests" - android:label="Tests for SystemUIComposeGallery"/> - -</manifest>
\ No newline at end of file diff --git a/packages/SystemUI/compose/gallery/tests/src/com/android/systemui/compose/gallery/ScreenshotsTests.kt b/packages/SystemUI/compose/gallery/tests/src/com/android/systemui/compose/gallery/ScreenshotsTests.kt deleted file mode 100644 index 66ecc8d4fde5..000000000000 --- a/packages/SystemUI/compose/gallery/tests/src/com/android/systemui/compose/gallery/ScreenshotsTests.kt +++ /dev/null @@ -1,36 +0,0 @@ -/* - * Copyright (C) 2022 The Android Open Source Project - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ - -package com.android.systemui.compose.gallery - -import androidx.compose.ui.test.junit4.createComposeRule -import androidx.test.ext.junit.runners.AndroidJUnit4 -import com.android.systemui.compose.theme.SystemUITheme -import org.junit.Rule -import org.junit.Test -import org.junit.runner.RunWith - -@RunWith(AndroidJUnit4::class) -class ScreenshotsTests { - @get:Rule val composeRule = createComposeRule() - - @Test - fun exampleFeatureScreenshotTest() { - // TODO(b/230832101): Wire this with the screenshot diff testing infra. We should reuse the - // configuration of the features in the gallery app to populate the UIs. - composeRule.setContent { SystemUITheme { ExampleFeatureScreen() } } - } -} |