compose: add scroll hint to all lazy columns + improve padding

This commit is contained in:
Rahul Patel
2026-05-06 05:24:10 +05:30
parent b2d10c17b4
commit fba6c4a8df
19 changed files with 507 additions and 265 deletions

View File

@@ -11,14 +11,12 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
@@ -46,18 +44,16 @@ fun Header(
Row( Row(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
.clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null) .clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null)
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_small), horizontal = dimensionResource(R.dimen.padding_medium),
vertical = dimensionResource(R.dimen.padding_xxsmall) vertical = dimensionResource(R.dimen.padding_xsmall)
), ),
horizontalArrangement = Arrangement.SpaceBetween, horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
Column( Column(
modifier = Modifier.weight(1F), modifier = Modifier.weight(1F)
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_xsmall))
) { ) {
Text( Text(
text = title, text = title,
@@ -68,7 +64,8 @@ fun Header(
if (!subtitle.isNullOrBlank()) { if (!subtitle.isNullOrBlank()) {
Text( Text(
text = subtitle, text = subtitle,
style = MaterialTheme.typography.bodyMedium style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
} }

View File

@@ -56,7 +56,7 @@ fun Info(
.fillMaxWidth() .fillMaxWidth()
.clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null) .clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null)
.padding( .padding(
horizontal = dimensionResource(R.dimen.padding_small), horizontal = dimensionResource(R.dimen.padding_medium),
vertical = dimensionResource(R.dimen.padding_xxsmall) vertical = dimensionResource(R.dimen.padding_xxsmall)
), ),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)), horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)),
@@ -75,7 +75,7 @@ fun Info(
Text( Text(
text = description, text = description,
style = MaterialTheme.typography.bodySmall, style = MaterialTheme.typography.bodySmall,
color = MaterialTheme.colorScheme.secondary color = MaterialTheme.colorScheme.onSurfaceVariant
) )
} }
} }

View File

@@ -10,10 +10,14 @@ import android.net.Uri
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.input.clearText import androidx.compose.foundation.text.input.clearText
import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
@@ -30,6 +34,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
@@ -40,6 +45,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.core.content.pm.PackageInfoCompat import androidx.core.content.pm.PackageInfoCompat
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
@@ -47,6 +53,7 @@ import com.aurora.Constants
import com.aurora.extensions.toast import com.aurora.extensions.toast
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.BlackListItem import com.aurora.store.compose.composable.BlackListItem
import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
import com.aurora.store.compose.ui.blacklist.menu.BlacklistMenu import com.aurora.store.compose.ui.blacklist.menu.BlacklistMenu
import com.aurora.store.compose.ui.blacklist.menu.MenuItem import com.aurora.store.compose.ui.blacklist.menu.MenuItem
@@ -209,35 +216,51 @@ private fun ScreenContent(
) )
} }
Scaffold(topBar = { SearchBar() }) { paddingValues -> Scaffold(
LazyColumn( modifier = Modifier.navigationBarsPadding(),
modifier = Modifier topBar = { SearchBar() }
.fillMaxWidth() ) { paddingValues ->
.padding(paddingValues), val listState = rememberLazyListState()
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall)) Box(modifier = Modifier.fillMaxSize()) {
) { LazyColumn(
items(items = packages ?: emptyList(), key = { p -> p.packageName.hashCode() }) { pkg -> modifier = Modifier
val isBlacklisted = isPackageBlacklisted(pkg.packageName) .fillMaxWidth()
val isFiltered = isPackageFiltered(pkg) .padding(paddingValues),
BlackListItem( state = listState,
icon = PackageUtil.getIconForPackage(context, pkg.packageName)!!, verticalArrangement = Arrangement.spacedBy(
displayName = pkg.applicationInfo!!.loadLabel( dimensionResource(R.dimen.margin_xxsmall)
context.packageManager
).toString(),
packageName = pkg.packageName,
versionName = pkg.versionName!!,
versionCode = PackageInfoCompat.getLongVersionCode(pkg),
isChecked = isBlacklisted || isFiltered,
isEnabled = !isFiltered,
onClick = {
if (isBlacklisted) {
onWhitelist(pkg.packageName)
} else {
onBlacklist(pkg.packageName)
}
}
) )
) {
items(items = packages ?: emptyList(), key = { p ->
p.packageName.hashCode()
}) { pkg ->
val isBlacklisted = isPackageBlacklisted(pkg.packageName)
val isFiltered = isPackageFiltered(pkg)
BlackListItem(
icon = PackageUtil.getIconForPackage(context, pkg.packageName)!!,
displayName = pkg.applicationInfo!!.loadLabel(
context.packageManager
).toString(),
packageName = pkg.packageName,
versionName = pkg.versionName!!,
versionCode = PackageInfoCompat.getLongVersionCode(pkg),
isChecked = isBlacklisted || isFiltered,
isEnabled = !isFiltered,
onClick = {
if (isBlacklisted) {
onWhitelist(pkg.packageName)
} else {
onBlacklist(pkg.packageName)
}
}
)
}
} }
ScrollHint(
listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter)
)
} }
} }
} }

