加入短视频

This commit is contained in:
2024-07-12 01:36:59 +08:00
parent e7d81dc827
commit 37a16dd683
9 changed files with 617 additions and 8 deletions

View File

@@ -62,7 +62,12 @@ dependencies {
implementation(libs.androidx.navigation.compose) implementation(libs.androidx.navigation.compose)
implementation (libs.androidx.paging.compose) implementation (libs.androidx.paging.compose)
implementation(libs.androidx.paging.runtime) implementation(libs.androidx.paging.runtime)
implementation("com.google.maps.android:maps-compose:4.3.3") implementation(libs.maps.compose)
implementation(libs.accompanist.systemuicontroller)
implementation(libs.androidx.media3.exoplayer) // 核心播放器
implementation(libs.androidx.media3.ui) // UI组件可选
implementation(libs.androidx.media3.session)
implementation(libs.androidx.activity.ktx) // 用于媒体会话(可选)
testImplementation(libs.junit) testImplementation(libs.junit)
androidTestImplementation(libs.androidx.junit) androidTestImplementation(libs.androidx.junit)
androidTestImplementation(libs.androidx.espresso.core) androidTestImplementation(libs.androidx.espresso.core)

View File

@@ -1,5 +1,6 @@
package com.aiosman.riderpro package com.aiosman.riderpro
import android.app.Activity
import android.app.StatusBarManager import android.app.StatusBarManager
import android.os.Bundle import android.os.Bundle
import android.widget.HorizontalScrollView import android.widget.HorizontalScrollView
@@ -8,6 +9,9 @@ import androidx.activity.ComponentActivity
import androidx.activity.SystemBarStyle import androidx.activity.SystemBarStyle
import androidx.activity.compose.setContent import androidx.activity.compose.setContent
import androidx.activity.enableEdgeToEdge import androidx.activity.enableEdgeToEdge
import androidx.compose.animation.core.tween
import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut
import androidx.compose.foundation.BorderStroke import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.Image import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
@@ -60,6 +64,7 @@ import androidx.navigation.compose.composable
import androidx.navigation.compose.currentBackStackEntryAsState import androidx.navigation.compose.currentBackStackEntryAsState
import androidx.navigation.compose.rememberNavController import androidx.navigation.compose.rememberNavController
import com.aiosman.riderpro.ui.theme.RiderProTheme import com.aiosman.riderpro.ui.theme.RiderProTheme
import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.google.android.gms.maps.model.CameraPosition import com.google.android.gms.maps.model.CameraPosition
import com.google.android.gms.maps.model.LatLng import com.google.android.gms.maps.model.LatLng
import com.google.maps.android.compose.GoogleMap import com.google.maps.android.compose.GoogleMap
@@ -82,7 +87,14 @@ class MainActivity : ComponentActivity() {
@Composable @Composable
fun NavigationController(navController: NavHostController){ fun NavigationController(navController: NavHostController){
NavHost( NavHost(
navController = navController, startDestination = NavigationItem.Home.route){ navController = navController,
startDestination = NavigationItem.Home.route,
enterTransition = {
fadeIn(animationSpec = tween(300))
},
exitTransition = {
fadeOut(animationSpec = tween(300))
}){
composable(route = NavigationItem.Home.route){ composable(route = NavigationItem.Home.route){
Home() Home()
} }
@@ -93,7 +105,7 @@ fun NavigationController(navController: NavHostController){
Add() Add()
} }
composable(route = NavigationItem.Message.route){ composable(route = NavigationItem.Message.route){
Message() Video()
} }
composable(route = NavigationItem.Profile.route){ composable(route = NavigationItem.Profile.route){
Profile() Profile()
@@ -124,6 +136,7 @@ fun Navigation(){
){ ){
val navBackStackEntry by navController.currentBackStackEntryAsState() val navBackStackEntry by navController.currentBackStackEntryAsState()
val currentRoute = navBackStackEntry?.destination?.route val currentRoute = navBackStackEntry?.destination?.route
val systemUiController = rememberSystemUiController()
item.forEach{ it -> item.forEach{ it ->
NavigationBarItem( NavigationBarItem(
selected = currentRoute == it.route , selected = currentRoute == it.route ,
@@ -131,6 +144,23 @@ fun Navigation(){
if(currentRoute != it.route){ if(currentRoute != it.route){
navController.navigate(it.route) navController.navigate(it.route)
} }
when (it.route) {
NavigationItem.Add.route -> {
systemUiController.setSystemBarsColor(
color = Color.Black
)
}
NavigationItem.Message.route -> {
systemUiController.setSystemBarsColor(
color = Color.Black
)
}
else -> {
systemUiController.setSystemBarsColor(
color = Color.Transparent
)
}
}
}, },
colors = NavigationBarItemColors( colors = NavigationBarItemColors(
selectedIconColor = Color.Red, selectedIconColor = Color.Red,
@@ -193,6 +223,17 @@ fun Add(){
} }
} }
@Composable
fun Video(){
Column (
modifier = Modifier.fillMaxSize(),
verticalArrangement = Arrangement.Top,
horizontalAlignment = Alignment.CenterHorizontally,
){
ShortVideo()
}
}
@Composable @Composable
fun Message(){ fun Message(){
Column ( Column (

View File

@@ -0,0 +1,14 @@
package com.aiosman.riderpro
import androidx.compose.foundation.clickable
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.runtime.remember
import androidx.compose.ui.Modifier
import androidx.compose.ui.composed
inline fun Modifier.noRippleClickable(crossinline onClick: () -> Unit): Modifier = composed {
this.clickable(indication = null,
interactionSource = remember { MutableInteractionSource() }) {
onClick()
}
}

View File

@@ -0,0 +1,232 @@
package com.aiosman.riderpro
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.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.runtime.*
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.abs
import kotlin.math.roundToInt
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
// 根据 fling 的方向滑动到下一页或上一页
_currentPageOffset.animateTo(velocity)
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(
modifier: Modifier = Modifier,
state: PagerState,
orientation: Orientation = Orientation.Horizontal,
offscreenLimit: Int = 2,
horizontalAlignment: Alignment.Horizontal = Alignment.CenterHorizontally, // 新增水平对齐参数
verticalAlignment: Alignment.Vertical = Alignment.CenterVertically, // 新增垂直对齐参数
content: @Composable PagerScope.() -> Unit
) {
var pageSize by remember { mutableStateOf(0) }
val coroutineScope = rememberCoroutineScope()
Layout(
content = {
// 根据 offscreenLimit 计算页面范围
val minPage = maxOf(state.currentPage - offscreenLimit, state.minPage)
val maxPage = minOf(state.currentPage + offscreenLimit, state.maxPage)
for (page in minPage..maxPage) {
val pageData = PageData(page)
val scope = PagerScope(state, page)
key(pageData) {
Column(modifier = pageData) {
scope.content()
}
}
}
},
modifier = modifier.draggable(
orientation = orientation,
onDragStarted = {
state.selectionState = PagerState.SelectionState.Undecided
},
onDragStopped = { velocity ->
coroutineScope.launch {
// 根据速度判断是否滑动到下一页
val threshold = 1000f // 速度阈值,可调整
if (velocity > threshold) {
state.fling(1f) // 向右滑动
} else if (velocity < -threshold) {
state.fling(-1f) // 向左滑动
} else {
state.fling(0f) // 保持当前页
}
}
},
state = rememberDraggableState { dy ->
coroutineScope.launch {
with(state) {
val pos = pageSize * currentPageOffset
val max = if (currentPage == minPage) 0 else pageSize
val min = if (currentPage == maxPage) 0 else -pageSize
// 直接将手指的位移应用到 currentPageOffset
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.forEach { measurable ->
val placeable = measurable.measure(childConstraints)
val page = measurable.page
// 根据对齐参数计算 x 和 y 位置
val xPosition = when (horizontalAlignment) {
Alignment.Start -> 0
Alignment.CenterHorizontally -> (constraints.maxWidth - placeable.width) / 2
Alignment.End -> constraints.maxWidth - placeable.width
else -> 0
}
val yPosition = when (verticalAlignment) {
Alignment.Top -> 0
Alignment.CenterVertically -> (constraints.maxHeight - placeable.height) / 2
Alignment.Bottom -> constraints.maxHeight - placeable.height
else -> 0
}
if (currentPage == page) { // 只在当前页面设置 pageSize避免不必要的设置
pageSize = if (orientation == Orientation.Horizontal) {
placeable.width
} else {
placeable.height
}
}
val isVisible = abs(page - (currentPage - offset)) <= 1
if (isVisible) {
// 修正 x 的计算
val xOffset = if (orientation == Orientation.Horizontal) {
((page - currentPage) * pageSize + offset * pageSize).roundToInt()
} else {
0
}
// 使用 placeRelative 进行放置
placeable.placeRelative(
x = xPosition + xOffset,
y = yPosition + if (orientation == Orientation.Vertical) ((page - (currentPage - offset)) * placeable.height).roundToInt() else 0
)
}
}
}
}
}
class PagerScope(
private val state: PagerState,
val page: Int
) {
val currentPage: Int
get() = state.currentPage
val currentPageOffset: Float
get() = state.currentPageOffset
val selectionState: PagerState.SelectionState
get() = state.selectionState
}

View File

@@ -0,0 +1,27 @@
package com.aiosman.riderpro
import android.app.Activity
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.runtime.Composable
import com.aiosman.riderpro.ShortViewCompose
import com.aiosman.riderpro.ui.theme.RiderProTheme
val videoUrls = listOf(
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/ForBiggerBlazes.mp4",
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/BigBuckBunny.mp4",
"https://commondatastorage.googleapis.com/gtv-videos-bucket/sample/WeAreGoingOnBullrun.mp4"
)
@Composable
fun ShortVideo() {
RiderProTheme {
Surface(color = MaterialTheme.colorScheme.background) {
ShortViewCompose(
videoItemsUrl = videoUrls,
clickItemPosition = 0
)
}
}
}

View File

@@ -0,0 +1,180 @@
package com.aiosman.riderpro
import android.app.Activity
import android.net.Uri
import android.view.ViewGroup
import android.widget.FrameLayout
import androidx.annotation.OptIn
import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.Orientation
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.size
import androidx.compose.material3.Icon
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.PlayArrow
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
import androidx.media3.common.C
import androidx.media3.common.MediaItem
import androidx.media3.common.Player
import androidx.media3.common.util.UnstableApi
import androidx.media3.common.util.Util
import androidx.media3.datasource.DataSource
import androidx.media3.datasource.DefaultDataSourceFactory
import androidx.media3.exoplayer.ExoPlayer
import androidx.media3.exoplayer.SimpleExoPlayer
import androidx.media3.exoplayer.source.ProgressiveMediaSource
import androidx.media3.ui.AspectRatioFrameLayout
import androidx.media3.ui.PlayerView
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
@Composable
fun ShortViewCompose(
videoItemsUrl:List<String>,
clickItemPosition:Int = 0,
videoHeader:@Composable () -> Unit = {},
videoBottom:@Composable () -> Unit = {}
) {
val pagerState: PagerState = run {
remember {
PagerState(clickItemPosition, 0, videoItemsUrl.size - 1)
}
}
val initialLayout= remember {
mutableStateOf(true)
}
val pauseIconVisibleState = remember {
mutableStateOf(false)
}
Pager(
state = pagerState,
orientation = Orientation.Vertical,
offscreenLimit = 1
) {
pauseIconVisibleState.value=false
SingleVideoItemContent(videoItemsUrl[page],
pagerState,
page,
initialLayout,
pauseIconVisibleState,
videoHeader,
videoBottom)
}
LaunchedEffect(clickItemPosition){
delay(300)
initialLayout.value=false
}
}
@Composable
private fun SingleVideoItemContent(
videoUrl: String,
pagerState: PagerState,
pager: Int,
initialLayout: MutableState<Boolean>,
pauseIconVisibleState: MutableState<Boolean>,
VideoHeader: @Composable() () -> Unit,
VideoBottom: @Composable() () -> Unit,
) {
Box(modifier = Modifier.fillMaxSize()){
VideoPlayer(videoUrl,pagerState,pager,pauseIconVisibleState)
VideoHeader.invoke()
Box(modifier = Modifier.align(Alignment.BottomStart)){
VideoBottom.invoke()
}
if (initialLayout.value) {
Box(modifier = Modifier
.fillMaxSize()
.background(color = Color.Black))
}
}
}
@OptIn(UnstableApi::class)
@Composable
fun VideoPlayer(
videoUrl: String,
pagerState: PagerState,
pager: Int,
pauseIconVisibleState: MutableState<Boolean>,
) {
val context = LocalContext.current
val scope= rememberCoroutineScope()
val exoPlayer = remember {
SimpleExoPlayer.Builder(context)
.build()
.apply {
val dataSourceFactory: DataSource.Factory = DefaultDataSourceFactory(
context,
Util.getUserAgent(context, context.packageName)
)
val source = ProgressiveMediaSource.Factory(dataSourceFactory)
.createMediaSource(MediaItem.fromUri(Uri.parse(videoUrl)))
this.prepare(source)
}
}
if (pager == pagerState.currentPage) {
exoPlayer.playWhenReady = true
exoPlayer.play()
} else {
exoPlayer.pause()
}
exoPlayer.videoScalingMode = C.VIDEO_SCALING_MODE_SCALE_TO_FIT_WITH_CROPPING
exoPlayer.repeatMode = Player.REPEAT_MODE_ONE
DisposableEffect(
Box(modifier = Modifier.fillMaxSize()){
AndroidView(factory = {
PlayerView(context).apply {
hideController()
useController = false
resizeMode = AspectRatioFrameLayout.RESIZE_MODE_ZOOM
player = exoPlayer
layoutParams = FrameLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT)
}
},modifier = Modifier.noRippleClickable {
pauseIconVisibleState.value=true
exoPlayer.pause()
scope.launch {
delay(500)
if (exoPlayer.isPlaying) {
exoPlayer.pause()
} else {
pauseIconVisibleState.value=false
exoPlayer.play()
}
}
})
if (pauseIconVisibleState.value)
Icon(imageVector = Icons.Default.PlayArrow, contentDescription = null,
modifier = Modifier
.align(Alignment.Center)
.size(80.dp))
}
) {
onDispose {
exoPlayer.release()
}
}
}

View File

@@ -0,0 +1,100 @@
package com.aiosman.riderpro
import android.annotation.SuppressLint
import android.app.Activity
import android.content.Context
import android.content.res.Resources
import android.graphics.Color
import android.graphics.drawable.ColorDrawable
import android.os.Build
import android.util.TypedValue
import android.view.View
import android.view.ViewGroup
import android.view.WindowManager
import android.widget.RelativeLayout
import androidx.annotation.ColorInt
import androidx.annotation.ColorRes
//import androidx.appcompat.app.AppCompatActivity
import androidx.fragment.app.Fragment
private const val COLOR_TRANSPARENT = 0
@SuppressLint("ObsoleteSdkInt")
@JvmOverloads
fun Activity.immersive(@ColorInt color: Int = COLOR_TRANSPARENT, darkMode: Boolean? = null) {
when {
Build.VERSION.SDK_INT >= 21 -> {
when (color) {
COLOR_TRANSPARENT -> {
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
var systemUiVisibility = window.decorView.systemUiVisibility
systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
systemUiVisibility = systemUiVisibility or View.SYSTEM_UI_FLAG_LAYOUT_STABLE
window.decorView.systemUiVisibility = systemUiVisibility
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = color
}
else -> {
window.clearFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
var systemUiVisibility = window.decorView.systemUiVisibility
systemUiVisibility = systemUiVisibility and View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN
systemUiVisibility = systemUiVisibility and View.SYSTEM_UI_FLAG_LAYOUT_STABLE
window.decorView.systemUiVisibility = systemUiVisibility
window.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS)
window.statusBarColor = color
}
}
}
Build.VERSION.SDK_INT >= 19 -> {
window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)
if (color != COLOR_TRANSPARENT) {
setTranslucentView(window.decorView as ViewGroup, color)
}
}
}
if (darkMode != null) {
darkMode(darkMode)
}
}
@JvmOverloads
fun Activity.darkMode(darkMode: Boolean = true) {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) {
var systemUiVisibility = window.decorView.systemUiVisibility
systemUiVisibility = if (darkMode) {
systemUiVisibility or View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR
} else {
systemUiVisibility and View.SYSTEM_UI_FLAG_LIGHT_STATUS_BAR.inv()
}
window.decorView.systemUiVisibility = systemUiVisibility
}
}
private fun Context.setTranslucentView(container: ViewGroup, color: Int) {
if (Build.VERSION.SDK_INT >= 19) {
var simulateStatusBar: View? = container.findViewById(android.R.id.custom)
if (simulateStatusBar == null && color != 0) {
simulateStatusBar = View(container.context)
simulateStatusBar.id = android.R.id.custom
val lp = ViewGroup.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, statusBarHeight)
container.addView(simulateStatusBar, lp)
}
simulateStatusBar?.setBackgroundColor(color)
}
}
val Context?.statusBarHeight: Int
get() {
this ?: return 0
var result = 24
val resId = resources.getIdentifier("status_bar_height", "dimen", "android")
result = if (resId > 0) {
resources.getDimensionPixelSize(resId)
} else {
TypedValue.applyDimension(
TypedValue.COMPLEX_UNIT_DIP,
result.toFloat(), Resources.getSystem().displayMetrics
).toInt()
}
return result
}

View File

@@ -1,4 +1,5 @@
[versions] [versions]
accompanistSystemuicontroller = "0.27.0"
agp = "8.4.0" agp = "8.4.0"
kotlin = "1.9.0" kotlin = "1.9.0"
coreKtx = "1.10.1" coreKtx = "1.10.1"
@@ -8,12 +9,19 @@ espressoCore = "3.5.1"
lifecycleRuntimeKtx = "2.6.1" lifecycleRuntimeKtx = "2.6.1"
activityCompose = "1.8.0" activityCompose = "1.8.0"
composeBom = "2023.08.00" composeBom = "2023.08.00"
mapsCompose = "4.3.3"
material3Android = "1.2.1" material3Android = "1.2.1"
media3Exoplayer = "1.3.1"
navigationCompose = "2.7.7" navigationCompose = "2.7.7"
pagingRuntime = "3.3.0" pagingRuntime = "3.3.0"
activityKtx = "1.9.0"
[libraries] [libraries]
accompanist-systemuicontroller = { module = "com.google.accompanist:accompanist-systemuicontroller", version.ref = "accompanistSystemuicontroller" }
androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" } androidx-core-ktx = { group = "androidx.core", name = "core-ktx", version.ref = "coreKtx" }
androidx-media3-exoplayer = { module = "androidx.media3:media3-exoplayer", version.ref = "media3Exoplayer" }
androidx-media3-session = { module = "androidx.media3:media3-session", version.ref = "media3Exoplayer" }
androidx-media3-ui = { module = "androidx.media3:media3-ui", version.ref = "media3Exoplayer" }
androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" } androidx-navigation-compose = { module = "androidx.navigation:navigation-compose", version.ref = "navigationCompose" }
androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pagingRuntime" } androidx-paging-compose = { module = "androidx.paging:paging-compose", version.ref = "pagingRuntime" }
androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pagingRuntime" } androidx-paging-runtime = { module = "androidx.paging:paging-runtime", version.ref = "pagingRuntime" }
@@ -21,7 +29,7 @@ junit = { group = "junit", name = "junit", version.ref = "junit" }
androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" } androidx-junit = { group = "androidx.test.ext", name = "junit", version.ref = "junitVersion" }
androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" } androidx-espresso-core = { group = "androidx.test.espresso", name = "espresso-core", version.ref = "espressoCore" }
androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" } androidx-lifecycle-runtime-ktx = { group = "androidx.lifecycle", name = "lifecycle-runtime-ktx", version.ref = "lifecycleRuntimeKtx" }
androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version.ref = "activityCompose" } androidx-activity-compose = { group = "androidx.activity", name = "activity-compose", version = "1.9.0" }
androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" } androidx-compose-bom = { group = "androidx.compose", name = "compose-bom", version.ref = "composeBom" }
androidx-ui = { group = "androidx.compose.ui", name = "ui" } androidx-ui = { group = "androidx.compose.ui", name = "ui" }
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" } androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
@@ -31,6 +39,8 @@ androidx-ui-test-manifest = { group = "androidx.compose.ui", name = "ui-test-man
androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" } androidx-ui-test-junit4 = { group = "androidx.compose.ui", name = "ui-test-junit4" }
androidx-material3 = { group = "androidx.compose.material3", name = "material3" } androidx-material3 = { group = "androidx.compose.material3", name = "material3" }
androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" } androidx-material3-android = { group = "androidx.compose.material3", name = "material3-android", version.ref = "material3Android" }
maps-compose = { module = "com.google.maps.android:maps-compose", version.ref = "mapsCompose" }
androidx-activity-ktx = { group = "androidx.activity", name = "activity-ktx", version.ref = "activityKtx" }
[plugins] [plugins]
android-application = { id = "com.android.application", version.ref = "agp" } android-application = { id = "com.android.application", version.ref = "agp" }