Migrated into to compose

Signed-off-by: Arnau Mora Gras <arnyminerz@proton.me>
This commit is contained in:
Arnau Mora 2024-06-11 11:20:37 +02:00 committed by Ricki Hirner
parent ea035fa931
commit e2ee0f459a
4 changed files with 300 additions and 54 deletions

View File

@ -0,0 +1,64 @@
package at.bitfire.davdroid.ui.composable
import androidx.compose.animation.AnimatedContent
import androidx.compose.foundation.layout.aspectRatio
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.shape.CircleShape
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.Dp
import androidx.compose.ui.unit.dp
import at.bitfire.davdroid.ui.AppTheme
@Composable
fun ButtonWithIcon(
icon: ImageVector,
contentDescription: String?,
modifier: Modifier = Modifier,
size: Dp = 56.dp,
color: Color = MaterialTheme.colorScheme.tertiary,
onClick: () -> Unit
) {
Surface(
color = color,
contentColor = contentColorFor(backgroundColor = color),
modifier = modifier
.size(size)
.aspectRatio(1f),
onClick = onClick,
shape = CircleShape
) {
AnimatedContent(
targetState = icon,
label = "Button Icon"
) {
Icon(
imageVector = it,
contentDescription = contentDescription,
modifier = Modifier.padding(12.dp)
)
}
}
}
@Preview
@Composable
fun ButtonWithIcon_Preview() {
AppTheme {
ButtonWithIcon(
icon = Icons.AutoMirrored.Filled.ArrowForward,
contentDescription = null
) { }
}
}

View File

@ -0,0 +1,77 @@
package at.bitfire.davdroid.ui.composable
import androidx.compose.animation.core.animateFloatAsState
import androidx.compose.foundation.Canvas
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.width
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.contentColorFor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier
import androidx.compose.ui.geometry.Offset
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
@Composable
fun PositionIndicator(
index: Int,
max: Int,
modifier: Modifier = Modifier,
selectedIndicatorColor: Color = MaterialTheme.colorScheme.tertiary,
unselectedIndicatorColor: Color = contentColorFor(selectedIndicatorColor),
indicatorSize: Float = 20f
) {
val selectedPosition by animateFloatAsState(
targetValue = index.toFloat(),
label = "position"
)
Canvas(modifier = modifier) {
val padding = size.width / (max + 1)
for (idx in 0 until max) {
drawCircle(
color = unselectedIndicatorColor,
radius = indicatorSize,
center = Offset(
x = (idx + 1) * padding,
y = size.height / 2
)
)
}
drawCircle(
color = selectedIndicatorColor,
radius = indicatorSize,
center = Offset(
x = (selectedPosition + 1) * padding,
y = size.height / 2
)
)
}
}
@Preview(
showBackground = true,
backgroundColor = 0xff000000
)
@Composable
fun PositionIndicator_Preview() {
var index by remember { mutableIntStateOf(0) }
PositionIndicator(
index = index,
max = 5,
modifier = Modifier
.width(200.dp)
.height(50.dp)
.clickable { if (index == 4) index = 0 else index++ }
)
}

View File

