compose: onboarding: Add back microG page for required flavors

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2025-12-09 13:37:53 +08:00
parent 08b2f0421e
commit e988a9af2f
7 changed files with 228 additions and 65 deletions

View File

@@ -0,0 +1,136 @@
/*
* SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.compose.composable
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
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.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.util.fastForEach
import com.aurora.extensions.browse
import com.aurora.store.R
import com.aurora.store.compose.preview.PreviewTemplate
import com.aurora.store.data.model.Link
/**
* Composable to display suggestion to install microG
* @param modifier Modifier for the composable
* @param isTOSChecked Whether the TOS checkbox is checked
* @param onInstall Callback when user requests installing microG bundle
*/
@Composable
fun MicroG(
modifier: Modifier = Modifier,
isTOSChecked: Boolean = false,
onInstall: () -> Unit = {}
) {
val context = LocalContext.current
var isChecked by rememberSaveable { mutableStateOf(isTOSChecked) }
val links = listOf(
Link(
id = 2,
title = stringResource(R.string.details_dev_website),
subtitle = stringResource(R.string.microg_website),
icon = R.drawable.ic_network,
url = "https://microG.org"
),
Link(
id = 4,
title = stringResource(R.string.privacy_policy_title),
subtitle = stringResource(R.string.microg_privacy_policy),
icon = R.drawable.ic_privacy,
url = "https://microg.org/privacy.html"
),
Link(
id = 5,
title = stringResource(R.string.menu_disclaimer),
subtitle = stringResource(R.string.microg_license_agreement),
icon = R.drawable.ic_disclaimer,
url = "https://raw.githubusercontent.com/microg/GmsCore/refs/heads/master/LICENSE"
)
)
Column(
modifier = modifier
.fillMaxSize()
.padding(
horizontal = dimensionResource(R.dimen.padding_small),
vertical = dimensionResource(R.dimen.padding_xxsmall)
),
verticalArrangement = Arrangement.SpaceBetween
) {
Column(
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_xxsmall))
) {
Text(
text = stringResource(R.string.onboarding_gms_microg),
style = MaterialTheme.typography.bodyMedium,
)
links.fastForEach { link ->
LinkListItem(
link = link,
onClick = { context.browse(link.url) },
iconTint = MaterialTheme.colorScheme.primary
)
}
}
Column(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = dimensionResource(R.dimen.padding_xlarge)),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(
dimensionResource(R.dimen.padding_small)
)
) {
Checkbox(checked = isChecked, onCheckedChange = { isChecked = !isChecked })
Text(
text = stringResource(R.string.onboarding_gms_agreement),
style = MaterialTheme.typography.bodyMedium,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
Button(onClick = onInstall, enabled = isChecked) {
Text(
text = stringResource(R.string.action_install_microG)
)
}
}
}
}
@Preview(showBackground = true)
@Composable
private fun MicroGPreview() {
PreviewTemplate {
MicroG(isTOSChecked = true)
}
}

View File

@@ -0,0 +1,72 @@
/*
* SPDX-FileCopyrightText: 2025 The Calyx Institute
* SPDX-License-Identifier: GPL-3.0-or-later
*/
package com.aurora.store.compose.ui.onboarding
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.dimensionResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.tooling.preview.Preview
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.aurora.store.R
import com.aurora.store.compose.composable.MicroG
import com.aurora.store.compose.preview.PreviewTemplate
import com.aurora.store.viewmodel.onboarding.MicroGViewModel
@Composable
fun MicroGPage(viewModel: MicroGViewModel = hiltViewModel()) {
ScreenContent(
onInstall = { viewModel.downloadMicroG() }
)
}
@Composable
private fun ScreenContent(onInstall: () -> Unit = {}) {
Column(
modifier = Modifier
.fillMaxSize()
.padding(horizontal = dimensionResource(R.dimen.padding_medium))
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_medium))
) {
Column(
modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium))
) {
Text(
text = stringResource(R.string.onboarding_title_gsf),
style = MaterialTheme.typography.headlineLargeEmphasized,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
text = stringResource(R.string.onboarding_title_gsf_desc),
style = MaterialTheme.typography.titleLarge,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
}
MicroG(onInstall = onInstall)
}
}
@Preview(showBackground = true)
@Composable
private fun MicroGPagePreview() {
PreviewTemplate {
ScreenContent()
}
}

