compose: downloads: Initial migration
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -263,6 +263,7 @@ dependencies {
|
||||
ksp(libs.androidx.room.compiler)
|
||||
implementation(libs.androidx.room.ktx)
|
||||
implementation(libs.androidx.room.runtime)
|
||||
implementation(libs.androidx.room.paging)
|
||||
|
||||
implementation(libs.process.phoenix)
|
||||
|
||||
|
||||
@@ -72,12 +72,12 @@ fun Context.appInfo(packageName: String) {
|
||||
}
|
||||
}
|
||||
|
||||
fun Context.share(app: App) {
|
||||
fun Context.share(displayName: String, packageName: String) {
|
||||
try {
|
||||
val sendIntent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
putExtra(Intent.EXTRA_SUBJECT, app.displayName)
|
||||
putExtra(Intent.EXTRA_TEXT, "${Constants.SHARE_URL}${app.packageName}")
|
||||
putExtra(Intent.EXTRA_SUBJECT, displayName)
|
||||
putExtra(Intent.EXTRA_TEXT, "${Constants.SHARE_URL}${packageName}")
|
||||
type = "text/plain"
|
||||
}
|
||||
startActivity(Intent.createChooser(sendIntent, getString(R.string.action_share)))
|
||||
|
||||
@@ -6,35 +6,43 @@
|
||||
package com.aurora.store.compose.composables
|
||||
|
||||
import android.text.format.Formatter
|
||||
import androidx.compose.foundation.combinedClickable
|
||||
import androidx.compose.animation.AnimatedVisibility
|
||||
import androidx.compose.animation.expandVertically
|
||||
import androidx.compose.animation.fadeIn
|
||||
import androidx.compose.animation.fadeOut
|
||||
import androidx.compose.animation.shrinkVertically
|
||||
import androidx.compose.animation.slideInVertically
|
||||
import androidx.compose.animation.slideOutVertically
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.requiredSize
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.LinearProgressIndicator
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.SplitButtonDefaults
|
||||
import androidx.compose.material3.SplitButtonLayout
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.CompositionLocalProvider
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.tooling.preview.PreviewParameter
|
||||
import coil3.compose.AsyncImage
|
||||
import coil3.compose.LocalAsyncImagePreviewHandler
|
||||
import coil3.request.ImageRequest
|
||||
import coil3.request.crossfade
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composables.app.AnimatedAppIconComposable
|
||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
import com.aurora.store.compose.preview.coilPreviewProvider
|
||||
import com.aurora.store.data.room.download.Download
|
||||
@@ -45,34 +53,42 @@ import com.aurora.store.util.CommonUtil.getETAString
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param download [Download] to display
|
||||
* @param onClick Callback when this composable is clicked
|
||||
* @param onLongClick Callback when this composable is long clicked
|
||||
*/
|
||||
@Composable
|
||||
fun DownloadComposable(
|
||||
modifier: Modifier = Modifier,
|
||||
download: Download,
|
||||
onClick: () -> Unit = {},
|
||||
onLongClick: (() -> Unit) = {}
|
||||
onClear: () -> Unit = {},
|
||||
onCancel: () -> Unit = {},
|
||||
onExport: (() -> Unit)? = null,
|
||||
onInstall: (() -> Unit)? = null,
|
||||
onShare: (() -> Unit)? = null
|
||||
) {
|
||||
Row(
|
||||
modifier = modifier
|
||||
.fillMaxWidth()
|
||||
.combinedClickable(onClick = onClick, onLongClick = onLongClick)
|
||||
val progress = "${download.progress}%"
|
||||
val speed = "${Formatter.formatShortFileSize(LocalContext.current, download.speed)}/s"
|
||||
val eta = getETAString(LocalContext.current, download.timeRemaining)
|
||||
|
||||
var isExpanded by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.clickable(onClick = onClick)
|
||||
.padding(
|
||||
horizontal = dimensionResource(R.dimen.padding_medium),
|
||||
vertical = dimensionResource(R.dimen.padding_xsmall)
|
||||
vertical = dimensionResource(R.dimen.padding_small)
|
||||
)
|
||||
) {
|
||||
AsyncImage(
|
||||
model = ImageRequest.Builder(LocalContext.current)
|
||||
.data(download.iconURL)
|
||||
.crossfade(true)
|
||||
.build(),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier
|
||||
.requiredSize(dimensionResource(R.dimen.icon_size_medium))
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.radius_medium)))
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Row {
|
||||
AnimatedAppIconComposable(
|
||||
modifier = Modifier.requiredSize(dimensionResource(R.dimen.icon_size_medium)),
|
||||
iconUrl = download.iconURL,
|
||||
inProgress = download.isRunning,
|
||||
progress = download.progress.toFloat()
|
||||
)
|
||||
Column(
|
||||
modifier = Modifier.padding(horizontal = dimensionResource(R.dimen.margin_small)),
|
||||
@@ -87,32 +103,76 @@ fun DownloadComposable(
|
||||
text = stringResource(download.downloadStatus.localized),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
LinearProgressIndicator(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
progress = { download.progress.toFloat() }
|
||||
)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Text(
|
||||
text = "${download.progress}%",
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
|
||||
if (download.isRunning) {
|
||||
Text(
|
||||
text = getETAString(LocalContext.current, download.timeRemaining),
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
Text(
|
||||
text = "${Formatter.formatShortFileSize(LocalContext.current, download.speed)}/s",
|
||||
text = "$progress • $speed • $eta",
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SplitButtonLayout(
|
||||
leadingButton = {
|
||||
when {
|
||||
download.isRunning -> {
|
||||
SplitButtonDefaults.LeadingButton(onClick = onCancel) {
|
||||
Text(text = stringResource(R.string.action_cancel))
|
||||
}
|
||||
}
|
||||
|
||||
else -> {
|
||||
SplitButtonDefaults.LeadingButton(onClick = onClear) {
|
||||
Text(text = stringResource(R.string.action_clear))
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
trailingButton = {
|
||||
SplitButtonDefaults.TrailingButton(
|
||||
checked = isExpanded,
|
||||
onCheckedChange = { isExpanded = it }
|
||||
) {
|
||||
Icon(
|
||||
painter = when {
|
||||
isExpanded -> painterResource(R.drawable.ic_keyboard_arrow_up)
|
||||
else -> painterResource(R.drawable.ic_keyboard_arrow_down)
|
||||
},
|
||||
contentDescription = stringResource(R.string.expand)
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
AnimatedVisibility(
|
||||
visible = isExpanded,
|
||||
enter = slideInVertically() + expandVertically() + fadeIn(),
|
||||
exit = slideOutVertically() + shrinkVertically() + fadeOut()
|
||||
) {
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
VerticalButtonComposable(
|
||||
painter = painterResource(R.drawable.ic_share),
|
||||
text = stringResource(R.string.action_share),
|
||||
onClick = onShare
|
||||
)
|
||||
|
||||
VerticalButtonComposable(
|
||||
painter = painterResource(R.drawable.ic_install),
|
||||
text = stringResource(R.string.action_install),
|
||||
onClick = onInstall
|
||||
)
|
||||
|
||||
VerticalButtonComposable(
|
||||
painter = painterResource(R.drawable.ic_file_copy),
|
||||
text = stringResource(R.string.action_export),
|
||||
onClick = onExport
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,66 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.composables
|
||||
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
|
||||
/**
|
||||
* Composable with an icon and text placed vertically to function as a button
|
||||
* @param modifier Modifier
|
||||
* @param painter Icon for the button
|
||||
* @param text Text describing the button's action
|
||||
* @param onClick Callback when this composable is clicked
|
||||
*/
|
||||
@Composable
|
||||
fun VerticalButtonComposable(
|
||||
modifier: Modifier = Modifier,
|
||||
painter: Painter,
|
||||
text: String,
|
||||
onClick: (() -> Unit)? = null
|
||||
) {
|
||||
Column(
|
||||
modifier = modifier
|
||||
.padding(dimensionResource(R.dimen.padding_xsmall))
|
||||
.clip(RoundedCornerShape(dimensionResource(R.dimen.icon_size_default)))
|
||||
.clickable(onClick = { if (onClick != null) onClick() }, enabled = onClick != null)
|
||||
.padding(dimensionResource(R.dimen.padding_medium)),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
painter = painter,
|
||||
contentDescription = text
|
||||
)
|
||||
Text(
|
||||
text = text,
|
||||
style = MaterialTheme.typography.bodySmall
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun VerticalButtonComposablePreview() {
|
||||
VerticalButtonComposable(
|
||||
painter = painterResource(R.drawable.ic_file_copy),
|
||||
text = stringResource(R.string.action_export)
|
||||
)
|
||||
}
|
||||
@@ -34,6 +34,7 @@ import com.aurora.gplayapi.data.models.App
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||
import com.aurora.store.compose.preview.coilPreviewProvider
|
||||
import kotlin.math.abs
|
||||
|
||||
/**
|
||||
* Composable to show icon for an app that can be animated to also show install progress
|
||||
@@ -49,6 +50,10 @@ fun AnimatedAppIconComposable(
|
||||
progress: Float = 0F,
|
||||
inProgress: Boolean = false
|
||||
) {
|
||||
val animatedProgress by animateFloatAsState(
|
||||
targetValue = progress,
|
||||
animationSpec = tween(durationMillis = 300)
|
||||
)
|
||||
val animatedScale by animateFloatAsState(
|
||||
targetValue = if (inProgress) 0.75F else 1F,
|
||||
animationSpec = tween(durationMillis = 300)
|
||||
@@ -61,10 +66,10 @@ fun AnimatedAppIconComposable(
|
||||
Box(modifier = modifier, contentAlignment = Alignment.Center) {
|
||||
if (inProgress) {
|
||||
val indicatorModifier = Modifier.fillMaxSize()
|
||||
if (progress > 0) {
|
||||
if (animatedProgress > 0) {
|
||||
CircularProgressIndicator(
|
||||
modifier = indicatorModifier,
|
||||
progress = { progress / 100 }
|
||||
progress = { animatedProgress / 100 }
|
||||
)
|
||||
} else {
|
||||
CircularProgressIndicator(modifier = indicatorModifier)
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.menu
|
||||
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.material3.DropdownMenu
|
||||
import androidx.compose.material3.DropdownMenuItem
|
||||
import androidx.compose.material3.Icon
|
||||
import androidx.compose.material3.IconButton
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.menu.items.DownloadsMenuItem
|
||||
|
||||
/**
|
||||
* Menu for the blacklist screen
|
||||
* @param modifier The modifier to be applied to the composable
|
||||
* @param onMenuItemClicked Callback when a menu item has been clicked
|
||||
* @see DownloadsMenuItem
|
||||
*/
|
||||
@Composable
|
||||
fun DownloadsMenu(
|
||||
modifier: Modifier = Modifier,
|
||||
isExpanded: Boolean = false,
|
||||
onMenuItemClicked: (menuItem: DownloadsMenuItem) -> Unit = {}
|
||||
) {
|
||||
var expanded by remember { mutableStateOf(isExpanded) }
|
||||
fun onClick(menuItem: DownloadsMenuItem) {
|
||||
onMenuItemClicked(menuItem)
|
||||
expanded = false
|
||||
}
|
||||
|
||||
Box(modifier = modifier) {
|
||||
IconButton(onClick = { expanded = true }) {
|
||||
Icon(
|
||||
painter = painterResource(R.drawable.ic_more_vert),
|
||||
contentDescription = stringResource(R.string.menu)
|
||||
)
|
||||
}
|
||||
DropdownMenu(expanded = expanded, onDismissRequest = { expanded = false }) {
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.download_cancel_all)) },
|
||||
onClick = { onClick(DownloadsMenuItem.CANCEL_ALL) }
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.download_clear_finished)) },
|
||||
onClick = { onClick(DownloadsMenuItem.CLEAR_FINISHED) }
|
||||
)
|
||||
DropdownMenuItem(
|
||||
text = { Text(text = stringResource(R.string.download_force_clear_all)) },
|
||||
onClick = { onClick(DownloadsMenuItem.FORCE_CLEAR_ALL) }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Composable
|
||||
private fun DownloadsMenuPreview() {
|
||||
DownloadsMenu(isExpanded = true)
|
||||
}
|
||||
@@ -0,0 +1,15 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.menu.items
|
||||
|
||||
/**
|
||||
* Valid menu items for the purpose of handling clicks
|
||||
*/
|
||||
enum class DownloadsMenuItem {
|
||||
CANCEL_ALL,
|
||||
FORCE_CLEAR_ALL,
|
||||
CLEAR_FINISHED
|
||||
}
|
||||
@@ -19,6 +19,7 @@ import com.aurora.store.compose.ui.commons.BlacklistScreen
|
||||
import com.aurora.store.compose.ui.details.AppDetailsScreen
|
||||
import com.aurora.store.compose.ui.dev.DevProfileScreen
|
||||
import com.aurora.store.compose.ui.commons.PermissionRationaleScreen
|
||||
import com.aurora.store.compose.ui.downloads.DownloadsScreen
|
||||
import com.aurora.store.compose.ui.search.SearchScreen
|
||||
|
||||
/**
|
||||
@@ -76,6 +77,15 @@ fun NavDisplay(startDestination: NavKey) {
|
||||
onNavigateUp = { onNavigateUp() },
|
||||
)
|
||||
}
|
||||
|
||||
entry<Screen.Downloads> {
|
||||
DownloadsScreen(
|
||||
onNavigateUp = { onNavigateUp() },
|
||||
onNavigateToAppDetails = { packageName ->
|
||||
backstack.add(Screen.AppDetails(packageName))
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -38,4 +38,7 @@ sealed class Screen : NavKey, Parcelable {
|
||||
data class PermissionRationale(
|
||||
val requiredPermissions: Set<PermissionType> = emptySet()
|
||||
) : Screen()
|
||||
|
||||
@Serializable
|
||||
data object Downloads : Screen()
|
||||
}
|
||||
|
||||
@@ -263,7 +263,7 @@ private fun ScreenContentApp(
|
||||
AppDetailsMenuItem.MANUAL_DOWNLOAD -> {
|
||||
showExtraPane(ExtraScreen.ManualDownload)
|
||||
}
|
||||
AppDetailsMenuItem.SHARE -> context.share(app)
|
||||
AppDetailsMenuItem.SHARE -> context.share(app.displayName, app.packageName)
|
||||
AppDetailsMenuItem.APP_INFO -> context.appInfo(app.packageName)
|
||||
AppDetailsMenuItem.PLAY_STORE -> context.browse("$SHARE_URL${app.packageName}")
|
||||
AppDetailsMenuItem.ADD_TO_HOME -> {
|
||||
|
||||
@@ -0,0 +1,188 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.compose.ui.downloads
|
||||
|
||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.material3.Scaffold
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.dimensionResource
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
|
||||
import androidx.paging.LoadState
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.compose.LazyPagingItems
|
||||
import androidx.paging.compose.collectAsLazyPagingItems
|
||||
import androidx.paging.compose.itemKey
|
||||
import com.aurora.extensions.share
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.compose.composables.DownloadComposable
|
||||
import com.aurora.store.compose.composables.ErrorComposable
|
||||
import com.aurora.store.compose.composables.ProgressComposable
|
||||
import com.aurora.store.compose.composables.TopAppBarComposable
|
||||
import com.aurora.store.compose.menu.DownloadsMenu
|
||||
import com.aurora.store.compose.menu.items.DownloadsMenuItem
|
||||
import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.viewmodel.downloads.DownloadsViewModel
|
||||
import kotlinx.coroutines.flow.flowOf
|
||||
|
||||
@Composable
|
||||
fun DownloadsScreen(
|
||||
onNavigateUp: () -> Unit,
|
||||
viewModel: DownloadsViewModel = hiltViewModel(),
|
||||
onNavigateToAppDetails: (packageName: String) -> Unit
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val downloads = viewModel.downloads.collectAsLazyPagingItems()
|
||||
|
||||
val exportMimeType = "application/zip"
|
||||
var requestedExport by rememberSaveable { mutableStateOf<Download?>(null) }
|
||||
val documentLauncher = rememberLauncherForActivityResult(
|
||||
contract = ActivityResultContracts.CreateDocument(exportMimeType),
|
||||
onResult = {
|
||||
if (it != null) {
|
||||
requestedExport?.let { download -> viewModel.export(download, it) }
|
||||
} else {
|
||||
context.toast(R.string.failed_apk_export)
|
||||
}
|
||||
requestedExport = null
|
||||
}
|
||||
)
|
||||
|
||||
ScreenContent(
|
||||
onNavigateUp = onNavigateUp,
|
||||
downloads = downloads,
|
||||
onNavigateToAppDetails = onNavigateToAppDetails,
|
||||
onCancelAll = { viewModel.cancelAll() },
|
||||
onForceClearAll = { viewModel.clearAll() },
|
||||
onClearFinished = { viewModel.clearFinished() },
|
||||
onCancel = { packageName -> viewModel.cancel(packageName) },
|
||||
onShare = { download -> context.share(download.displayName, download.packageName) },
|
||||
onInstall = { download -> viewModel.install(download) },
|
||||
onClear = { download ->
|
||||
viewModel.clear(download.packageName, download.versionCode)
|
||||
},
|
||||
onExport = { download ->
|
||||
requestedExport = download
|
||||
documentLauncher.launch("${download.packageName}.zip")
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ScreenContent(
|
||||
downloads: LazyPagingItems<Download> = flowOf(PagingData.empty<Download>()).collectAsLazyPagingItems(),
|
||||
onNavigateUp: () -> Unit = {},
|
||||
onNavigateToAppDetails: (packageName: String) -> Unit = {},
|
||||
onCancel: (packageName: String) -> Unit = {},
|
||||
onClear: (download: Download) -> Unit = {},
|
||||
onExport: ((download: Download) -> Unit)? = null,
|
||||
onInstall: ((download: Download) -> Unit)? = null,
|
||||
onShare: ((download: Download) -> Unit)? = null,
|
||||
onCancelAll: () -> Unit = {},
|
||||
onForceClearAll: () -> Unit = {},
|
||||
onClearFinished: () -> Unit = {}
|
||||
) {
|
||||
/*
|
||||
* For some reason paging3 frequently out-of-nowhere invalidates the list which causes
|
||||
* the loading animation to play again even if the keys are same causing a glitching effect.
|
||||
*
|
||||
* Save the initial loading state to make sure we don't replay the loading animation again.
|
||||
*/
|
||||
var initialLoad by rememberSaveable { mutableStateOf(true) }
|
||||
|
||||
@Composable
|
||||
fun SetupMenu() {
|
||||
DownloadsMenu { menuItem ->
|
||||
when (menuItem) {
|
||||
DownloadsMenuItem.CANCEL_ALL -> onCancelAll()
|
||||
DownloadsMenuItem.FORCE_CLEAR_ALL -> onForceClearAll()
|
||||
DownloadsMenuItem.CLEAR_FINISHED -> onClearFinished()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
TopAppBarComposable(
|
||||
title = stringResource(R.string.title_download_manager),
|
||||
onNavigateUp = onNavigateUp,
|
||||
actions = { if (downloads.itemCount != 0) SetupMenu() }
|
||||
)
|
||||
}
|
||||
) { paddingValues ->
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(paddingValues)
|
||||
.fillMaxSize()
|
||||
.padding(vertical = dimensionResource(R.dimen.padding_medium))
|
||||
) {
|
||||
when {
|
||||
downloads.loadState.refresh is LoadState.Loading && initialLoad -> {
|
||||
ProgressComposable()
|
||||
}
|
||||
|
||||
else -> {
|
||||
initialLoad = false
|
||||
|
||||
if (downloads.itemCount == 0) {
|
||||
ErrorComposable(
|
||||
modifier = Modifier.padding(paddingValues),
|
||||
icon = painterResource(R.drawable.ic_disclaimer),
|
||||
message = stringResource(R.string.download_none)
|
||||
)
|
||||
} else {
|
||||
LazyColumn {
|
||||
items(
|
||||
count = downloads.itemCount,
|
||||
key = downloads.itemKey { it.packageName }
|
||||
) { index ->
|
||||
downloads[index]?.let { download ->
|
||||
DownloadComposable(
|
||||
download = download,
|
||||
onClick = { onNavigateToAppDetails(download.packageName) },
|
||||
onClear = { onClear(download) },
|
||||
onShare = { onShare!!(download) },
|
||||
onCancel = { onCancel(download.packageName) },
|
||||
onExport = if (download.canInstall(LocalContext.current)) {
|
||||
{ onExport!!(download) }
|
||||
} else {
|
||||
null
|
||||
},
|
||||
onInstall = if (download.isSuccessful) {
|
||||
{ onInstall!!(download) }
|
||||
} else {
|
||||
null
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
private fun DownloadsScreenPreview() {
|
||||
ScreenContent()
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.aurora.store.data.helper
|
||||
|
||||
import android.content.Context
|
||||
import android.util.Log
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.work.Data
|
||||
import androidx.work.ExistingWorkPolicy
|
||||
import androidx.work.OneTimeWorkRequestBuilder
|
||||
@@ -43,9 +45,11 @@ class DownloadHelper @Inject constructor(
|
||||
private const val VERSION_CODE = "VERSION_CODE"
|
||||
}
|
||||
|
||||
val downloadsList = downloadDao.downloads()
|
||||
val downloadsList get() = downloadDao.downloads()
|
||||
.stateIn(AuroraApp.scope, SharingStarted.WhileSubscribed(), emptyList())
|
||||
|
||||
val pagedDownloads get() = downloadDao.pagedDownloads()
|
||||
|
||||
private val TAG = DownloadHelper::class.java.simpleName
|
||||
|
||||
/**
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package com.aurora.store.data.room.download
|
||||
|
||||
import android.content.Context
|
||||
import android.os.Parcelable
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.room.Entity
|
||||
import androidx.room.PrimaryKey
|
||||
import com.aurora.gplayapi.data.models.App
|
||||
@@ -8,6 +10,7 @@ import com.aurora.gplayapi.data.models.PlayFile
|
||||
import com.aurora.store.data.model.DownloadStatus
|
||||
import com.aurora.store.data.room.suite.ExternalApk
|
||||
import com.aurora.store.data.room.update.Update
|
||||
import com.aurora.store.util.PathUtil
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.Date
|
||||
|
||||
@@ -35,6 +38,7 @@ data class Download(
|
||||
) : Parcelable {
|
||||
val isFinished get() = downloadStatus in DownloadStatus.finished
|
||||
val isRunning get() = downloadStatus in DownloadStatus.running
|
||||
val isSuccessful get() = downloadStatus == DownloadStatus.COMPLETED
|
||||
|
||||
companion object {
|
||||
fun fromApp(app: App): Download {
|
||||
@@ -104,4 +108,9 @@ data class Download(
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
fun canInstall(context: Context): Boolean {
|
||||
val dir = PathUtil.getAppDownloadDir(context, packageName, versionCode)
|
||||
return isSuccessful && dir.listFiles() != null
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
package com.aurora.store.data.room.download
|
||||
|
||||
import androidx.paging.PagingSource
|
||||
import androidx.room.Dao
|
||||
import androidx.room.Insert
|
||||
import androidx.room.OnConflictStrategy
|
||||
@@ -40,6 +41,9 @@ interface DownloadDao {
|
||||
@Query("SELECT * FROM download")
|
||||
fun downloads(): Flow<List<Download>>
|
||||
|
||||
@Query("SELECT * FROM download ORDER BY downloadedAt DESC")
|
||||
fun pagedDownloads(): PagingSource<Int, Download>
|
||||
|
||||
@Query("SELECT * FROM download WHERE packageName = :packageName")
|
||||
suspend fun getDownload(packageName: String): Download
|
||||
|
||||
|
||||
@@ -95,7 +95,7 @@ object NotificationUtil {
|
||||
val builder = NotificationCompat.Builder(context, Constants.NOTIFICATION_CHANNEL_DOWNLOADS)
|
||||
builder.setSmallIcon(R.drawable.ic_notification_outlined)
|
||||
builder.setContentTitle(download.displayName)
|
||||
builder.setContentIntent(getContentIntentForDownloads(context))
|
||||
builder.setContentIntent(getContentIntentForDetails(context, download.packageName))
|
||||
builder.setLargeIcon(largeIcon)
|
||||
|
||||
val cancelIntent = Intent(context, DownloadCancelReceiver::class.java).apply {
|
||||
@@ -379,14 +379,6 @@ object NotificationUtil {
|
||||
.createPendingIntent()
|
||||
}
|
||||
|
||||
private fun getContentIntentForDownloads(context: Context): PendingIntent {
|
||||
return NavDeepLinkBuilder(context)
|
||||
.setGraph(R.navigation.mobile_navigation)
|
||||
.setDestination(R.id.downloadFragment)
|
||||
.setComponentName(MainActivity::class.java)
|
||||
.createPendingIntent()
|
||||
}
|
||||
|
||||
private fun getInstallIntent(context: Context, download: Download): PendingIntent? {
|
||||
val intent = Intent(context, InstallActivity::class.java).apply {
|
||||
putExtra(Constants.PARCEL_DOWNLOAD, download)
|
||||
|
||||
@@ -1,88 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* Aurora Store is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Aurora Store is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.aurora.store.view.epoxy.views
|
||||
|
||||
import android.content.Context
|
||||
import android.text.format.Formatter
|
||||
import android.util.AttributeSet
|
||||
import coil3.load
|
||||
import coil3.request.placeholder
|
||||
import coil3.request.transformations
|
||||
import coil3.transform.RoundedCornersTransformation
|
||||
import com.airbnb.epoxy.CallbackProp
|
||||
import com.airbnb.epoxy.ModelProp
|
||||
import com.airbnb.epoxy.ModelView
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.model.DownloadStatus
|
||||
import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.databinding.ViewDownloadBinding
|
||||
import com.aurora.store.util.CommonUtil.getETAString
|
||||
|
||||
@ModelView(
|
||||
autoLayout = ModelView.Size.MATCH_WIDTH_WRAP_HEIGHT,
|
||||
baseModelClass = BaseModel::class
|
||||
)
|
||||
class DownloadView @JvmOverloads constructor(
|
||||
context: Context?,
|
||||
attrs: AttributeSet? = null,
|
||||
defStyleAttr: Int = 0
|
||||
) : BaseView<ViewDownloadBinding>(context, attrs, defStyleAttr) {
|
||||
|
||||
@ModelProp
|
||||
fun download(download: Download) {
|
||||
binding.imgDownload.load(download.iconURL) {
|
||||
placeholder(R.drawable.bg_placeholder)
|
||||
transformations(RoundedCornersTransformation(32F))
|
||||
}
|
||||
binding.txtTitle.text = download.displayName
|
||||
binding.txtStatus.text = context.getString(download.downloadStatus.localized)
|
||||
|
||||
binding.progressDownload.apply {
|
||||
progress = download.progress
|
||||
isIndeterminate = download.progress <= 0 && !download.isFinished
|
||||
}
|
||||
binding.txtProgress.text = ("${download.progress}%")
|
||||
|
||||
binding.txtEta.text = getETAString(context, download.timeRemaining)
|
||||
binding.txtSpeed.text = "${Formatter.formatShortFileSize(context, download.speed)}/s"
|
||||
|
||||
when (download.downloadStatus) {
|
||||
DownloadStatus.DOWNLOADING, DownloadStatus.QUEUED -> {
|
||||
binding.txtSpeed.visibility = VISIBLE
|
||||
binding.txtEta.visibility = VISIBLE
|
||||
}
|
||||
|
||||
else -> {
|
||||
binding.txtSpeed.visibility = INVISIBLE
|
||||
binding.txtEta.visibility = INVISIBLE
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@CallbackProp
|
||||
fun click(onClickListener: OnClickListener?) {
|
||||
binding.root.setOnClickListener(onClickListener)
|
||||
}
|
||||
|
||||
@CallbackProp
|
||||
fun longClick(onClickListener: OnLongClickListener?) {
|
||||
binding.root.setOnLongClickListener(onClickListener)
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ class AppsContainerFragment : BaseFragment<FragmentAppsGamesBinding>() {
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_download_manager -> {
|
||||
findNavController().navigate(R.id.downloadFragment)
|
||||
requireContext().navigate(Screen.Downloads)
|
||||
}
|
||||
|
||||
R.id.menu_more -> {
|
||||
|
||||
@@ -1,140 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* Aurora Store is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Aurora Store is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.aurora.store.view.ui.downloads
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateUtils
|
||||
import android.view.View
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.findNavController
|
||||
import com.aurora.Constants.GITLAB_URL
|
||||
import com.aurora.extensions.browse
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.databinding.FragmentDownloadBinding
|
||||
import com.aurora.store.view.epoxy.views.DownloadViewModel_
|
||||
import com.aurora.store.view.epoxy.views.TextDividerViewModel_
|
||||
import com.aurora.store.view.epoxy.views.app.NoAppViewModel_
|
||||
import com.aurora.store.view.ui.commons.BaseFragment
|
||||
import com.aurora.store.viewmodel.downloads.DownloadViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.flow.collectLatest
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.Date
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DownloadFragment : BaseFragment<FragmentDownloadBinding>() {
|
||||
|
||||
private val viewModel: DownloadViewModel by viewModels()
|
||||
|
||||
private lateinit var downloadList: List<Download>
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
// Toolbar
|
||||
binding.toolbar.apply {
|
||||
setNavigationOnClickListener { findNavController().navigateUp() }
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.action_force_clear_all -> {
|
||||
viewLifecycleOwner.lifecycleScope.launch(NonCancellable) {
|
||||
viewModel.downloadHelper.clearAllDownloads()
|
||||
}
|
||||
}
|
||||
|
||||
R.id.action_cancel_all -> {
|
||||
viewLifecycleOwner.lifecycleScope.launch(NonCancellable) {
|
||||
viewModel.downloadHelper.cancelAll()
|
||||
}
|
||||
}
|
||||
|
||||
R.id.action_clear_completed -> {
|
||||
viewLifecycleOwner.lifecycleScope.launch(NonCancellable) {
|
||||
viewModel.downloadHelper.clearFinishedDownloads()
|
||||
}
|
||||
}
|
||||
}
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
viewLifecycleOwner.lifecycleScope.launch {
|
||||
viewModel.downloadHelper.downloadsList.collectLatest {
|
||||
downloadList = it
|
||||
updateController(it.reversed())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun updateController(downloads: List<Download>) {
|
||||
binding.recycler.withModels {
|
||||
if (downloads.isEmpty()) {
|
||||
add(
|
||||
NoAppViewModel_()
|
||||
.id("no_downloads")
|
||||
.message(R.string.download_none)
|
||||
)
|
||||
} else {
|
||||
downloads.groupBy {
|
||||
DateUtils.getRelativeTimeSpanString(
|
||||
it.downloadedAt,
|
||||
Date().time,
|
||||
DateUtils.DAY_IN_MILLIS
|
||||
).toString()
|
||||
}.forEach { (date, downloadList) ->
|
||||
add(
|
||||
TextDividerViewModel_()
|
||||
.id(date)
|
||||
.title(date)
|
||||
)
|
||||
|
||||
downloadList.forEach {
|
||||
add(
|
||||
DownloadViewModel_()
|
||||
.id(it.packageName)
|
||||
.download(it)
|
||||
.click { _ ->
|
||||
if (it.packageName == requireContext().packageName) {
|
||||
requireContext().browse(GITLAB_URL)
|
||||
} else {
|
||||
openDetailsFragment(it.packageName)
|
||||
}
|
||||
}
|
||||
.longClick { _ ->
|
||||
openDownloadMenuSheet(it.packageName)
|
||||
true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun openDownloadMenuSheet(packageName: String) {
|
||||
val download = downloadList.find { it.packageName == packageName }!!
|
||||
findNavController().navigate(
|
||||
DownloadFragmentDirections.actionDownloadFragmentToDownloadMenuSheet(download)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -69,7 +69,7 @@ class GamesContainerFragment : BaseFragment<FragmentAppsGamesBinding>() {
|
||||
setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_download_manager -> {
|
||||
findNavController().navigate(R.id.downloadFragment)
|
||||
requireContext().navigate(Screen.Downloads)
|
||||
}
|
||||
|
||||
R.id.menu_more -> {
|
||||
|
||||
@@ -1,130 +0,0 @@
|
||||
/*
|
||||
* Aurora Store
|
||||
* Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
*
|
||||
* Aurora Store is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 2 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* Aurora Store is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*/
|
||||
|
||||
package com.aurora.store.view.ui.sheets
|
||||
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.fragment.app.viewModels
|
||||
import androidx.lifecycle.findViewTreeLifecycleOwner
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.navigation.fragment.navArgs
|
||||
import com.aurora.extensions.copyToClipBoard
|
||||
import com.aurora.extensions.toast
|
||||
import com.aurora.store.R
|
||||
import com.aurora.store.data.model.DownloadStatus
|
||||
import com.aurora.store.databinding.SheetDownloadMenuBinding
|
||||
import com.aurora.store.util.PathUtil
|
||||
import com.aurora.store.viewmodel.sheets.DownloadMenuViewModel
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DownloadMenuSheet : BaseDialogSheet<SheetDownloadMenuBinding>() {
|
||||
|
||||
private val TAG = DownloadMenuSheet::class.java.simpleName
|
||||
|
||||
private val viewModel: DownloadMenuViewModel by viewModels()
|
||||
private val args: DownloadMenuSheetArgs by navArgs()
|
||||
private val playStoreURL = "https://play.google.com/store/apps/details?id="
|
||||
|
||||
private val exportMimeType = "application/zip"
|
||||
|
||||
private val requestDocumentCreation =
|
||||
registerForActivityResult(ActivityResultContracts.CreateDocument(exportMimeType)) {
|
||||
if (it != null) {
|
||||
viewModel.copyDownloadedApp(requireContext(), args.download, it)
|
||||
} else {
|
||||
toast(R.string.failed_apk_export)
|
||||
}
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
|
||||
with(binding.navigationView) {
|
||||
val downloadCompleted = args.download.downloadStatus == DownloadStatus.COMPLETED
|
||||
val downloadDir = PathUtil.getAppDownloadDir(
|
||||
requireContext(),
|
||||
args.download.packageName,
|
||||
args.download.versionCode
|
||||
)
|
||||
|
||||
menu.findItem(R.id.action_cancel).isVisible = !args.download.isFinished
|
||||
menu.findItem(R.id.action_clear).isVisible = args.download.isFinished
|
||||
menu.findItem(R.id.action_install).isVisible = downloadCompleted
|
||||
menu.findItem(R.id.action_local).isVisible =
|
||||
downloadCompleted && downloadDir.listFiles() != null
|
||||
|
||||
setNavigationItemSelectedListener { item ->
|
||||
when (item.itemId) {
|
||||
R.id.action_install -> {
|
||||
install()
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
R.id.action_copy -> {
|
||||
requireContext().copyToClipBoard(
|
||||
"${playStoreURL}${args.download.packageName}"
|
||||
)
|
||||
requireContext().toast(requireContext().getString(R.string.toast_clipboard_copied))
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
R.id.action_cancel -> {
|
||||
findViewTreeLifecycleOwner()?.lifecycleScope?.launch(NonCancellable) {
|
||||
viewModel.downloadHelper.cancelDownload(args.download.packageName)
|
||||
}
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
R.id.action_clear -> {
|
||||
findViewTreeLifecycleOwner()?.lifecycleScope?.launch(NonCancellable) {
|
||||
viewModel.downloadHelper.clearDownload(
|
||||
args.download.packageName,
|
||||
args.download.versionCode
|
||||
)
|
||||
}
|
||||
dismissAllowingStateLoss()
|
||||
}
|
||||
|
||||
R.id.action_local -> {
|
||||
requestDocumentCreation.launch("${args.download.packageName}.zip")
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun install() {
|
||||
try {
|
||||
viewModel.appInstaller.getPreferredInstaller().install(args.download)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to install ${args.download.packageName}", exception)
|
||||
if (exception is NullPointerException) {
|
||||
requireContext().toast(R.string.installer_status_failure_invalid)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -73,7 +73,7 @@ class UpdatesFragment : BaseFragment<FragmentUpdatesBinding>() {
|
||||
binding.toolbar.setOnMenuItemClickListener {
|
||||
when (it.itemId) {
|
||||
R.id.menu_download_manager -> {
|
||||
findNavController().navigate(R.id.downloadFragment)
|
||||
requireContext().navigate(Screen.Downloads)
|
||||
}
|
||||
|
||||
R.id.menu_more -> {
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.viewmodel.downloads
|
||||
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.aurora.store.data.helper.DownloadHelper
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DownloadViewModel @Inject constructor(val downloadHelper: DownloadHelper) : ViewModel()
|
||||
@@ -0,0 +1,102 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.viewmodel.downloads
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import android.util.Log
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import androidx.paging.Pager
|
||||
import androidx.paging.PagingConfig
|
||||
import androidx.paging.PagingData
|
||||
import androidx.paging.cachedIn
|
||||
import com.aurora.store.data.helper.DownloadHelper
|
||||
import com.aurora.store.data.installer.AppInstaller
|
||||
import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.data.work.ExportWorker
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DownloadsViewModel @Inject constructor(
|
||||
private val downloadHelper: DownloadHelper,
|
||||
private val appInstaller: AppInstaller,
|
||||
@ApplicationContext private val context: Context
|
||||
) : ViewModel() {
|
||||
|
||||
private val TAG = DownloadsViewModel::class.java.simpleName
|
||||
|
||||
private val _downloads = MutableStateFlow<PagingData<Download>>(PagingData.empty())
|
||||
val downloads = _downloads.asStateFlow()
|
||||
|
||||
init {
|
||||
getPagedDownloads()
|
||||
}
|
||||
|
||||
fun cancel(packageName: String) {
|
||||
viewModelScope.launch(NonCancellable) {
|
||||
downloadHelper.cancelDownload(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
fun cancelAll() {
|
||||
viewModelScope.launch {
|
||||
downloadHelper.cancelAll()
|
||||
}
|
||||
}
|
||||
|
||||
fun clear(packageName: String, versionCode: Long) {
|
||||
viewModelScope.launch(NonCancellable) {
|
||||
downloadHelper.clearDownload(packageName, versionCode)
|
||||
}
|
||||
}
|
||||
|
||||
fun clearFinished() {
|
||||
viewModelScope.launch {
|
||||
downloadHelper.clearFinishedDownloads()
|
||||
}
|
||||
}
|
||||
|
||||
fun clearAll() {
|
||||
viewModelScope.launch {
|
||||
downloadHelper.clearAllDownloads()
|
||||
}
|
||||
}
|
||||
|
||||
fun install(download: Download) {
|
||||
try {
|
||||
appInstaller.getPreferredInstaller().install(download)
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to install ${download.packageName}", exception)
|
||||
}
|
||||
}
|
||||
|
||||
fun export(download: Download, uri: Uri) {
|
||||
ExportWorker.exportDownloadedApp(context, download, uri)
|
||||
}
|
||||
|
||||
private fun getPagedDownloads() {
|
||||
Pager(
|
||||
config = PagingConfig(
|
||||
pageSize = 20,
|
||||
enablePlaceholders = true
|
||||
),
|
||||
pagingSourceFactory = { downloadHelper.pagedDownloads }
|
||||
).flow.distinctUntilChanged()
|
||||
.cachedIn(viewModelScope)
|
||||
.onEach { _downloads.value = it }
|
||||
.launchIn(viewModelScope)
|
||||
}
|
||||
}
|
||||
@@ -1,27 +0,0 @@
|
||||
/*
|
||||
* SPDX-FileCopyrightText: 2025 The Calyx Institute
|
||||
* SPDX-License-Identifier: GPL-3.0-or-later
|
||||
*/
|
||||
|
||||
package com.aurora.store.viewmodel.sheets
|
||||
|
||||
import android.content.Context
|
||||
import android.net.Uri
|
||||
import androidx.lifecycle.ViewModel
|
||||
import com.aurora.store.data.helper.DownloadHelper
|
||||
import com.aurora.store.data.installer.AppInstaller
|
||||
import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.data.work.ExportWorker
|
||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||
import javax.inject.Inject
|
||||
|
||||
@HiltViewModel
|
||||
class DownloadMenuViewModel @Inject constructor(
|
||||
val downloadHelper: DownloadHelper,
|
||||
val appInstaller: AppInstaller
|
||||
) : ViewModel() {
|
||||
|
||||
fun copyDownloadedApp(context: Context, download: Download, uri: Uri) {
|
||||
ExportWorker.exportDownloadedApp(context, download, uri)
|
||||
}
|
||||
}
|
||||
14
app/src/main/res/drawable/ic_keyboard_arrow_down.xml
Normal file
14
app/src/main/res/drawable/ic_keyboard_arrow_down.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: Material Design Authors / Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480,616L240,376L296,320L480,504L664,320L720,376L480,616Z"/>
|
||||
</vector>
|
||||
14
app/src/main/res/drawable/ic_keyboard_arrow_up.xml
Normal file
14
app/src/main/res/drawable/ic_keyboard_arrow_up.xml
Normal file
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!--
|
||||
~ SPDX-FileCopyrightText: Material Design Authors / Google LLC
|
||||
~ SPDX-License-Identifier: Apache-2.0
|
||||
-->
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="960"
|
||||
android:viewportHeight="960">
|
||||
<path
|
||||
android:fillColor="@android:color/white"
|
||||
android:pathData="M480,432L296,616L240,560L480,320L720,560L664,616L480,432Z"/>
|
||||
</vector>
|
||||
@@ -1,46 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Aurora Store
|
||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
~
|
||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 2 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Aurora Store is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
~
|
||||
-->
|
||||
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:orientation="vertical"
|
||||
tools:context=".view.ui.downloads.DownloadFragment">
|
||||
|
||||
<androidx.appcompat.widget.Toolbar
|
||||
android:id="@+id/toolbar"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:minHeight="?attr/actionBarSize"
|
||||
app:menu="@menu/menu_download_main"
|
||||
app:navigationIcon="@drawable/ic_arrow_back"
|
||||
app:title="@string/title_download_manager" />
|
||||
|
||||
<com.airbnb.epoxy.EpoxyRecyclerView
|
||||
android:id="@+id/recycler"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:clipToPadding="true"
|
||||
android:paddingBottom="@dimen/height_bottom_adj"
|
||||
app:layoutManager="androidx.recyclerview.widget.LinearLayoutManager"
|
||||
app:stackFromEnd="false"
|
||||
tools:listitem="@layout/view_download" />
|
||||
</LinearLayout>
|
||||
@@ -1,34 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Aurora Store
|
||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
~
|
||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 2 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Aurora Store is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
~
|
||||
-->
|
||||
|
||||
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
app:layout_behavior="com.google.android.material.bottomsheet.BottomSheetBehavior">
|
||||
|
||||
<com.google.android.material.navigation.NavigationView
|
||||
android:id="@+id/navigation_view"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="@android:color/transparent"
|
||||
android:padding="@dimen/padding_small"
|
||||
app:elevation="0dp"
|
||||
app:menu="@menu/menu_download_single" />
|
||||
</FrameLayout>
|
||||
@@ -1,115 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Aurora Store
|
||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
~
|
||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 2 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Aurora Store is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
~
|
||||
-->
|
||||
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:background="?android:attr/selectableItemBackground"
|
||||
android:paddingStart="@dimen/padding_medium"
|
||||
android:paddingTop="@dimen/padding_xsmall"
|
||||
android:paddingEnd="@dimen/padding_small"
|
||||
android:paddingBottom="@dimen/padding_xsmall">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatImageView
|
||||
android:id="@+id/img_download"
|
||||
android:layout_width="@dimen/icon_size_medium"
|
||||
android:layout_height="@dimen/icon_size_medium"
|
||||
android:layout_centerVertical="true"
|
||||
android:src="@drawable/bg_placeholder" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/txt_title"
|
||||
style="@style/AuroraTextStyle.Line1"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginStart="@dimen/margin_small"
|
||||
android:layout_toEndOf="@id/img_download"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
tools:text="App Name" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/txt_status"
|
||||
style="@style/AuroraTextStyle.Line2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/txt_title"
|
||||
android:layout_alignStart="@id/txt_title"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
tools:text="Status" />
|
||||
|
||||
<com.google.android.material.progressindicator.LinearProgressIndicator
|
||||
android:id="@+id/progress_download"
|
||||
style="@style/Widget.Material3.LinearProgressIndicator.Legacy"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_below="@id/txt_status"
|
||||
android:layout_alignStart="@id/txt_title"
|
||||
android:indeterminate="false"
|
||||
android:max="100"
|
||||
android:paddingTop="@dimen/padding_xxsmall"
|
||||
android:paddingBottom="@dimen/padding_xxsmall"
|
||||
tools:progress="75" />
|
||||
|
||||
<LinearLayout
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_below="@id/progress_download"
|
||||
android:layout_alignStart="@id/txt_title"
|
||||
android:orientation="horizontal"
|
||||
android:weightSum="3">
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/txt_progress"
|
||||
style="@style/AuroraTextStyle.Line2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewStart"
|
||||
tools:text="75%" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/txt_eta"
|
||||
style="@style/AuroraTextStyle.Line2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="center"
|
||||
tools:text="15 sec" />
|
||||
|
||||
<androidx.appcompat.widget.AppCompatTextView
|
||||
android:id="@+id/txt_speed"
|
||||
style="@style/AuroraTextStyle.Line2"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_weight="1"
|
||||
android:ellipsize="end"
|
||||
android:singleLine="true"
|
||||
android:textAlignment="viewEnd"
|
||||
tools:text="1.5Mb/s" />
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
@@ -1,30 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Aurora Store
|
||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
~
|
||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 2 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Aurora Store is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
~
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_cancel_all"
|
||||
android:title="@string/download_cancel_all" />
|
||||
<item
|
||||
android:id="@+id/action_clear_completed"
|
||||
android:title="@string/download_clear_finished" />
|
||||
<item
|
||||
android:id="@+id/action_force_clear_all"
|
||||
android:title="@string/download_force_clear_all" />
|
||||
</menu>
|
||||
@@ -1,36 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Aurora Store
|
||||
~ Copyright (C) 2021, Rahul Kumar Patel <whyorean@gmail.com>
|
||||
~
|
||||
~ Aurora Store is free software: you can redistribute it and/or modify
|
||||
~ it under the terms of the GNU General Public License as published by
|
||||
~ the Free Software Foundation, either version 2 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ Aurora Store is distributed in the hope that it will be useful,
|
||||
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
~ GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License
|
||||
~ along with Aurora Store. If not, see <http://www.gnu.org/licenses/>.
|
||||
~
|
||||
-->
|
||||
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<item
|
||||
android:id="@+id/action_local"
|
||||
android:title="@string/action_get_local_apk" />
|
||||
<item
|
||||
android:id="@+id/action_install"
|
||||
android:title="@string/action_install" />
|
||||
<item
|
||||
android:id="@+id/action_copy"
|
||||
android:title="@string/action_copy_link" />
|
||||
<item
|
||||
android:id="@+id/action_cancel"
|
||||
android:title="@string/action_cancel" />
|
||||
<item
|
||||
android:id="@+id/action_clear"
|
||||
android:title="@string/action_clear" />
|
||||
</menu>
|
||||
@@ -1,15 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<menu xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
<item
|
||||
android:id="@+id/action_clear"
|
||||
android:icon="@drawable/ic_cancel"
|
||||
android:title="@string/action_clear"
|
||||
android:visible="false"
|
||||
app:showAsAction="ifRoom" />
|
||||
<item
|
||||
android:id="@+id/action_download"
|
||||
android:icon="@drawable/ic_download_manager"
|
||||
android:title="@string/title_download_manager"
|
||||
app:showAsAction="ifRoom" />
|
||||
</menu>
|
||||
@@ -110,15 +110,6 @@
|
||||
android:name="com.aurora.store.view.ui.preferences.updates.UpdatesPreference"
|
||||
android:label="UpdatesPreference"
|
||||
tools:layout="@layout/fragment_setting" />
|
||||
<fragment
|
||||
android:id="@+id/downloadFragment"
|
||||
android:name="com.aurora.store.view.ui.downloads.DownloadFragment"
|
||||
android:label="@string/title_download_manager"
|
||||
tools:layout="@layout/fragment_download">
|
||||
<action
|
||||
android:id="@+id/action_downloadFragment_to_downloadMenuSheet"
|
||||
app:destination="@id/downloadMenuSheet" />
|
||||
</fragment>
|
||||
<fragment
|
||||
android:id="@+id/categoryBrowseFragment"
|
||||
android:name="com.aurora.store.view.ui.commons.CategoryBrowseFragment"
|
||||
@@ -272,20 +263,6 @@
|
||||
android:name="app"
|
||||
app:argType="com.aurora.store.data.model.MinimalApp" />
|
||||
</dialog>
|
||||
<dialog
|
||||
android:id="@+id/filterSheet"
|
||||
android:name="com.aurora.store.view.ui.sheets.FilterSheet"
|
||||
android:label="FilterSheet"
|
||||
tools:layout="@layout/sheet_filter" />
|
||||
<dialog
|
||||
android:id="@+id/downloadMenuSheet"
|
||||
android:name="com.aurora.store.view.ui.sheets.DownloadMenuSheet"
|
||||
android:label="DownloadMenuSheet"
|
||||
tools:layout="@layout/sheet_download_menu">
|
||||
<argument
|
||||
android:name="download"
|
||||
app:argType="com.aurora.store.data.room.download.Download" />
|
||||
</dialog>
|
||||
<dialog
|
||||
android:id="@+id/deviceMiuiSheet"
|
||||
android:name="com.aurora.store.view.ui.sheets.DeviceMiuiSheet"
|
||||
|
||||
@@ -75,6 +75,7 @@ androidx-preference-ktx = { module = "androidx.preference:preference-ktx", versi
|
||||
androidx-room-compiler = { module = "androidx.room:room-compiler", version.ref = "room" }
|
||||
androidx-room-ktx = { module = "androidx.room:room-ktx", version.ref = "room" }
|
||||
androidx-room-runtime = { module = "androidx.room:room-runtime", version.ref = "room" }
|
||||
androidx-room-paging = { module = "androidx.room:room-paging", version.ref = "room" }
|
||||
androidx-swiperefreshlayout = { module = "androidx.swiperefreshlayout:swiperefreshlayout", version.ref = "swiperefreshlayout" }
|
||||
androidx-ui = { group = "androidx.compose.ui", name = "ui" }
|
||||
androidx-ui-graphics = { group = "androidx.compose.ui", name = "ui-graphics" }
|
||||
|
||||
Reference in New Issue
Block a user