View File

@@ -12,14 +12,15 @@ import androidx.compose.animation.fadeIn
import androidx.compose.animation.fadeOut import androidx.compose.animation.fadeOut
import androidx.compose.animation.togetherWith import androidx.compose.animation.togetherWith
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.material3.adaptive.WindowAdaptiveInfo
@@ -49,6 +50,7 @@ import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation3.runtime.NavKey import androidx.navigation3.runtime.NavKey
@@ -65,6 +67,7 @@ import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.Header import com.aurora.store.compose.composable.Header
import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.navigation.Screen import com.aurora.store.compose.navigation.Screen
@@ -163,7 +166,7 @@ fun AppDetailsScreen(
PackageUtil.getLaunchIntent(context, packageName) PackageUtil.getLaunchIntent(context, packageName)
) )
} catch (_: ActivityNotFoundException) { } catch (_: ActivityNotFoundException) {
context.toast(context.getString(R.string.unable_to_open)) context.toast(R.string.unable_to_open)
} }
}, },
onTestingSubscriptionChange = { subscribe -> onTestingSubscriptionChange = { subscribe ->
@@ -390,6 +393,7 @@ private fun ScreenContentApp(
@Composable @Composable
fun MainPane() { fun MainPane() {
Scaffold( Scaffold(
modifier = Modifier.navigationBarsPadding(),
topBar = { topBar = {
TopAppBar( TopAppBar(
onNavigateUp = onNavigateUp, onNavigateUp = onNavigateUp,
@@ -397,81 +401,123 @@ private fun ScreenContentApp(
) )
} }
) { paddingValues -> ) { paddingValues ->
Column( val listState = rememberLazyListState()
modifier = Modifier Box(modifier = Modifier.fillMaxSize()) {
.padding(paddingValues) LazyColumn(
.fillMaxSize() modifier = Modifier
.verticalScroll(rememberScrollState()) .padding(paddingValues)
.padding(dimensionResource(R.dimen.padding_medium)), .fillMaxSize(),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) verticalArrangement = Arrangement.spacedBy(
) { dimensionResource(R.dimen.margin_medium)
Details( ),
app = app, state = listState
state = state, ) {
onNavigateToDetailsDevProfile = { showExtraPane(Screen.DevProfile(it)) } item {
) Details(
app = app,
SetupActions() state = state,
onNavigateToDetailsDevProfile = { showExtraPane(Screen.DevProfile(it)) }
Tags(app = app) )
Changelog(changelog = app.changes)
Header(
title = stringResource(R.string.details_more_about_app),
subtitle = app.shortDescription,
onClick = { showExtraPane(ExtraScreen.More) }
)
Screenshots(
screenshots = app.screenshots,
onNavigateToScreenshot = { showExtraPane(ExtraScreen.Screenshot(it)) }
)
RatingAndReviews(
rating = app.rating,
featuredReviews = featuredReviews,
onNavigateToDetailsReview = { showExtraPane(ExtraScreen.Review) }
)
if (!isAnonymous && app.testingProgram?.isAvailable == true) {
Testing(
isSubscribed = app.testingProgram!!.isSubscribed,
onTestingSubscriptionChange = onTestingSubscriptionChange
)
}
Compatibility(needsGms = app.requiresGMS(), plexusScores = plexusScores)
Header(
title = stringResource(R.string.details_permission),
subtitle = if (app.permissions.isNotEmpty()) {
stringResource(R.string.permissions_requested, app.permissions.size)
} else {
stringResource(R.string.details_no_permission)
},
onClick = if (app.permissions.isNotEmpty()) {
{ showExtraPane(ExtraScreen.Permission) }
} else {
null
} }
)
if (dataSafetyReport != null) { item {
DataSafety(report = dataSafetyReport, privacyPolicyUrl = app.privacyPolicyUrl) SetupActions()
}
Privacy(
report = exodusReport,
onNavigateToDetailsExodus = if (exodusReport != null && exodusReport.id != -1) {
{ showExtraPane(ExtraScreen.Exodus) }
} else {
null
} }
)
DeveloperDetails( item {
address = app.developerAddress, Tags(app = app)
website = app.developerWebsite, }
email = app.developerEmail
item {
Changelog(changelog = app.changes)
}
item {
Header(
title = stringResource(R.string.details_more_about_app),
subtitle = app.shortDescription,
onClick = { showExtraPane(ExtraScreen.More) }
)
}
item {
Screenshots(
screenshots = app.screenshots,
onNavigateToScreenshot = { showExtraPane(ExtraScreen.Screenshot(it)) }
)
}
item {
RatingAndReviews(
rating = app.rating,
featuredReviews = featuredReviews,
onNavigateToDetailsReview = { showExtraPane(ExtraScreen.Review) }
)
}
item {
if (!isAnonymous && app.testingProgram?.isAvailable == true) {
Testing(
isSubscribed = app.testingProgram!!.isSubscribed,
onTestingSubscriptionChange = onTestingSubscriptionChange
)
}
}
item {
Compatibility(needsGms = app.requiresGMS(), plexusScores = plexusScores)
}
item {
Header(
title = stringResource(R.string.details_permission),
subtitle = if (app.permissions.isNotEmpty()) {
stringResource(R.string.permissions_requested, app.permissions.size)
} else {
stringResource(R.string.details_no_permission)
},
onClick = if (app.permissions.isNotEmpty()) {
{ showExtraPane(ExtraScreen.Permission) }
} else {
null
}
)
}
item {
if (dataSafetyReport != null) {
DataSafety(
report = dataSafetyReport,
privacyPolicyUrl = app.privacyPolicyUrl
)
}
}
item {
Privacy(
report = exodusReport,
onNavigateToDetailsExodus = if (exodusReport != null &&
exodusReport.id != -1
) {
{ showExtraPane(ExtraScreen.Exodus) }
} else {
null
}
)
}
item {
DeveloperDetails(
address = app.developerAddress,
website = app.developerWebsite,
email = app.developerEmail
)
}
}
ScrollHint(
listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter)
) )
} }
} }

View File

@@ -5,11 +5,14 @@
package com.aurora.store.compose.ui.details package com.aurora.store.compose.ui.details
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.FloatingActionButton import androidx.compose.material3.FloatingActionButton
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
@@ -19,6 +22,7 @@ import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
@@ -27,6 +31,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.aurora.Constants.EXODUS_REPORT_URL import com.aurora.Constants.EXODUS_REPORT_URL
@@ -38,6 +43,7 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.Header import com.aurora.store.compose.composable.Header
import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.details.ExodusListItem import com.aurora.store.compose.composable.details.ExodusListItem
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
@@ -103,6 +109,7 @@ private fun ScreenContentReport(
val context = LocalContext.current val context = LocalContext.current
Scaffold( Scaffold(
modifier = Modifier.navigationBarsPadding(),
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
@@ -119,33 +126,42 @@ private fun ScreenContentReport(
} }
} }
) { paddingValues -> ) { paddingValues ->
LazyColumn( val listState = rememberLazyListState()
modifier = Modifier Box(modifier = Modifier.fillMaxSize()) {
.padding(paddingValues) LazyColumn(
.fillMaxSize() modifier = Modifier
.padding(horizontal = dimensionResource(R.dimen.padding_medium)) .padding(paddingValues)
) { .fillMaxSize()
stickyHeader { .padding(horizontal = dimensionResource(R.dimen.padding_medium)),
Surface(modifier = Modifier.fillMaxWidth()) { state = listState
Header( ) {
title = if (report?.trackers.isNullOrEmpty()) { stickyHeader {
stringResource(R.string.exodus_no_tracker) Surface(modifier = Modifier.fillMaxWidth()) {
} else { Header(
stringResource( title = if (report?.trackers.isNullOrEmpty()) {
R.string.exodus_report_trackers, stringResource(R.string.exodus_no_tracker)
report.trackers.size, } else {
report.version stringResource(
) R.string.exodus_report_trackers,
}, report.trackers.size,
subtitle = stringResource(R.string.exodus_view_report), report.version
onClick = { context.browse(EXODUS_REPORT_URL + report!!.id) } )
) },
subtitle = stringResource(R.string.exodus_view_report),
onClick = { context.browse(EXODUS_REPORT_URL + report!!.id) }
)
}
}
items(items = trackers, key = { item -> item.id }) { tracker ->
ExodusListItem(tracker = tracker)
} }
} }
ScrollHint(
items(items = trackers, key = { item -> item.id }) { tracker -> listState = listState,
ExodusListItem(tracker = tracker) bottomPadding = 5.dp,
} modifier = Modifier.align(Alignment.BottomCenter)
)
} }
} }
} }