View File

@@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable
import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Alignment 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.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.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
@@ -36,14 +37,17 @@ import com.aurora.store.compose.composable.Logo
import com.aurora.store.compose.composable.PageIndicator import com.aurora.store.compose.composable.PageIndicator
import com.aurora.store.compose.preview.PreviewTemplate import com.aurora.store.compose.preview.PreviewTemplate
import com.aurora.store.compose.ui.onboarding.navigation.OnboardingPage import com.aurora.store.compose.ui.onboarding.navigation.OnboardingPage
import com.aurora.store.util.FlavouredUtil
import com.aurora.store.viewmodel.onboarding.OnboardingViewModel import com.aurora.store.viewmodel.onboarding.OnboardingViewModel
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
@Composable @Composable
fun OnboardingScreen(viewModel: OnboardingViewModel = hiltViewModel()) { fun OnboardingScreen(viewModel: OnboardingViewModel = hiltViewModel()) {
val context = LocalContext.current
val pages = listOfNotNull( val pages = listOfNotNull(
OnboardingPage.WELCOME, OnboardingPage.WELCOME,
OnboardingPage.PERMISSIONS OnboardingPage.PERMISSIONS,
if (FlavouredUtil.promptMicroGInstall(context)) OnboardingPage.MICRO_G else null
) )
ScreenContent( ScreenContent(
@@ -171,6 +175,7 @@ private fun ScreenContent(
when (pages[page]) { when (pages[page]) {
OnboardingPage.WELCOME -> WelcomePage() OnboardingPage.WELCOME -> WelcomePage()
OnboardingPage.PERMISSIONS -> PermissionsPage() OnboardingPage.PERMISSIONS -> PermissionsPage()
OnboardingPage.MICRO_G -> MicroGPage()
} }
} }
} }

View File

@@ -51,7 +51,7 @@ private fun PageContent(
header = { header = {
Surface(modifier = Modifier.fillMaxWidth()) { Surface(modifier = Modifier.fillMaxWidth()) {
Column( Column(
modifier = Modifier.padding(dimensionResource(R.dimen.padding_normal)) modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium))
) { ) {
Text( Text(
text = stringResource(R.string.onboarding_title_permissions), text = stringResource(R.string.onboarding_title_permissions),

View File

@@ -102,7 +102,7 @@ private fun PageContent(onAboutAurora: () -> Unit = {}) {
stickyHeader { stickyHeader {
Surface(modifier = Modifier.fillMaxWidth()) { Surface(modifier = Modifier.fillMaxWidth()) {
Column( Column(
modifier = Modifier.padding(dimensionResource(R.dimen.padding_normal)) modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium))
) { ) {
Text( Text(
text = stringResource(R.string.onboarding_title_welcome), text = stringResource(R.string.onboarding_title_welcome),

View File

@@ -11,4 +11,5 @@ package com.aurora.store.compose.ui.onboarding.navigation
enum class OnboardingPage { enum class OnboardingPage {
WELCOME, WELCOME,
PERMISSIONS, PERMISSIONS,
MICRO_G
} }

View File

@@ -5,52 +5,32 @@
package com.aurora.store.viewmodel.onboarding package com.aurora.store.viewmodel.onboarding
import android.content.Context
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.aurora.Constants.PACKAGE_NAME_GMS import com.aurora.Constants.PACKAGE_NAME_GMS
import com.aurora.Constants.PACKAGE_NAME_PLAY_STORE import com.aurora.Constants.PACKAGE_NAME_PLAY_STORE
import com.aurora.gplayapi.data.models.PlayFile import com.aurora.gplayapi.data.models.PlayFile
import com.aurora.store.AuroraApp
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.room.suite.ExternalApk import com.aurora.store.data.room.suite.ExternalApk
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asSharedFlow
import kotlinx.coroutines.flow.combine
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
@HiltViewModel @HiltViewModel
class MicroGViewModel @Inject constructor( class MicroGViewModel @Inject constructor(
@ApplicationContext private val context: Context,
private val downloadHelper: DownloadHelper private val downloadHelper: DownloadHelper
) : ViewModel() { ) : ViewModel() {
companion object { companion object {
const val MICROG_DOWNLOAD_URL = "https://github.com/microg/GmsCore/releases/download" private const val MICROG_DOWNLOAD_URL =
const val MICROG_VERSION = "v0.3.6.244735" "https://github.com/microg/GmsCore/releases/download"
const val GMS_VERSION_CODE = 244735012 private const val MICROG_VERSION = "v0.3.6.244735"
const val COMPANION_VERSION_CODE = 84022612 private const val GMS_VERSION_CODE = 244735012
private const val COMPANION_VERSION_CODE = 84022612
} }
private val _packageNames = MutableSharedFlow<List<String>>() private val microGServiceApk = ExternalApk(
val packageNames = _packageNames.asSharedFlow()
private val _checked = MutableStateFlow(false)
val checked: StateFlow<Boolean> = _checked
val download = combine(packageNames, downloadHelper.downloadsList) { apps, list ->
list.find { d -> apps.any { it == d.packageName } }
}.stateIn(viewModelScope, SharingStarted.Eagerly, null)
val microGServiceApk = ExternalApk(
packageName = PACKAGE_NAME_GMS, packageName = PACKAGE_NAME_GMS,
versionCode = GMS_VERSION_CODE.toLong(), versionCode = GMS_VERSION_CODE.toLong(),
versionName = MICROG_VERSION, versionName = MICROG_VERSION,
@@ -67,7 +47,7 @@ class MicroGViewModel @Inject constructor(
) )
) )
val microGCompanionApk = ExternalApk( private val microGCompanionApk = ExternalApk(
packageName = PACKAGE_NAME_PLAY_STORE, packageName = PACKAGE_NAME_PLAY_STORE,
versionCode = COMPANION_VERSION_CODE.toLong(), versionCode = COMPANION_VERSION_CODE.toLong(),
versionName = MICROG_VERSION, versionName = MICROG_VERSION,
@@ -84,41 +64,10 @@ class MicroGViewModel @Inject constructor(
) )
) )
fun markAgreement(checked: Boolean) {
viewModelScope.launch {
_checked.emit(checked)
}
}
fun downloadMicroG() { fun downloadMicroG() {
viewModelScope.launch { viewModelScope.launch(Dispatchers.IO) {
try { downloadHelper.enqueueStandalone(microGServiceApk)
_packageNames.emit(listOf(PACKAGE_NAME_GMS)) downloadHelper.enqueueStandalone(microGCompanionApk)
if (microGServiceApk.isInstalled(context)) {
AuroraApp.events.send(InstallerEvent.Installed(PACKAGE_NAME_GMS))
} else {
downloadHelper.enqueueStandalone(microGServiceApk)
}
} catch (e: Exception) {
e.printStackTrace()
}
}
}
fun downloadCompanion() {
viewModelScope.launch {
try {
_packageNames.emit(listOf(PACKAGE_NAME_PLAY_STORE))
if (microGCompanionApk.isInstalled(context)) {
AuroraApp.events.send(InstallerEvent.Installed(PACKAGE_NAME_PLAY_STORE))
} else {
downloadHelper.enqueueStandalone(microGCompanionApk)
}
} catch (e: Exception) {
e.printStackTrace()
}
} }
} }
} }