diff --git a/app/src/main/java/com/aurora/store/compose/composable/MicroG.kt b/app/src/main/java/com/aurora/store/compose/composable/MicroG.kt new file mode 100644 index 000000000..2eb0b94cb --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/composable/MicroG.kt @@ -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) + } +} diff --git a/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt b/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt new file mode 100644 index 000000000..e9bb521e7 --- /dev/null +++ b/app/src/main/java/com/aurora/store/compose/ui/onboarding/MicroGPage.kt @@ -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() + } +} diff --git a/app/src/main/java/com/aurora/store/compose/ui/onboarding/OnboardingScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/onboarding/OnboardingScreen.kt index 345065396..b1003e2c2 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/onboarding/OnboardingScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/onboarding/OnboardingScreen.kt @@ -25,6 +25,7 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.rememberCoroutineScope 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 @@ -36,14 +37,17 @@ import com.aurora.store.compose.composable.Logo import com.aurora.store.compose.composable.PageIndicator import com.aurora.store.compose.preview.PreviewTemplate import com.aurora.store.compose.ui.onboarding.navigation.OnboardingPage +import com.aurora.store.util.FlavouredUtil import com.aurora.store.viewmodel.onboarding.OnboardingViewModel import kotlinx.coroutines.launch @Composable fun OnboardingScreen(viewModel: OnboardingViewModel = hiltViewModel()) { + val context = LocalContext.current val pages = listOfNotNull( OnboardingPage.WELCOME, - OnboardingPage.PERMISSIONS + OnboardingPage.PERMISSIONS, + if (FlavouredUtil.promptMicroGInstall(context)) OnboardingPage.MICRO_G else null ) ScreenContent( @@ -171,6 +175,7 @@ private fun ScreenContent( when (pages[page]) { OnboardingPage.WELCOME -> WelcomePage() OnboardingPage.PERMISSIONS -> PermissionsPage() + OnboardingPage.MICRO_G -> MicroGPage() } } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/onboarding/PermissionsPage.kt b/app/src/main/java/com/aurora/store/compose/ui/onboarding/PermissionsPage.kt index 21062f16e..4bb98ae75 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/onboarding/PermissionsPage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/onboarding/PermissionsPage.kt @@ -51,7 +51,7 @@ private fun PageContent( header = { Surface(modifier = Modifier.fillMaxWidth()) { Column( - modifier = Modifier.padding(dimensionResource(R.dimen.padding_normal)) + modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)) ) { Text( text = stringResource(R.string.onboarding_title_permissions), diff --git a/app/src/main/java/com/aurora/store/compose/ui/onboarding/WelcomePage.kt b/app/src/main/java/com/aurora/store/compose/ui/onboarding/WelcomePage.kt index ac10f2d07..1d650af83 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/onboarding/WelcomePage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/onboarding/WelcomePage.kt @@ -102,7 +102,7 @@ private fun PageContent(onAboutAurora: () -> Unit = {}) { stickyHeader { Surface(modifier = Modifier.fillMaxWidth()) { Column( - modifier = Modifier.padding(dimensionResource(R.dimen.padding_normal)) + modifier = Modifier.padding(dimensionResource(R.dimen.padding_medium)) ) { Text( text = stringResource(R.string.onboarding_title_welcome), diff --git a/app/src/main/java/com/aurora/store/compose/ui/onboarding/navigation/OnboardingPage.kt b/app/src/main/java/com/aurora/store/compose/ui/onboarding/navigation/OnboardingPage.kt index 28759caed..35ae683c9 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/onboarding/navigation/OnboardingPage.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/onboarding/navigation/OnboardingPage.kt @@ -11,4 +11,5 @@ package com.aurora.store.compose.ui.onboarding.navigation enum class OnboardingPage { WELCOME, PERMISSIONS, + MICRO_G } diff --git a/app/src/main/java/com/aurora/store/viewmodel/onboarding/MicroGViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/onboarding/MicroGViewModel.kt index 73bda70f5..9a85cb7fc 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/onboarding/MicroGViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/onboarding/MicroGViewModel.kt @@ -5,52 +5,32 @@ package com.aurora.store.viewmodel.onboarding -import android.content.Context import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope import com.aurora.Constants.PACKAGE_NAME_GMS import com.aurora.Constants.PACKAGE_NAME_PLAY_STORE 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.room.suite.ExternalApk import dagger.hilt.android.lifecycle.HiltViewModel -import dagger.hilt.android.qualifiers.ApplicationContext -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.Dispatchers import kotlinx.coroutines.launch import javax.inject.Inject @HiltViewModel class MicroGViewModel @Inject constructor( - @ApplicationContext private val context: Context, private val downloadHelper: DownloadHelper ) : ViewModel() { companion object { - const val MICROG_DOWNLOAD_URL = "https://github.com/microg/GmsCore/releases/download" - const val MICROG_VERSION = "v0.3.6.244735" - const val GMS_VERSION_CODE = 244735012 - const val COMPANION_VERSION_CODE = 84022612 + private const val MICROG_DOWNLOAD_URL = + "https://github.com/microg/GmsCore/releases/download" + private const val MICROG_VERSION = "v0.3.6.244735" + private const val GMS_VERSION_CODE = 244735012 + private const val COMPANION_VERSION_CODE = 84022612 } - private val _packageNames = MutableSharedFlow>() - val packageNames = _packageNames.asSharedFlow() - - private val _checked = MutableStateFlow(false) - val checked: StateFlow = _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( + private val microGServiceApk = ExternalApk( packageName = PACKAGE_NAME_GMS, versionCode = GMS_VERSION_CODE.toLong(), versionName = MICROG_VERSION, @@ -67,7 +47,7 @@ class MicroGViewModel @Inject constructor( ) ) - val microGCompanionApk = ExternalApk( + private val microGCompanionApk = ExternalApk( packageName = PACKAGE_NAME_PLAY_STORE, versionCode = COMPANION_VERSION_CODE.toLong(), versionName = MICROG_VERSION, @@ -84,41 +64,10 @@ class MicroGViewModel @Inject constructor( ) ) - fun markAgreement(checked: Boolean) { - viewModelScope.launch { - _checked.emit(checked) - } - } - fun downloadMicroG() { - viewModelScope.launch { - try { - _packageNames.emit(listOf(PACKAGE_NAME_GMS)) - - 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() - } + viewModelScope.launch(Dispatchers.IO) { + downloadHelper.enqueueStandalone(microGServiceApk) + downloadHelper.enqueueStandalone(microGCompanionApk) } } }