View File

@@ -6,14 +6,15 @@
package com.aurora.store.compose.ui.details package com.aurora.store.compose.ui.details
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.rememberScrollState import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.Text import androidx.compose.material3.Text
@@ -21,6 +22,7 @@ import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalLocale import androidx.compose.ui.platform.LocalLocale
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
@@ -30,6 +32,7 @@ import androidx.compose.ui.text.fromHtml
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.extensions.adaptiveNavigationIcon
@@ -38,6 +41,7 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.Header import com.aurora.store.compose.composable.Header
import com.aurora.store.compose.composable.Info import com.aurora.store.compose.composable.Info
import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.AppListItem import com.aurora.store.compose.composable.app.AppListItem
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
@@ -83,6 +87,7 @@ private fun ScreenContent(
} }
Scaffold( Scaffold(
modifier = Modifier.navigationBarsPadding(),
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
@@ -91,30 +96,49 @@ private fun ScreenContent(
) )
} }
) { paddingValues -> ) { paddingValues ->
Column( val listState = rememberLazyListState()
modifier = Modifier Box(modifier = Modifier.fillMaxSize()) {
.padding(paddingValues) LazyColumn(
.fillMaxSize() modifier = Modifier
.verticalScroll(rememberScrollState()), .padding(paddingValues)
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) .fillMaxSize(),
) { state = listState,
Header(title = stringResource(R.string.details_description)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
Text( ) {
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.padding_medium)), item {
text = AnnotatedString.fromHtml( Header(title = stringResource(R.string.details_description))
htmlString = app.description }
),
style = MaterialTheme.typography.bodyMedium
)
if (dependencies != null) { item {
AppDependencies( Text(
dependencies = dependencies, modifier = Modifier.padding(
onNavigateToAppDetails = onNavigateToAppDetails horizontal = dimensionResource(R.dimen.padding_medium)
) ),
text = AnnotatedString.fromHtml(
htmlString = app.description
),
style = MaterialTheme.typography.bodyMedium
)
}
item {
if (dependencies != null) {
AppDependencies(
dependencies = dependencies,
onNavigateToAppDetails = onNavigateToAppDetails
)
}
}
item {
AppInfoMore(app = app)
}
} }
ScrollHint(
AppInfoMore(app = app) listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter)
)
} }
} }
} }