@ -11,12 +11,18 @@ import android.os.Bundle
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.activity.addCallback
import androidx.activity.compose.BackHandler
import androidx.activity.compose.setContent
import androidx.activity.result.contract.ActivityResultContract
import androidx.activity.viewModels
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material3.Surface
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.toArgb
import androidx.compose.ui.platform.ComposeView
@ -32,74 +38,44 @@ import com.github.appintro.AppIntro2
import dagger.hilt.android.AndroidEntryPoint
import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject
import kotlinx.coroutines.launch
@AndroidEntryPoint
class IntroActivity : AppIntro2() {
@OptIn(ExperimentalFoundationApi::class)
class IntroActivity : AppCompatActivity() {
val model by viewModels<Model>()
private var currentSlide = 0
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
model.pages.forEachIndexed { idx, _ ->
addSlide(PageFragment().apply {
arguments = Bundle(1).apply {
putInt(PageFragment.ARG_PAGE_IDX, idx)
}
})
}
val pages = model.pages
setBarColor(M3ColorScheme.primaryLight.toArgb())
isSkipButtonEnabled = false
setContent {
AppTheme {
val scope = rememberCoroutineScope()
val pagerState = rememberPagerState { pages.size }
onBackPressedDispatcher.addCallback(this) {
if (currentSlide == 0) {
setResult(Activity.RESULT_CANCELED)
finish()
} else {
goToPreviousSlide()
}
}
}
override fun onPageSelected(position: Int) {
super.onPageSelected(position)
currentSlide = position
}
override fun onDonePressed(currentFragment: Fragment?) {
super.onDonePressed(currentFragment)
setResult(Activity.RESULT_OK)
finish()
}
@AndroidEntryPoint
class PageFragment: Fragment() {
companion object {
const val ARG_PAGE_IDX = "page"
}
val model by activityViewModels<Model>()
val page by lazy { model.pages[requireArguments().getInt(ARG_PAGE_IDX)] }
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?) =
ComposeView(requireActivity()).apply {
setViewCompositionStrategy(ViewCompositionStrategy.DisposeOnViewTreeLifecycleDestroyed)
setContent {
AppTheme {
Surface(Modifier.fillMaxSize()) {
Box(Modifier.padding(bottom = dimensionResource(com.github.appintro.R.dimen.appintro2_bottombar_height))) {
page.ComposePage()
}
}
BackHandler {
if (pagerState.settledPage == 0) {
setResult(Activity.RESULT_CANCELED)
finish()
} else scope.launch {
pagerState.animateScrollToPage(pagerState.settledPage - 1)
}
}
}
IntroScreen(
pages = pages,
pagerState = pagerState,
onDonePressed = {
setResult(Activity.RESULT_OK)
finish()
}
)
}
}
}

View File

@ -0,0 +1,129 @@
package at.bitfire.davdroid.ui.intro
import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.pager.HorizontalPager
import androidx.compose.foundation.pager.PagerState
import androidx.compose.foundation.pager.rememberPagerState
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowForward
import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.MaterialTheme
import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.compose.ui.zIndex
import at.bitfire.davdroid.ui.AppTheme
import at.bitfire.davdroid.ui.composable.ButtonWithIcon
import at.bitfire.davdroid.ui.composable.PositionIndicator
import kotlinx.coroutines.launch
@Composable
@OptIn(ExperimentalFoundationApi::class)
fun IntroScreen(
pages: List<IntroPage>,
pagerState: PagerState = rememberPagerState { pages.size },
onDonePressed: () -> Unit
) {
val scope = rememberCoroutineScope()
Column(modifier = Modifier.fillMaxSize()) {
HorizontalPager(
state = pagerState,
modifier = Modifier
.fillMaxWidth()
.weight(1f)
) { pages[it].ComposePage() }
Box(
modifier = Modifier
.fillMaxWidth()
.height(90.dp)
.background(MaterialTheme.colorScheme.primary)
) {
PositionIndicator(
index = pagerState.currentPage,
max = pages.size,
modifier = Modifier
.fillMaxHeight()
.padding(horizontal = 128.dp)
.align(Alignment.Center)
.fillMaxWidth(),
selectedIndicatorColor = MaterialTheme.colorScheme.onPrimary,
unselectedIndicatorColor = MaterialTheme.colorScheme.tertiary,
indicatorSize = 15f
)
ButtonWithIcon(
icon = if (pagerState.currentPage + 1 == pagerState.pageCount) {
Icons.Default.Check
} else {
Icons.AutoMirrored.Default.ArrowForward
},
contentDescription = null,
modifier = Modifier
.padding(end = 16.dp)
.align(Alignment.CenterEnd)
) {
if (pagerState.currentPage + 1 == pagerState.pageCount) {
onDonePressed()
} else scope.launch {
pagerState.animateScrollToPage(pagerState.currentPage + 1)
}
}
}
}
}
@Preview(
showSystemUi = true
)
@Composable
@OptIn(ExperimentalFoundationApi::class)
fun IntroScreen_Preview() {
AppTheme {
IntroScreen(
listOf(
object : IntroPage {
override fun getShowPolicy(): IntroPage.ShowPolicy =
IntroPage.ShowPolicy.SHOW_ALWAYS
@Composable
override fun ComposePage() {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.surface)
)
}
},
object : IntroPage {
override fun getShowPolicy(): IntroPage.ShowPolicy =
IntroPage.ShowPolicy.SHOW_ALWAYS
@Composable
override fun ComposePage() {
Box(
modifier = Modifier
.fillMaxSize()
.background(MaterialTheme.colorScheme.primary)
)
}
}
),
onDonePressed = {}
)
}
}