Skip to content

[All] Update to Compose 1.0.0-beta03 #444

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Mar 24, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.unit.dp
import dev.chrisbanes.accompanist.coil.CoilImage
import com.google.accompanist.coil.CoilImage

@Composable
fun ExploreSection(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ object Versions {
}

object Libs {
const val androidGradlePlugin = "com.android.tools.build:gradle:7.0.0-alpha09"
const val androidGradlePlugin = "com.android.tools.build:gradle:7.0.0-alpha10"
const val ktLint = "com.pinterest:ktlint:${Versions.ktLint}"

object GoogleMaps {
Expand All @@ -30,8 +30,8 @@ object Libs {
}

object Accompanist {
private const val version = "0.6.2"
const val coil = "dev.chrisbanes.accompanist:accompanist-coil:$version"
private const val version = "0.7.0"
const val coil = "com.google.accompanist:accompanist-coil:$version"
}

object Kotlin {
Expand All @@ -49,14 +49,14 @@ object Libs {

object AndroidX {
object Activity {
const val activityCompose = "androidx.activity:activity-compose:1.3.0-alpha04"
const val activityCompose = "androidx.activity:activity-compose:1.3.0-alpha05"
}

const val appcompat = "androidx.appcompat:appcompat:1.3.0-beta01"

object Compose {
const val snapshot = ""
private const val version = "1.0.0-beta02"
private const val version = "1.0.0-beta03"

const val runtime = "androidx.compose.runtime:runtime:$version"
const val runtimeLivedata = "androidx.compose.runtime:runtime-livedata:$version"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ private val DarkThemeColors = darkColors(
primaryVariant = Red700,
onPrimary = Color.Black,
secondary = Red300,
onSecondary = Color.White,
onSecondary = Color.Black,
error = Red200
)

Expand Down
4 changes: 2 additions & 2 deletions JetNews/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@

buildscript {
ext.kotlin_version = '1.4.31'
ext.compose_version = '1.0.0-beta02'
ext.compose_version = '1.0.0-beta03'
ext.coroutines_version = '1.4.2'

repositories {
Expand All @@ -25,7 +25,7 @@ buildscript {
}

dependencies {
classpath 'com.android.tools.build:gradle:7.0.0-alpha09'
classpath 'com.android.tools.build:gradle:7.0.0-alpha10'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
}
}
Expand Down
2 changes: 1 addition & 1 deletion Jetcaster/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ dependencies {
implementation Libs.AndroidX.Lifecycle.viewModelCompose

implementation Libs.Accompanist.coil

implementation Libs.Accompanist.pager
implementation Libs.Accompanist.insets

implementation Libs.OkHttp.okhttp
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ import androidx.activity.ComponentActivity
import androidx.activity.compose.setContent
import androidx.core.view.WindowCompat
import com.example.jetcaster.ui.theme.JetcasterTheme
import dev.chrisbanes.accompanist.insets.ProvideWindowInsets
import com.google.accompanist.insets.ProvideWindowInsets

class MainActivity : ComponentActivity() {
override fun onCreate(savedInstanceState: Bundle?) {
Expand Down
24 changes: 12 additions & 12 deletions Jetcaster/app/src/main/java/com/example/jetcaster/ui/home/Home.kt
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,6 @@ import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
Expand All @@ -69,15 +68,17 @@ import com.example.jetcaster.ui.home.discover.Discover
import com.example.jetcaster.ui.theme.JetcasterTheme
import com.example.jetcaster.ui.theme.Keyline1
import com.example.jetcaster.util.DynamicThemePrimaryColorsFromImage
import com.example.jetcaster.util.Pager
import com.example.jetcaster.util.PagerState
import com.example.jetcaster.util.ToggleFollowPodcastIconButton
import com.example.jetcaster.util.constrastAgainst
import com.example.jetcaster.util.quantityStringResource
import com.example.jetcaster.util.rememberDominantColorState
import com.example.jetcaster.util.verticalGradientScrim
import dev.chrisbanes.accompanist.coil.CoilImage
import dev.chrisbanes.accompanist.insets.statusBarsHeight
import com.google.accompanist.coil.CoilImage
import com.google.accompanist.insets.statusBarsHeight
import com.google.accompanist.pager.ExperimentalPagerApi
import com.google.accompanist.pager.HorizontalPager
import com.google.accompanist.pager.PagerState
import com.google.accompanist.pager.rememberPagerState
import java.time.Duration
import java.time.LocalDateTime
import java.time.OffsetDateTime
Expand Down Expand Up @@ -154,6 +155,7 @@ fun HomeAppBar(
*/
private const val MinConstastOfPrimaryVsSurface = 3f

@OptIn(ExperimentalPagerApi::class) // HorizontalPager is experimental
@Composable
fun HomeContent(
featuredPodcasts: List<PodcastWithExtraInfo>,
Expand All @@ -175,8 +177,7 @@ fun HomeContent(
}

DynamicThemePrimaryColorsFromImage(dominantColorState) {

val pagerState = remember { PagerState() }
val pagerState = rememberPagerState(pageCount = featuredPodcasts.size)

val selectedImageUrl = featuredPodcasts.getOrNull(pagerState.currentPage)
?.podcast?.imageUrl
Expand Down Expand Up @@ -309,19 +310,18 @@ fun HomeCategoryTabIndicator(
)
}

@ExperimentalPagerApi // HorizontalPager is experimental
@Composable
fun FollowedPodcasts(
items: List<PodcastWithExtraInfo>,
pagerState: PagerState,
modifier: Modifier = Modifier,
pagerState: PagerState = remember { PagerState() },
onPodcastUnfollowed: (String) -> Unit,
) {
pagerState.maxPage = (items.size - 1).coerceAtLeast(0)

Pager(
HorizontalPager(
state = pagerState,
modifier = modifier
) {
) { page ->
val (podcast, lastEpisodeDate) = items[page]
FollowedPodcastCarouselItem(
podcastImageUrl = podcast.imageUrl,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -74,7 +74,7 @@ import com.example.jetcaster.ui.theme.JetcasterTheme
import com.example.jetcaster.ui.theme.Keyline1
import com.example.jetcaster.util.ToggleFollowPodcastIconButton
import com.example.jetcaster.util.viewModelProviderFactoryOf
import dev.chrisbanes.accompanist.coil.CoilImage
import com.google.accompanist.coil.CoilImage
import java.time.format.DateTimeFormatter
import java.time.format.FormatStyle

Expand Down
210 changes: 1 addition & 209 deletions Jetcaster/app/src/main/java/com/example/jetcaster/util/Pager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -14,216 +14,8 @@
* limitations under the License.
*/

@file:Suppress("unused")

package com.example.jetcaster.util

import androidx.compose.animation.core.Animatable
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.gestures.draggable
import androidx.compose.foundation.gestures.rememberDraggableState
import androidx.compose.foundation.layout.Box
import androidx.compose.runtime.Composable
import androidx.compose.runtime.Immutable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.key
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.setValue
import androidx.compose.runtime.structuralEqualityPolicy
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.layout.Layout
import androidx.compose.ui.layout.Measurable
import androidx.compose.ui.layout.ParentDataModifier
import androidx.compose.ui.unit.Density
import kotlinx.coroutines.launch
import kotlin.math.roundToInt

/**
* This is a modified version of:
* https://gist.github.com/adamp/07d468f4bcfe632670f305ce3734f511
* Pager is now a library! https://google.github.io/accompanist/pager/
*/

class PagerState(
currentPage: Int = 0,
minPage: Int = 0,
maxPage: Int = 0
) {
private var _minPage by mutableStateOf(minPage)
var minPage: Int
get() = _minPage
set(value) {
_minPage = value.coerceAtMost(_maxPage)
_currentPage = _currentPage.coerceIn(_minPage, _maxPage)
}

private var _maxPage by mutableStateOf(maxPage, structuralEqualityPolicy())
var maxPage: Int
get() = _maxPage
set(value) {
_maxPage = value.coerceAtLeast(_minPage)
_currentPage = _currentPage.coerceIn(_minPage, maxPage)
}

private var _currentPage by mutableStateOf(currentPage.coerceIn(minPage, maxPage))
var currentPage: Int
get() = _currentPage
set(value) {
_currentPage = value.coerceIn(minPage, maxPage)
}

enum class SelectionState { Selected, Undecided }

var selectionState by mutableStateOf(SelectionState.Selected)

suspend inline fun <R> selectPage(block: PagerState.() -> R): R = try {
selectionState = SelectionState.Undecided
block()
} finally {
selectPage()
}

suspend fun selectPage() {
currentPage -= currentPageOffset.roundToInt()
snapToOffset(0f)
selectionState = SelectionState.Selected
}

private var _currentPageOffset = Animatable(0f).apply {
updateBounds(-1f, 1f)
}
val currentPageOffset: Float
get() = _currentPageOffset.value

suspend fun snapToOffset(offset: Float) {
val max = if (currentPage == minPage) 0f else 1f
val min = if (currentPage == maxPage) 0f else -1f
_currentPageOffset.snapTo(offset.coerceIn(min, max))
}

suspend fun fling(velocity: Float) {
if (velocity < 0 && currentPage == maxPage) return
if (velocity > 0 && currentPage == minPage) return

_currentPageOffset.animateTo(currentPageOffset.roundToInt().toFloat())
selectPage()
}

override fun toString(): String = "PagerState{minPage=$minPage, maxPage=$maxPage, " +
"currentPage=$currentPage, currentPageOffset=$currentPageOffset}"
}

@Immutable
private data class PageData(val page: Int) : ParentDataModifier {
override fun Density.modifyParentData(parentData: Any?): Any = this@PageData
}

private val Measurable.page: Int
get() = (parentData as? PageData)?.page ?: error("no PageData for measurable $this")

@Composable
fun Pager(
state: PagerState,
modifier: Modifier = Modifier,
offscreenLimit: Int = 2,
pageContent: @Composable PagerScope.() -> Unit
) {
var pageSize by remember { mutableStateOf(0) }
val coroutineScope = rememberCoroutineScope()
Layout(
content = {
val minPage = (state.currentPage - offscreenLimit).coerceAtLeast(state.minPage)
val maxPage = (state.currentPage + offscreenLimit).coerceAtMost(state.maxPage)

for (page in minPage..maxPage) {
val pageData = PageData(page)
val scope = PagerScope(state, page)
key(pageData) {
Box(contentAlignment = Alignment.Center, modifier = pageData) {
scope.pageContent()
}
}
}
},
modifier = modifier.draggable(
orientation = Orientation.Horizontal,
onDragStarted = {
state.selectionState = PagerState.SelectionState.Undecided
},
onDragStopped = { velocity ->
coroutineScope.launch {
// Velocity is in pixels per second, but we deal in percentage offsets, so we
// need to scale the velocity to match
state.fling(velocity / pageSize)
}
},
state = rememberDraggableState { dy ->
coroutineScope.launch {
with(state) {
val pos = pageSize * currentPageOffset
val max = if (currentPage == minPage) 0 else pageSize * offscreenLimit
val min = if (currentPage == maxPage) 0 else -pageSize * offscreenLimit
val newPos = (pos + dy).coerceIn(min.toFloat(), max.toFloat())
snapToOffset(newPos / pageSize)
}
}
},
)
) { measurables, constraints ->
layout(constraints.maxWidth, constraints.maxHeight) {
val currentPage = state.currentPage
val offset = state.currentPageOffset
val childConstraints = constraints.copy(minWidth = 0, minHeight = 0)

measurables
.map {
it.measure(childConstraints) to it.page
}
.forEach { (placeable, page) ->
// TODO: current this centers each page. We should investigate reading
// gravity modifiers on the child, or maybe as a param to Pager.
val xCenterOffset = (constraints.maxWidth - placeable.width) / 2
val yCenterOffset = (constraints.maxHeight - placeable.height) / 2

if (currentPage == page) {
pageSize = placeable.width
}

val xItemOffset = ((page + offset - currentPage) * placeable.width).roundToInt()

placeable.place(
x = xCenterOffset + xItemOffset,
y = yCenterOffset
)
}
}
}
}

/**
* Scope for [Pager] content.
*/
class PagerScope(
private val state: PagerState,
val page: Int
) {
/**
* Returns the current selected page
*/
val currentPage: Int
get() = state.currentPage

/**
* Returns the current selected page offset
*/
val currentPageOffset: Float
get() = state.currentPageOffset

/**
* Returns the current selection state
*/
val selectionState: PagerState.SelectionState
get() = state.selectionState
}
1 change: 1 addition & 0 deletions Jetcaster/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ subprojects {
// Jetpack Compose SNAPSHOTs
if (!Libs.AndroidX.Compose.snapshot.isEmpty()) {
maven { url "https://androidx.dev/snapshots/builds/${Libs.AndroidX.Compose.snapshot}/artifacts/repository/" }
maven { url 'https://oss.sonatype.org/content/repositories/snapshots/' }
}
}

Expand Down
Loading