View File

@@ -7,15 +7,19 @@ package com.aurora.store.compose.ui.details
import android.content.pm.PermissionInfo import android.content.pm.PermissionInfo
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.material3.adaptive.WindowAdaptiveInfo import androidx.compose.material3.adaptive.WindowAdaptiveInfo
import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo import androidx.compose.material3.adaptive.currentWindowAdaptiveInfo
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.platform.LocalLocale import androidx.compose.ui.platform.LocalLocale
@@ -25,6 +29,7 @@ import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.aurora.extensions.adaptiveNavigationIcon import com.aurora.extensions.adaptiveNavigationIcon
@@ -32,6 +37,7 @@ import com.aurora.extensions.isWindowCompact
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.Info import com.aurora.store.compose.composable.Info
import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
@@ -76,6 +82,7 @@ private fun ScreenContent(
val packageManager = LocalContext.current.packageManager val packageManager = LocalContext.current.packageManager
Scaffold( Scaffold(
modifier = Modifier.navigationBarsPadding(),
topBar = { topBar = {
TopAppBar( TopAppBar(
title = topAppBarTitle, title = topAppBarTitle,
@@ -84,34 +91,43 @@ private fun ScreenContent(
) )
} }
) { paddingValues -> ) { paddingValues ->
LazyColumn( val listState = rememberLazyListState()
modifier = Modifier Box(modifier = Modifier.fillMaxSize()) {
.padding(paddingValues) LazyColumn(
.fillMaxSize() modifier = Modifier
.padding(horizontal = dimensionResource(R.dimen.padding_medium)), .padding(paddingValues)
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium)) .fillMaxSize()
) { .padding(horizontal = dimensionResource(R.dimen.padding_medium)),
items(items = permissionsInfo.keys.toList(), key = { it }) { permission -> state = listState,
val permissionInfo = permissionsInfo.getValue(permission) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
) {
items(items = permissionsInfo.keys.toList(), key = { it }) { permission ->
val permissionInfo = permissionsInfo.getValue(permission)
Info( Info(
title = AnnotatedString( title = AnnotatedString(
text = permissionInfo.loadLabel(packageManager) text = permissionInfo.loadLabel(packageManager)
.toString() .toString()
.replaceFirstChar { .replaceFirstChar {
if (it.isLowerCase()) { if (it.isLowerCase()) {
it.titlecase(LocalLocale.current.platformLocale) it.titlecase(LocalLocale.current.platformLocale)
} else { } else {
it.toString() it.toString()
}
} }
} ),
), description = AnnotatedString(
description = AnnotatedString( text = permissionInfo.loadDescription(packageManager)?.toString()
text = permissionInfo.loadDescription(packageManager)?.toString() ?: stringResource(R.string.no_description)
?: stringResource(R.string.no_description) )
) )
) }
} }
ScrollHint(
listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter)
)
} }
} }
} }

