Merge branch 'dev'
This commit is contained in:
@@ -135,7 +135,7 @@ fun AppDetailsScreen(
|
|||||||
exodusReport = exodusReport,
|
exodusReport = exodusReport,
|
||||||
onNavigateUp = onNavigateUp,
|
onNavigateUp = onNavigateUp,
|
||||||
onNavigateToAppDetails = onNavigateToAppDetails,
|
onNavigateToAppDetails = onNavigateToAppDetails,
|
||||||
onDownload = { viewModel.purchase(app!!) },
|
onDownload = { viewModel.enqueueDownload(app!!) },
|
||||||
onFavorite = { viewModel.toggleFavourite(app!!) },
|
onFavorite = { viewModel.toggleFavourite(app!!) },
|
||||||
onCancelDownload = { viewModel.cancelDownload(app!!) },
|
onCancelDownload = { viewModel.cancelDownload(app!!) },
|
||||||
onUninstall = { AppInstaller.uninstall(context, packageName) },
|
onUninstall = { AppInstaller.uninstall(context, packageName) },
|
||||||
@@ -274,14 +274,14 @@ private fun ScreenContentApp(
|
|||||||
@Composable
|
@Composable
|
||||||
fun SetupActions() {
|
fun SetupActions() {
|
||||||
when (state) {
|
when (state) {
|
||||||
|
is AppState.Queued,
|
||||||
is AppState.Purchasing,
|
is AppState.Purchasing,
|
||||||
is AppState.Downloading -> {
|
is AppState.Downloading -> {
|
||||||
Actions(
|
Actions(
|
||||||
primaryActionDisplayName = stringResource(R.string.action_open),
|
primaryActionDisplayName = stringResource(R.string.action_open),
|
||||||
secondaryActionDisplayName = stringResource(R.string.action_cancel),
|
secondaryActionDisplayName = stringResource(R.string.action_cancel),
|
||||||
isPrimaryActionEnabled = false,
|
isPrimaryActionEnabled = false,
|
||||||
onSecondaryAction = onCancelDownload,
|
onSecondaryAction = onCancelDownload
|
||||||
isSecondaryActionEnabled = state !is AppState.Purchasing
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -299,7 +299,9 @@ private fun ScreenContentApp(
|
|||||||
primaryActionDisplayName = stringResource(R.string.action_open),
|
primaryActionDisplayName = stringResource(R.string.action_open),
|
||||||
secondaryActionDisplayName = stringResource(R.string.action_uninstall),
|
secondaryActionDisplayName = stringResource(R.string.action_uninstall),
|
||||||
onPrimaryAction = onOpen,
|
onPrimaryAction = onOpen,
|
||||||
onSecondaryAction = onUninstall
|
onSecondaryAction = onUninstall,
|
||||||
|
isPrimaryActionEnabled = PackageUtil
|
||||||
|
.getLaunchIntent(context, app.packageName) != null
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,9 +12,12 @@ import androidx.compose.foundation.layout.fillMaxSize
|
|||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.imePadding
|
import androidx.compose.foundation.layout.imePadding
|
||||||
import androidx.compose.foundation.layout.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.layout.requiredSize
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
import androidx.compose.material3.Button
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ContainedLoadingIndicator
|
||||||
import androidx.compose.material3.FilledTonalButton
|
import androidx.compose.material3.FilledTonalButton
|
||||||
import androidx.compose.material3.OutlinedTextField
|
import androidx.compose.material3.OutlinedTextField
|
||||||
import androidx.compose.material3.Scaffold
|
import androidx.compose.material3.Scaffold
|
||||||
@@ -33,7 +36,7 @@ import androidx.compose.runtime.setValue
|
|||||||
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
|
||||||
import androidx.compose.ui.platform.LocalContext
|
import androidx.compose.ui.platform.LocalFocusManager
|
||||||
import androidx.compose.ui.res.dimensionResource
|
import androidx.compose.ui.res.dimensionResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
import androidx.compose.ui.text.AnnotatedString
|
import androidx.compose.ui.text.AnnotatedString
|
||||||
@@ -49,12 +52,12 @@ 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
|
||||||
import com.aurora.extensions.isWindowCompact
|
import com.aurora.extensions.isWindowCompact
|
||||||
import com.aurora.extensions.toast
|
|
||||||
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.composables.InfoComposable
|
import com.aurora.store.compose.composables.InfoComposable
|
||||||
import com.aurora.store.compose.composables.TopAppBarComposable
|
import com.aurora.store.compose.composables.TopAppBarComposable
|
||||||
import com.aurora.store.compose.preview.AppPreviewProvider
|
import com.aurora.store.compose.preview.AppPreviewProvider
|
||||||
|
import com.aurora.store.data.model.AppState
|
||||||
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
import com.aurora.store.viewmodel.details.AppDetailsViewModel
|
||||||
import kotlinx.coroutines.android.awaitFrame
|
import kotlinx.coroutines.android.awaitFrame
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
@@ -66,26 +69,21 @@ fun ManualDownloadScreen(
|
|||||||
viewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
|
viewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
|
||||||
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
|
||||||
) {
|
) {
|
||||||
val context = LocalContext.current
|
|
||||||
|
|
||||||
val app by viewModel.app.collectAsStateWithLifecycle()
|
val app by viewModel.app.collectAsStateWithLifecycle()
|
||||||
|
val state by viewModel.state.collectAsStateWithLifecycle()
|
||||||
val topAppBarTitle = when {
|
val topAppBarTitle = when {
|
||||||
windowAdaptiveInfo.isWindowCompact -> app!!.displayName
|
windowAdaptiveInfo.isWindowCompact -> app!!.displayName
|
||||||
else -> stringResource(R.string.title_manual_download)
|
else -> stringResource(R.string.title_manual_download)
|
||||||
}
|
}
|
||||||
|
|
||||||
LaunchedEffect(key1 = Unit) {
|
LaunchedEffect(key1 = state) {
|
||||||
viewModel.purchaseStatus.collect { success ->
|
if (state is AppState.Downloading) {
|
||||||
if (success) {
|
onNavigateUp()
|
||||||
context.toast(R.string.toast_manual_available)
|
|
||||||
onNavigateUp()
|
|
||||||
} else {
|
|
||||||
context.toast(R.string.toast_manual_unavailable)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
ScreenContent(
|
ScreenContent(
|
||||||
|
state = state,
|
||||||
topAppBarTitle = topAppBarTitle,
|
topAppBarTitle = topAppBarTitle,
|
||||||
currentVersionCode = app!!.versionCode,
|
currentVersionCode = app!!.versionCode,
|
||||||
onNavigateUp = onNavigateUp,
|
onNavigateUp = onNavigateUp,
|
||||||
@@ -98,13 +96,14 @@ fun ManualDownloadScreen(
|
|||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
viewModel.purchase(requestedApp)
|
viewModel.enqueueDownload(requestedApp)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
private fun ScreenContent(
|
private fun ScreenContent(
|
||||||
|
state: AppState = AppState.Unavailable,
|
||||||
topAppBarTitle: String? = null,
|
topAppBarTitle: String? = null,
|
||||||
currentVersionCode: Long = 0L,
|
currentVersionCode: Long = 0L,
|
||||||
onNavigateUp: () -> Unit = {},
|
onNavigateUp: () -> Unit = {},
|
||||||
@@ -116,6 +115,7 @@ private fun ScreenContent(
|
|||||||
val snackBarHostState = remember { SnackbarHostState() }
|
val snackBarHostState = remember { SnackbarHostState() }
|
||||||
val errorMessage = stringResource(R.string.manual_download_version_error)
|
val errorMessage = stringResource(R.string.manual_download_version_error)
|
||||||
|
|
||||||
|
val focusManager = LocalFocusManager.current
|
||||||
val focusRequester = remember { FocusRequester() }
|
val focusRequester = remember { FocusRequester() }
|
||||||
var versionCode by remember {
|
var versionCode by remember {
|
||||||
val initText = currentVersionCode.toString()
|
val initText = currentVersionCode.toString()
|
||||||
@@ -157,6 +157,7 @@ private fun ScreenContent(
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.focusRequester(focusRequester),
|
.focusRequester(focusRequester),
|
||||||
|
enabled = !state.inProgress(),
|
||||||
value = versionCode,
|
value = versionCode,
|
||||||
onValueChange = {
|
onValueChange = {
|
||||||
if (it.text.isDigitsOnly()) {
|
if (it.text.isDigitsOnly()) {
|
||||||
@@ -167,9 +168,16 @@ private fun ScreenContent(
|
|||||||
},
|
},
|
||||||
shape = RoundedCornerShape(10.dp),
|
shape = RoundedCornerShape(10.dp),
|
||||||
singleLine = true,
|
singleLine = true,
|
||||||
keyboardOptions = KeyboardOptions(
|
keyboardOptions = KeyboardOptions(keyboardType = KeyboardType.Number),
|
||||||
keyboardType = KeyboardType.Number
|
keyboardActions = KeyboardActions(onDone = { focusManager.clearFocus() }),
|
||||||
)
|
trailingIcon = {
|
||||||
|
if (state.inProgress()) {
|
||||||
|
ContainedLoadingIndicator(
|
||||||
|
modifier = Modifier
|
||||||
|
.requiredSize(dimensionResource(R.dimen.icon_size_default))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -182,7 +190,7 @@ private fun ScreenContent(
|
|||||||
onClick = onNavigateUp
|
onClick = onNavigateUp
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.action_cancel),
|
text = stringResource(R.string.action_close),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
overflow = TextOverflow.Ellipsis
|
overflow = TextOverflow.Ellipsis
|
||||||
)
|
)
|
||||||
@@ -190,7 +198,12 @@ private fun ScreenContent(
|
|||||||
|
|
||||||
Button(
|
Button(
|
||||||
modifier = Modifier.weight(1F),
|
modifier = Modifier.weight(1F),
|
||||||
onClick = { onDownload(versionCode.text.toLong()) }) {
|
enabled = !state.inProgress(),
|
||||||
|
onClick = {
|
||||||
|
onDownload(versionCode.text.toLong())
|
||||||
|
focusManager.clearFocus()
|
||||||
|
}
|
||||||
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = stringResource(R.string.action_install),
|
text = stringResource(R.string.action_install),
|
||||||
maxLines = 1,
|
maxLines = 1,
|
||||||
@@ -204,9 +217,7 @@ private fun ScreenContent(
|
|||||||
|
|
||||||
@Preview
|
@Preview
|
||||||
@Composable
|
@Composable
|
||||||
private fun ManualDownloadScreenPreview(
|
private fun ManualDownloadScreenPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
|
||||||
@PreviewParameter(AppPreviewProvider::class) app: App
|
|
||||||
) {
|
|
||||||
ScreenContent(
|
ScreenContent(
|
||||||
topAppBarTitle = app.displayName,
|
topAppBarTitle = app.displayName,
|
||||||
currentVersionCode = app.versionCode
|
currentVersionCode = app.versionCode
|
||||||
|
|||||||
@@ -97,9 +97,8 @@ fun Details(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
AppState.Purchasing::class -> {
|
AppState.Queued::class -> stringResource(R.string.status_queued)
|
||||||
stringResource(R.string.preparing_to_install)
|
AppState.Purchasing::class -> stringResource(R.string.preparing_to_install)
|
||||||
}
|
|
||||||
|
|
||||||
else -> {
|
else -> {
|
||||||
stringResource(R.string.version, versionName, versionCode)
|
stringResource(R.string.version, versionName, versionCode)
|
||||||
|
|||||||
@@ -10,10 +10,11 @@ enum class DownloadStatus(@StringRes val localized: Int) {
|
|||||||
COMPLETED(R.string.status_completed),
|
COMPLETED(R.string.status_completed),
|
||||||
QUEUED(R.string.status_queued),
|
QUEUED(R.string.status_queued),
|
||||||
UNAVAILABLE(R.string.status_unavailable),
|
UNAVAILABLE(R.string.status_unavailable),
|
||||||
VERIFYING(R.string.status_verifying);
|
VERIFYING(R.string.status_verifying),
|
||||||
|
PURCHASING(R.string.preparing_to_install);
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val finished = listOf(FAILED, CANCELLED, COMPLETED)
|
val finished = listOf(FAILED, CANCELLED, COMPLETED)
|
||||||
val running = listOf(QUEUED, DOWNLOADING)
|
val running = listOf(QUEUED, PURCHASING, DOWNLOADING)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -55,6 +55,7 @@ sealed class AppState {
|
|||||||
val timeRemaining: Long
|
val timeRemaining: Long
|
||||||
) : AppState()
|
) : AppState()
|
||||||
|
|
||||||
|
data object Queued : AppState()
|
||||||
data object Purchasing : AppState()
|
data object Purchasing : AppState()
|
||||||
data class Installing(val progress: Float) : AppState()
|
data class Installing(val progress: Float) : AppState()
|
||||||
data class Error(val message: String?) : AppState()
|
data class Error(val message: String?) : AppState()
|
||||||
@@ -68,7 +69,7 @@ sealed class AppState {
|
|||||||
* Whether there is some sort of ongoing process related to the app
|
* Whether there is some sort of ongoing process related to the app
|
||||||
*/
|
*/
|
||||||
fun inProgress(): Boolean {
|
fun inProgress(): Boolean {
|
||||||
return this is Downloading || this is Installing || this is Purchasing
|
return this is Downloading || this is Installing || this is Purchasing || this is Queued
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -171,8 +171,9 @@ class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient): IH
|
|||||||
responseBytes = response.body.bytes(),
|
responseBytes = response.body.bytes(),
|
||||||
errorString = if (!response.isSuccessful) response.message else String()
|
errorString = if (!response.isSuccessful) response.message else String()
|
||||||
).also {
|
).also {
|
||||||
|
val isCached = if (response.cacheResponse != null) "CACHED" else "NETWORK"
|
||||||
_responseCode.value = response.code
|
_responseCode.value = response.code
|
||||||
Log.i(TAG, "OKHTTP [${response.code}] ${response.request.url}")
|
Log.i(TAG, "OKHTTP [$isCached] [${response.code}] ${response.request.url}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -34,9 +34,11 @@ import dagger.hilt.InstallIn
|
|||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import dagger.hilt.components.SingletonComponent
|
import dagger.hilt.components.SingletonComponent
|
||||||
import kotlinx.serialization.json.Json
|
import kotlinx.serialization.json.Json
|
||||||
|
import okhttp3.Cache
|
||||||
import okhttp3.CertificatePinner
|
import okhttp3.CertificatePinner
|
||||||
import okhttp3.OkHttpClient
|
import okhttp3.OkHttpClient
|
||||||
import java.io.ByteArrayInputStream
|
import java.io.ByteArrayInputStream
|
||||||
|
import java.io.File
|
||||||
import java.io.InputStream
|
import java.io.InputStream
|
||||||
import java.net.Authenticator
|
import java.net.Authenticator
|
||||||
import java.net.InetSocketAddress
|
import java.net.InetSocketAddress
|
||||||
@@ -59,8 +61,13 @@ object OkHttpClientModule {
|
|||||||
|
|
||||||
@Provides
|
@Provides
|
||||||
@Singleton
|
@Singleton
|
||||||
fun providesOkHttpClientInstance(certPinner: CertificatePinner, proxy: Proxy?): OkHttpClient {
|
fun providesOkHttpClientInstance(
|
||||||
|
certificatePinner: CertificatePinner,
|
||||||
|
proxy: Proxy?,
|
||||||
|
cache: Cache
|
||||||
|
): OkHttpClient {
|
||||||
val okHttpClientBuilder = OkHttpClient().newBuilder()
|
val okHttpClientBuilder = OkHttpClient().newBuilder()
|
||||||
|
.cache(cache)
|
||||||
.proxy(proxy)
|
.proxy(proxy)
|
||||||
.connectTimeout(25, TimeUnit.SECONDS)
|
.connectTimeout(25, TimeUnit.SECONDS)
|
||||||
.readTimeout(25, TimeUnit.SECONDS)
|
.readTimeout(25, TimeUnit.SECONDS)
|
||||||
@@ -70,7 +77,7 @@ object OkHttpClientModule {
|
|||||||
.followSslRedirects(true)
|
.followSslRedirects(true)
|
||||||
|
|
||||||
if (!BuildConfig.DEBUG) {
|
if (!BuildConfig.DEBUG) {
|
||||||
okHttpClientBuilder.certificatePinner(certPinner)
|
okHttpClientBuilder.certificatePinner(certificatePinner)
|
||||||
}
|
}
|
||||||
|
|
||||||
return okHttpClientBuilder.build()
|
return okHttpClientBuilder.build()
|
||||||
@@ -122,6 +129,15 @@ object OkHttpClientModule {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Provides
|
||||||
|
@Singleton
|
||||||
|
fun providesCacheDir(@ApplicationContext context: Context): Cache {
|
||||||
|
return Cache(
|
||||||
|
directory = File(context.cacheDir, "http_cache"),
|
||||||
|
maxSize = 100L * 1024 * 1024
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
private fun getGoogleRootCertHashes(context: Context): List<String> {
|
private fun getGoogleRootCertHashes(context: Context): List<String> {
|
||||||
return try {
|
return try {
|
||||||
val certs = getX509Certificates(context.resources.openRawResource(R.raw.google_roots_ca))
|
val certs = getX509Certificates(context.resources.openRawResource(R.raw.google_roots_ca))
|
||||||
|
|||||||
@@ -111,6 +111,7 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
setForeground(getForegroundInfo())
|
setForeground(getForegroundInfo())
|
||||||
|
|
||||||
// Try to purchase the app if file list is empty
|
// Try to purchase the app if file list is empty
|
||||||
|
notifyStatus(DownloadStatus.PURCHASING)
|
||||||
download.fileList = download.fileList.ifEmpty {
|
download.fileList = download.fileList.ifEmpty {
|
||||||
purchase(download.packageName, download.versionCode, download.offerType)
|
purchase(download.packageName, download.versionCode, download.offerType)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,7 +17,6 @@ import com.aurora.gplayapi.data.models.App
|
|||||||
import com.aurora.gplayapi.data.models.Review
|
import com.aurora.gplayapi.data.models.Review
|
||||||
import com.aurora.gplayapi.data.models.details.TestingProgramStatus
|
import com.aurora.gplayapi.data.models.details.TestingProgramStatus
|
||||||
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
import com.aurora.gplayapi.helpers.AppDetailsHelper
|
||||||
import com.aurora.gplayapi.helpers.PurchaseHelper
|
|
||||||
import com.aurora.gplayapi.helpers.ReviewsHelper
|
import com.aurora.gplayapi.helpers.ReviewsHelper
|
||||||
import com.aurora.gplayapi.helpers.web.WebDataSafetyHelper
|
import com.aurora.gplayapi.helpers.web.WebDataSafetyHelper
|
||||||
import com.aurora.gplayapi.network.IHttpClient
|
import com.aurora.gplayapi.network.IHttpClient
|
||||||
@@ -26,6 +25,7 @@ import com.aurora.store.BuildConfig
|
|||||||
import com.aurora.store.data.event.InstallerEvent
|
import com.aurora.store.data.event.InstallerEvent
|
||||||
import com.aurora.store.data.helper.DownloadHelper
|
import com.aurora.store.data.helper.DownloadHelper
|
||||||
import com.aurora.store.data.model.AppState
|
import com.aurora.store.data.model.AppState
|
||||||
|
import com.aurora.store.data.model.DownloadStatus
|
||||||
import com.aurora.store.data.model.ExodusReport
|
import com.aurora.store.data.model.ExodusReport
|
||||||
import com.aurora.store.data.model.PlexusReport
|
import com.aurora.store.data.model.PlexusReport
|
||||||
import com.aurora.store.data.model.Report
|
import com.aurora.store.data.model.Report
|
||||||
@@ -40,11 +40,8 @@ import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_EXTENDED
|
|||||||
import dagger.hilt.android.lifecycle.HiltViewModel
|
import dagger.hilt.android.lifecycle.HiltViewModel
|
||||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.NonCancellable
|
|
||||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.SharingStarted
|
import kotlinx.coroutines.flow.SharingStarted
|
||||||
import kotlinx.coroutines.flow.asSharedFlow
|
|
||||||
import kotlinx.coroutines.flow.asStateFlow
|
import kotlinx.coroutines.flow.asStateFlow
|
||||||
import kotlinx.coroutines.flow.combine
|
import kotlinx.coroutines.flow.combine
|
||||||
import kotlinx.coroutines.flow.filter
|
import kotlinx.coroutines.flow.filter
|
||||||
@@ -62,7 +59,6 @@ import com.aurora.gplayapi.data.models.datasafety.Report as DataSafetyReport
|
|||||||
class AppDetailsViewModel @Inject constructor(
|
class AppDetailsViewModel @Inject constructor(
|
||||||
val authProvider: AuthProvider,
|
val authProvider: AuthProvider,
|
||||||
@ApplicationContext private val context: Context,
|
@ApplicationContext private val context: Context,
|
||||||
private val purchaseHelper: PurchaseHelper,
|
|
||||||
private val appDetailsHelper: AppDetailsHelper,
|
private val appDetailsHelper: AppDetailsHelper,
|
||||||
private val reviewsHelper: ReviewsHelper,
|
private val reviewsHelper: ReviewsHelper,
|
||||||
private val webDataSafetyHelper: WebDataSafetyHelper,
|
private val webDataSafetyHelper: WebDataSafetyHelper,
|
||||||
@@ -104,9 +100,6 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
private val _favourite = MutableStateFlow(false)
|
private val _favourite = MutableStateFlow(false)
|
||||||
val favourite = _favourite.asStateFlow()
|
val favourite = _favourite.asStateFlow()
|
||||||
|
|
||||||
private val _purchaseStatus = MutableSharedFlow<Boolean>()
|
|
||||||
val purchaseStatus = _purchaseStatus.asSharedFlow()
|
|
||||||
|
|
||||||
private val download = combine(app, downloadHelper.downloadsList) { a, list ->
|
private val download = combine(app, downloadHelper.downloadsList) { a, list ->
|
||||||
if (a?.packageName.isNullOrBlank()) return@combine null
|
if (a?.packageName.isNullOrBlank()) return@combine null
|
||||||
list.find { d -> d.packageName == a.packageName }
|
list.find { d -> d.packageName == a.packageName }
|
||||||
@@ -219,20 +212,9 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun purchase(app: App) {
|
fun enqueueDownload(app: App) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
downloadHelper.enqueueApp(app)
|
||||||
_state.value = AppState.Purchasing
|
|
||||||
val files = purchaseHelper.purchase(app.packageName, app.versionCode, app.offerType)
|
|
||||||
_purchaseStatus.emit(files.isNotEmpty())
|
|
||||||
if (files.isNotEmpty()) {
|
|
||||||
downloadHelper.enqueueApp(app.copy(fileList = files.toMutableList()))
|
|
||||||
}
|
|
||||||
} catch (exception: Exception) {
|
|
||||||
_state.value = defaultAppState
|
|
||||||
_purchaseStatus.emit(false)
|
|
||||||
Log.e(TAG, "Failed to purchase the app", exception)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -271,13 +253,15 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
}.launchIn(viewModelScope)
|
}.launchIn(viewModelScope)
|
||||||
|
|
||||||
download.filterNotNull().onEach {
|
download.filterNotNull().onEach {
|
||||||
_state.value = when {
|
_state.value = when (it.status) {
|
||||||
it.isRunning -> AppState.Downloading(
|
DownloadStatus.DOWNLOADING -> AppState.Downloading(
|
||||||
it.progress.toFloat(),
|
it.progress.toFloat(),
|
||||||
it.speed,
|
it.speed,
|
||||||
it.timeRemaining
|
it.timeRemaining
|
||||||
)
|
)
|
||||||
|
|
||||||
|
DownloadStatus.QUEUED -> AppState.Queued
|
||||||
|
DownloadStatus.PURCHASING -> AppState.Purchasing
|
||||||
else -> defaultAppState
|
else -> defaultAppState
|
||||||
}
|
}
|
||||||
}.launchIn(viewModelScope)
|
}.launchIn(viewModelScope)
|
||||||
|
|||||||
Reference in New Issue
Block a user