View File

@@ -6,13 +6,17 @@
package com.aurora.store.compose.ui.details package com.aurora.store.compose.ui.details
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChip
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
@@ -24,6 +28,7 @@ import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
@@ -31,6 +36,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState import androidx.paging.LoadState
@@ -45,6 +51,7 @@ import com.aurora.gplayapi.data.models.Review
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.details.ReviewListItem import com.aurora.store.compose.composable.details.ReviewListItem
import com.aurora.store.compose.preview.ReviewPreviewProvider import com.aurora.store.compose.preview.ReviewPreviewProvider
@@ -92,22 +99,23 @@ private fun ScreenContent(
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
) { ) {
Scaffold( Scaffold(
modifier = Modifier.navigationBarsPadding(),
topBar = { topBar = {
TopAppBar( Column {
title = topAppBarTitle, TopAppBar(
navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon, title = topAppBarTitle,
onNavigateUp = onNavigateUp navigationIcon = windowAdaptiveInfo.adaptiveNavigationIcon,
) onNavigateUp = onNavigateUp
)
FilterHeader { filter -> onFilter(filter) }
}
} }
) { paddingValues -> ) { paddingValues ->
Column( Column(
modifier = Modifier modifier = Modifier
.padding(paddingValues) .padding(paddingValues)
.fillMaxSize() .fillMaxSize()
.padding(horizontal = dimensionResource(R.dimen.padding_medium))
) { ) {
FilterHeader { filter -> onFilter(filter) }
when (reviews.loadState.refresh) { when (reviews.loadState.refresh) {
is LoadState.Loading -> ContainedLoadingIndicator() is LoadState.Loading -> ContainedLoadingIndicator()
@@ -120,13 +128,24 @@ private fun ScreenContent(
} }
else -> { else -> {
LazyColumn(modifier = Modifier.fillMaxSize()) { val listState = rememberLazyListState()
items( Box(modifier = Modifier.fillMaxSize()) {
count = reviews.itemCount, LazyColumn(
key = reviews.itemKey { it.commentId } modifier = Modifier.fillMaxSize(),
) { index -> state = listState
reviews[index]?.let { review -> ReviewListItem(review = review) } ) {
items(
count = reviews.itemCount,
key = reviews.itemKey { it.commentId }
) { index ->
reviews[index]?.let { review -> ReviewListItem(review = review) }
}
} }
ScrollHint(
listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter)
)
} }
} }
} }
@@ -155,6 +174,7 @@ private fun FilterHeader(onClick: (filter: Review.Filter) -> Unit) {
LazyRow( LazyRow(
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.margin_normal)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_normal))
) { ) {
items(items = filters.keys.toList(), key = { item -> item }) { filter -> items(items = filters.keys.toList(), key = { item -> item }) { filter ->

View File

@@ -7,8 +7,10 @@ package com.aurora.store.compose.ui.details.composable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.FilledTonalButton
@@ -48,7 +50,9 @@ fun Actions(
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
) { ) {
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
.fillMaxWidth()
.padding(PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium))),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium))
) { ) {
val buttonWidthModifier = when { val buttonWidthModifier = when {

View File

@@ -41,18 +41,27 @@ fun Changelog(changelog: String) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
.background(color = MaterialTheme.colorScheme.secondaryContainer)
.padding(dimensionResource(R.dimen.padding_medium)) .padding(dimensionResource(R.dimen.padding_medium))
) { ) {
Text( Box(
text = if (changelog.isBlank()) { modifier = Modifier
AnnotatedString(text = stringResource(R.string.details_changelog_unavailable)) .fillMaxWidth()
} else { .clip(RoundedCornerShape(dimensionResource(R.dimen.radius_small)))
AnnotatedString.fromHtml(htmlString = changelog) .background(color = MaterialTheme.colorScheme.secondaryContainer)
}, .padding(
style = MaterialTheme.typography.bodyMedium horizontal = dimensionResource(R.dimen.padding_medium),
) vertical = dimensionResource(R.dimen.padding_small)
)
) {
Text(
text = if (changelog.isBlank()) {
AnnotatedString(text = stringResource(R.string.details_changelog_unavailable))
} else {
AnnotatedString.fromHtml(htmlString = changelog)
},
style = MaterialTheme.typography.bodyMedium
)
}
} }
} }

View File

@@ -105,7 +105,11 @@ fun Details(
) )
} }
Row(modifier = Modifier.fillMaxWidth()) { Row(
modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.margin_medium))
) {
AnimatedAppIcon( AnimatedAppIcon(
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_large)), modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_large)),
iconUrl = app.iconArtwork.url, iconUrl = app.iconArtwork.url,

View File

@@ -87,9 +87,11 @@ fun RatingAndReviews(
) )
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_medium)),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_large))
) { ) {
Column( Column(
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)), modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)),
@@ -111,7 +113,6 @@ fun RatingAndReviews(
} }
Column( Column(
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
) { ) {
stars.reversed().fastForEach { star -> stars.reversed().fastForEach { star ->
@@ -127,7 +128,7 @@ fun RatingAndReviews(
val pagerState = rememberPagerState { featuredReviews.size } val pagerState = rememberPagerState { featuredReviews.size }
HorizontalPager( HorizontalPager(
state = pagerState, state = pagerState,
contentPadding = PaddingValues(dimensionResource(R.dimen.padding_large)), contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)),
pageSpacing = dimensionResource(R.dimen.margin_medium) pageSpacing = dimensionResource(R.dimen.margin_medium)
) { page -> ) { page ->
Box( Box(

View File

@@ -8,6 +8,7 @@ package com.aurora.store.compose.ui.details.composable
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
@@ -34,7 +35,10 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
*/ */
@Composable @Composable
fun Screenshots(screenshots: List<Artwork>, onNavigateToScreenshot: (index: Int) -> Unit = {}) { fun Screenshots(screenshots: List<Artwork>, onNavigateToScreenshot: (index: Int) -> Unit = {}) {
LazyRow(horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))) { LazyRow(
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
) {
items(items = screenshots, key = { artwork -> artwork.url }) { artwork -> items(items = screenshots, key = { artwork -> artwork.url }) { artwork ->
ScreenshotListItem( ScreenshotListItem(
modifier = Modifier modifier = Modifier

View File

@@ -8,6 +8,7 @@ package com.aurora.store.compose.ui.details.composable
import android.text.format.Formatter import android.text.format.Formatter
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -61,6 +62,7 @@ fun Tags(app: App) {
).filterKeys { it != null } ).filterKeys { it != null }
LazyRow( LazyRow(
contentPadding = PaddingValues(horizontal = dimensionResource(R.dimen.padding_medium)),
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.padding_medium))
) { ) {
items(items = tags.keys.toList()) { label -> items(items = tags.keys.toList()) { label ->

View File

@@ -9,6 +9,7 @@ import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.FilledTonalButton import androidx.compose.material3.FilledTonalButton
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -40,7 +41,9 @@ import com.aurora.store.compose.preview.ThemePreviewProvider
fun Testing(isSubscribed: Boolean, onTestingSubscriptionChange: (subscribe: Boolean) -> Unit = {}) { fun Testing(isSubscribed: Boolean, onTestingSubscriptionChange: (subscribe: Boolean) -> Unit = {}) {
Header(title = stringResource(R.string.details_beta)) Header(title = stringResource(R.string.details_beta))
Row( Row(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
.fillMaxWidth()
.padding(dimensionResource(R.dimen.padding_medium)),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) horizontalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
) { ) {

View File

@@ -7,16 +7,20 @@ package com.aurora.store.compose.ui.downloads
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
@@ -25,6 +29,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.PagingData import androidx.paging.PagingData
@@ -38,6 +43,7 @@ import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.DownloadListItem import com.aurora.store.compose.composable.DownloadListItem
import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
@@ -124,6 +130,7 @@ private fun ScreenContent(
} }
Scaffold( Scaffold(
modifier = Modifier.navigationBarsPadding(),
topBar = { topBar = {
TopAppBar( TopAppBar(
title = stringResource(R.string.title_download_manager), title = stringResource(R.string.title_download_manager),
@@ -153,23 +160,35 @@ private fun ScreenContent(
message = stringResource(R.string.download_none) message = stringResource(R.string.download_none)
) )
} else { } else {
LazyColumn { val listState = rememberLazyListState()
items( Box(modifier = Modifier.fillMaxSize()) {
count = downloads.itemCount, LazyColumn(
key = downloads.itemKey { it.packageName } state = listState
) { index -> ) {
downloads[index]?.let { download -> items(
DownloadListItem( count = downloads.itemCount,
modifier = Modifier.animateItem(), key = downloads.itemKey { it.packageName }
download = download, ) { index ->
onClick = { onNavigateToAppDetails(download.packageName) }, downloads[index]?.let { download ->
onClear = { onClear(download) }, DownloadListItem(
onCancel = { onCancel(download.packageName) }, modifier = Modifier.animateItem(),
onExport = { onExport(download) }, download = download,
onInstall = { onInstall(download) } onClick = {
) onNavigateToAppDetails(download.packageName)
},
onClear = { onClear(download) },
onCancel = { onCancel(download.packageName) },
onExport = { onExport(download) },
onInstall = { onInstall(download) }
)
}
} }
} }
ScrollHint(
listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter)
)
} }
} }
} }

View File

@@ -7,16 +7,20 @@ package com.aurora.store.compose.ui.favourite
import androidx.activity.compose.rememberLauncherForActivityResult import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource import androidx.compose.ui.res.dimensionResource
@@ -25,6 +29,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.PagingData import androidx.paging.PagingData
@@ -38,6 +43,7 @@ import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.FavouriteListItem import com.aurora.store.compose.composable.FavouriteListItem
import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.preview.FavouritePreviewProvider import com.aurora.store.compose.preview.FavouritePreviewProvider
import com.aurora.store.compose.preview.ThemePreviewProvider import com.aurora.store.compose.preview.ThemePreviewProvider
@@ -126,6 +132,7 @@ private fun ScreenContent(
} }
Scaffold( Scaffold(
modifier = Modifier.navigationBarsPadding(),
topBar = { topBar = {
TopAppBar( TopAppBar(
title = stringResource(R.string.title_favourites_manager), title = stringResource(R.string.title_favourites_manager),
@@ -155,20 +162,32 @@ private fun ScreenContent(
message = stringResource(R.string.details_no_favourites) message = stringResource(R.string.details_no_favourites)
) )
} else { } else {
LazyColumn { val listState = rememberLazyListState()
items( Box(modifier = Modifier.fillMaxSize()) {
count = favourites.itemCount, LazyColumn(
key = favourites.itemKey { it.packageName } state = listState
) { index -> ) {
favourites[index]?.let { favourite -> items(
FavouriteListItem( count = favourites.itemCount,
modifier = Modifier.animateItem(), key = favourites.itemKey { it.packageName }
favourite = favourite, ) { index ->
onClick = { onNavigateToAppDetails(favourite.packageName) }, favourites[index]?.let { favourite ->
onClear = { onRemoveFavourite(favourite.packageName) } FavouriteListItem(
) modifier = Modifier.animateItem(),
favourite = favourite,
onClick = {
onNavigateToAppDetails(favourite.packageName)
},
onClear = { onRemoveFavourite(favourite.packageName) }
)
}
} }
} }
ScrollHint(
listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter)
)
} }
} }
} }

View File

@@ -5,22 +5,27 @@
package com.aurora.store.compose.ui.installed package com.aurora.store.compose.ui.installed
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material3.Scaffold import androidx.compose.material3.Scaffold
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.paging.LoadState import androidx.paging.LoadState
import androidx.paging.PagingData import androidx.paging.PagingData
@@ -32,6 +37,7 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.TopAppBar import com.aurora.store.compose.composable.TopAppBar
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
@@ -70,6 +76,7 @@ private fun ScreenContent(
var initialLoad by rememberSaveable { mutableStateOf(true) } var initialLoad by rememberSaveable { mutableStateOf(true) }
Scaffold( Scaffold(
modifier = Modifier.navigationBarsPadding(),
topBar = { topBar = {
TopAppBar( TopAppBar(
title = stringResource(R.string.title_apps_games), title = stringResource(R.string.title_apps_games),
@@ -97,18 +104,28 @@ private fun ScreenContent(
message = stringResource(R.string.no_apps_available) message = stringResource(R.string.no_apps_available)
) )
} else { } else {
LazyColumn { val listState = rememberLazyListState()
items( Box(modifier = Modifier.fillMaxSize()) {
count = apps.itemCount, LazyColumn(
key = apps.itemKey { it.packageName } state = listState
) { index -> ) {
apps[index]?.let { app -> items(
LargeAppListItem( count = apps.itemCount,
app = app, key = apps.itemKey { it.packageName }
onClick = { onNavigateToAppDetails(app.packageName) } ) { index ->
) apps[index]?.let { app ->
LargeAppListItem(
app = app,
onClick = { onNavigateToAppDetails(app.packageName) }
)
}
} }
} }
ScrollHint(
listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter)
)
} }
} }
} }

View File

@@ -14,10 +14,12 @@ import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.navigationBarsPadding
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.LazyRow import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.items import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.foundation.text.input.clearText import androidx.compose.foundation.text.input.clearText
import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
@@ -45,6 +47,7 @@ import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.runtime.snapshotFlow import androidx.compose.runtime.snapshotFlow
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.FocusRequester
import androidx.compose.ui.focus.focusRequester import androidx.compose.ui.focus.focusRequester
@@ -58,6 +61,7 @@ import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.PreviewParameter import androidx.compose.ui.tooling.preview.PreviewParameter
import androidx.compose.ui.tooling.preview.PreviewScreenSizes import androidx.compose.ui.tooling.preview.PreviewScreenSizes
import androidx.compose.ui.tooling.preview.PreviewWrapper import androidx.compose.ui.tooling.preview.PreviewWrapper
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.paging.LoadState import androidx.paging.LoadState
@@ -70,6 +74,7 @@ import com.aurora.gplayapi.data.models.App
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.composable.ContainedLoadingIndicator import com.aurora.store.compose.composable.ContainedLoadingIndicator
import com.aurora.store.compose.composable.Error import com.aurora.store.compose.composable.Error
import com.aurora.store.compose.composable.ScrollHint
import com.aurora.store.compose.composable.SearchSuggestionListItem import com.aurora.store.compose.composable.SearchSuggestionListItem
import com.aurora.store.compose.composable.app.LargeAppListItem import com.aurora.store.compose.composable.app.LargeAppListItem
import com.aurora.store.compose.preview.AppPreviewProvider import com.aurora.store.compose.preview.AppPreviewProvider
@@ -221,7 +226,9 @@ private fun ScreenContent(
fun ListPane() { fun ListPane() {
// TODO: https://issuetracker.google.com/issues/445720462 // TODO: https://issuetracker.google.com/issues/445720462
Scaffold( Scaffold(
modifier = Modifier.focusable(), modifier = Modifier
.focusable()
.navigationBarsPadding(),
topBar = { SearchBar() } topBar = { SearchBar() }
) { paddingValues -> ) { paddingValues ->
Column( Column(
@@ -255,18 +262,29 @@ private fun ScreenContent(
message = stringResource(R.string.no_apps_available) message = stringResource(R.string.no_apps_available)
) )
} else { } else {
LazyColumn { val listState = rememberLazyListState()
items( Box(modifier = Modifier.fillMaxSize()) {
count = results.itemCount, LazyColumn(
key = { Uuid.random().toString() } state = listState,
) { index -> modifier = Modifier.fillMaxSize()
results[index]?.let { app -> ) {
LargeAppListItem( items(
app = app, count = results.itemCount,
onClick = { showDetailPane(app.packageName) } key = { Uuid.random().toString() }
) ) { index ->
results[index]?.let { app ->
LargeAppListItem(
app = app,
onClick = { showDetailPane(app.packageName) }
)
}
} }
} }
ScrollHint(
listState = listState,
bottomPadding = 5.dp,
modifier = Modifier.align(Alignment.BottomCenter)
)
} }
} }
} }