Merge branch 'dev'

This commit is contained in:
Aayush Gupta
2025-12-29 14:43:30 +08:00
19 changed files with 261 additions and 68 deletions

View File

@@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 5,
"identityHash": "d5da6e54113409ba811bafebc85632e6",
"identityHash": "82935f33f6fe476bd752dd3643095d69",
"entities": [
{
"tableName": "download",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`packageName` TEXT NOT NULL, `versionCode` INTEGER NOT NULL, `offerType` INTEGER NOT NULL, `isInstalled` INTEGER NOT NULL, `displayName` TEXT NOT NULL, `iconURL` TEXT NOT NULL, `size` INTEGER NOT NULL, `id` INTEGER NOT NULL, `downloadStatus` TEXT NOT NULL, `progress` INTEGER NOT NULL, `speed` INTEGER NOT NULL, `timeRemaining` INTEGER NOT NULL, `totalFiles` INTEGER NOT NULL, `downloadedFiles` INTEGER NOT NULL, `fileList` TEXT NOT NULL, `sharedLibs` TEXT NOT NULL, `targetSdk` INTEGER NOT NULL, `downloadedAt` INTEGER NOT NULL, PRIMARY KEY(`packageName`))",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`packageName` TEXT NOT NULL, `versionCode` INTEGER NOT NULL, `offerType` INTEGER NOT NULL, `isInstalled` INTEGER NOT NULL, `displayName` TEXT NOT NULL, `iconURL` TEXT NOT NULL, `size` INTEGER NOT NULL, `id` INTEGER NOT NULL, `downloadStatus` TEXT NOT NULL, `progress` INTEGER NOT NULL, `speed` INTEGER NOT NULL, `timeRemaining` INTEGER NOT NULL, `totalFiles` INTEGER NOT NULL, `downloadedFiles` INTEGER NOT NULL, `fileList` TEXT NOT NULL, `sharedLibs` TEXT NOT NULL, `targetSdk` INTEGER NOT NULL, `downloadedAt` INTEGER NOT NULL, `requiresGMS` INTEGER NOT NULL, PRIMARY KEY(`packageName`))",
"fields": [
{
"fieldPath": "packageName",
@@ -57,7 +57,7 @@
"notNull": true
},
{
"fieldPath": "downloadStatus",
"fieldPath": "status",
"columnName": "downloadStatus",
"affinity": "TEXT",
"notNull": true
@@ -115,6 +115,12 @@
"columnName": "downloadedAt",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "requiresGMS",
"columnName": "requiresGMS",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
@@ -122,9 +128,7 @@
"columnNames": [
"packageName"
]
},
"indices": [],
"foreignKeys": []
}
},
{
"tableName": "favourite",
@@ -166,9 +170,7 @@
"columnNames": [
"packageName"
]
},
"indices": [],
"foreignKeys": []
}
},
{
"tableName": "update",
@@ -270,15 +272,12 @@
"columnNames": [
"packageName"
]
},
"indices": [],
"foreignKeys": []
}
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'd5da6e54113409ba811bafebc85632e6')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '82935f33f6fe476bd752dd3643095d69')"
]
}
}

View File

@@ -58,7 +58,7 @@ class InstallerStatusReceiver : BaseInstallerStatusReceiver() {
}
}
override fun postStatus(status: Int, packageName: String?, extra: String?, context: Context) {
override fun postStatus(status: Int, packageName: String, extra: String?, context: Context) {
super.postStatus(status, packageName, extra, context)
if (::apiClient.isInitialized && apiClient.isConnected) {

View File

@@ -5,7 +5,7 @@ import com.aurora.extensions.isHuawei
object FlavouredUtil : IFlavouredUtil {
override val defaultDispensers = emptySet()
override val defaultDispensers: Set<String> = emptySet()
override fun promptMicroGInstall(context: Context): Boolean {
return isHuawei &&

View File

@@ -69,4 +69,9 @@ object Constants {
const val PACKAGE_NAME_GMS = "com.google.android.gms"
const val PACKAGE_NAME_PLAY_STORE = "com.android.vending"
const val PACKAGE_NAME_APP_GALLERY = "com.huawei.appmarket"
// FLAVOURS
const val FLAVOUR_VANILLA = "vanilla"
const val FLAVOUR_HUAWEI = "huawei"
const val FLAVOUR_PRELOAD = "preload"
}

View File

@@ -13,6 +13,7 @@ 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.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable
@@ -32,21 +33,22 @@ import com.aurora.extensions.browse
import com.aurora.store.R
import com.aurora.store.compose.preview.PreviewTemplate
import com.aurora.store.data.model.Link
import com.aurora.store.viewmodel.onboarding.MicroGUIState
/**
* 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 = {}
uiState: MicroGUIState,
onInstall: () -> Unit = {},
onTOSChecked: (Boolean) -> Unit = {}
) {
val context = LocalContext.current
var isChecked by rememberSaveable { mutableStateOf(isTOSChecked) }
var isChecked by rememberSaveable { mutableStateOf(false) }
val links = listOf(
Link(
id = 2,
@@ -101,7 +103,6 @@ fun MicroG(
modifier = Modifier
.fillMaxWidth()
.padding(vertical = dimensionResource(R.dimen.padding_xlarge)),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
) {
Row(
@@ -110,7 +111,14 @@ fun MicroG(
dimensionResource(R.dimen.padding_small)
)
) {
Checkbox(checked = isChecked, onCheckedChange = { isChecked = !isChecked })
Checkbox(
checked = isChecked,
onCheckedChange = {
isChecked = it
onTOSChecked(it)
},
enabled = !uiState.isInstalled && !uiState.isDownloading
)
Text(
text = stringResource(R.string.onboarding_gms_agreement),
style = MaterialTheme.typography.bodyMedium,
@@ -118,9 +126,21 @@ fun MicroG(
overflow = TextOverflow.Ellipsis
)
}
Button(onClick = onInstall, enabled = isChecked) {
Button(
onClick = onInstall,
enabled = isChecked && !uiState.isDownloading && !uiState.isInstalled,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = stringResource(R.string.action_install_microG)
text = if (uiState.isDownloading)
stringResource(R.string.action_installing)
else
stringResource(R.string.action_install_microG)
)
}
if (uiState.isDownloading) {
LinearProgressIndicator(
modifier = Modifier.fillMaxWidth()
)
}
}
@@ -131,6 +151,6 @@ fun MicroG(
@Composable
private fun MicroGPreview() {
PreviewTemplate {
MicroG(isTOSChecked = true)
MicroG(uiState = MicroGUIState())
}
}

View File

@@ -492,7 +492,7 @@ private fun ScreenContentApp(
is ExtraScreen.MicroG -> MicroGScreen(
packageName = app.packageName,
onNavigateUp = ::onNavigateBack,
onIgnore = { onInstall(ignoreMicroG = true) }
onIgnore = { onInstall(ignoreMicroG = it) }
)
is Screen.DevProfile -> DevProfileScreen(

View File

@@ -42,13 +42,14 @@ import com.aurora.store.compose.preview.PreviewTemplate
import com.aurora.store.data.model.PermissionType
import com.aurora.store.data.providers.PermissionProvider
import com.aurora.store.viewmodel.details.AppDetailsViewModel
import com.aurora.store.viewmodel.onboarding.MicroGUIState
import com.aurora.store.viewmodel.onboarding.MicroGViewModel
@Composable
fun MicroGScreen(
packageName: String,
onNavigateUp: () -> Unit,
onIgnore: () -> Unit,
onIgnore: (Boolean) -> Unit,
appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
viewModel: MicroGViewModel = hiltViewModel(),
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
@@ -61,6 +62,7 @@ fun MicroGScreen(
ScreenContent(
topAppBarTitle = topAppBarTitle,
uiState = viewModel.uiState,
onNavigateUp = onNavigateUp,
onInstall = { viewModel.downloadMicroG() },
onIgnore = onIgnore
@@ -70,9 +72,10 @@ fun MicroGScreen(
@Composable
private fun ScreenContent(
topAppBarTitle: String? = null,
uiState: MicroGUIState = MicroGUIState(),
onNavigateUp: () -> Unit = {},
onInstall: () -> Unit = {},
onIgnore: () -> Unit = {},
onIgnore: (Boolean) -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
) {
val context = LocalContext.current
@@ -98,13 +101,17 @@ private fun ScreenContent(
modifier = Modifier.weight(1F),
onInstall = {
when {
PermissionProvider.isGranted(context, PermissionType.INSTALL_UNKNOWN_APPS) -> {
PermissionProvider.isGranted(
context,
PermissionType.INSTALL_UNKNOWN_APPS
) -> {
onInstall()
}
else -> context.toast(R.string.permissions_denied)
}
}
},
uiState = uiState
)
Row(
@@ -124,10 +131,14 @@ private fun ScreenContent(
Button(
modifier = Modifier.weight(1F),
onClick = onIgnore
onClick = { onIgnore(uiState.isInstalled) },
enabled = !uiState.isDownloading
) {
Text(
text = stringResource(R.string.action_ignore),
text = if (uiState.isInstalled)
stringResource(R.string.action_install)
else
stringResource(R.string.action_ignore),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
@@ -139,7 +150,7 @@ private fun ScreenContent(
@Preview
@Composable
private fun MicroGScreenPreview(@PreviewParameter(AppPreviewProvider ::class) app: App) {
private fun MicroGScreenPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
PreviewTemplate {
ScreenContent(topAppBarTitle = app.displayName)
}

View File

@@ -27,17 +27,27 @@ import com.aurora.store.compose.composable.MicroG
import com.aurora.store.compose.preview.PreviewTemplate
import com.aurora.store.data.model.PermissionType
import com.aurora.store.data.providers.PermissionProvider
import com.aurora.store.viewmodel.onboarding.MicroGUIState
import com.aurora.store.viewmodel.onboarding.MicroGViewModel
@Composable
fun MicroGPage(viewModel: MicroGViewModel = hiltViewModel()) {
fun MicroGPage(
onMicrogTOSChecked: (Boolean) -> Unit = {},
viewModel: MicroGViewModel = hiltViewModel(),
) {
ScreenContent(
onInstall = { viewModel.downloadMicroG() }
uiState = viewModel.uiState,
onInstall = { viewModel.downloadMicroG() },
onMicrogTOSChecked = onMicrogTOSChecked,
)
}
@Composable
private fun ScreenContent(onInstall: () -> Unit = {}) {
private fun ScreenContent(
uiState: MicroGUIState,
onInstall: () -> Unit = {},
onMicrogTOSChecked: (Boolean) -> Unit = {}
) {
val context = LocalContext.current
Column(
@@ -58,7 +68,7 @@ private fun ScreenContent(onInstall: () -> Unit = {}) {
)
Text(
text = stringResource(R.string.onboarding_title_gsf_desc),
style = MaterialTheme.typography.titleLarge,
style = MaterialTheme.typography.titleMedium,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)
@@ -73,7 +83,9 @@ private fun ScreenContent(onInstall: () -> Unit = {}) {
else -> context.toast(R.string.permissions_denied)
}
}
},
uiState = uiState,
onTOSChecked = onMicrogTOSChecked
)
}
@@ -83,6 +95,8 @@ private fun ScreenContent(onInstall: () -> Unit = {}) {
@Composable
private fun MicroGPagePreview() {
PreviewTemplate {
ScreenContent()
ScreenContent(
uiState = MicroGUIState(),
)
}
}

View File

@@ -10,6 +10,7 @@ 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.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.pager.HorizontalPager
@@ -25,11 +26,11 @@ 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
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.aurora.extensions.isWindowCompact
import com.aurora.store.R
@@ -37,22 +38,25 @@ 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.OnboardingUiState
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,
if (FlavouredUtil.promptMicroGInstall(context)) OnboardingPage.MICRO_G else null
if (viewModel.isMicroGPromptRequired) OnboardingPage.MICRO_G else null
)
ScreenContent(
pages = pages,
onFinishOnboarding = { viewModel.finishOnboarding() }
onFinishOnboarding = {
viewModel.finishOnboarding()
},
uiState = viewModel.uiState,
onMicrogTOSChecked = viewModel::onMicrogTOSChecked
)
}
@@ -60,6 +64,8 @@ fun OnboardingScreen(viewModel: OnboardingViewModel = hiltViewModel()) {
private fun ScreenContent(
pages: List<OnboardingPage> = emptyList(),
onFinishOnboarding: () -> Unit = {},
uiState: OnboardingUiState,
onMicrogTOSChecked: (Boolean) -> Unit,
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
) {
val pagerState = rememberPagerState { pages.size }
@@ -70,7 +76,7 @@ private fun ScreenContent(
}
@Composable
fun SetupActions() {
fun SetupActions(uiState: OnboardingUiState) {
val horizontalButtonPadding = when {
windowAdaptiveInfo.isWindowCompact -> dimensionResource(R.dimen.padding_medium)
else -> dimensionResource(R.dimen.padding_xlarge)
@@ -119,6 +125,7 @@ private fun ScreenContent(
Button(
modifier = buttonWidthModifier,
enabled = !isFinalPage() || (!uiState.isMicroBundleChecked || uiState.isMicroGBundleInstalled),
onClick = {
when {
isFinalPage() -> onFinishOnboarding()
@@ -145,6 +152,7 @@ private fun ScreenContent(
Scaffold(
topBar = {
TopAppBar(
modifier = Modifier.height(64.dp),
title = {
PageIndicator(totalPages = pages.size, currentPage = pagerState.currentPage)
}
@@ -175,12 +183,12 @@ private fun ScreenContent(
when (pages[page]) {
OnboardingPage.WELCOME -> WelcomePage()
OnboardingPage.PERMISSIONS -> PermissionsPage()
OnboardingPage.MICRO_G -> MicroGPage()
OnboardingPage.MICRO_G -> MicroGPage(onMicrogTOSChecked = onMicrogTOSChecked)
}
}
}
SetupActions()
SetupActions(uiState)
}
}
}
@@ -190,7 +198,9 @@ private fun ScreenContent(
private fun OnboardingScreenPreview() {
PreviewTemplate {
ScreenContent(
pages = listOf(OnboardingPage.WELCOME, OnboardingPage.PERMISSIONS)
pages = listOf(OnboardingPage.WELCOME, OnboardingPage.PERMISSIONS),
uiState = OnboardingUiState(),
onMicrogTOSChecked = {}
)
}
}

View File

@@ -61,7 +61,7 @@ private fun PageContent(
)
Text(
text = stringResource(R.string.onboarding_permission_select),
style = MaterialTheme.typography.titleLarge,
style = MaterialTheme.typography.titleMedium,
maxLines = 2,
overflow = TextOverflow.Ellipsis
)

View File

@@ -112,7 +112,7 @@ private fun PageContent(onAboutAurora: () -> Unit = {}) {
)
Text(
text = stringResource(R.string.onboarding_welcome_select),
style = MaterialTheme.typography.titleLarge,
style = MaterialTheme.typography.titleMedium,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)

View File

@@ -166,6 +166,10 @@ class AppInstaller @Inject constructor(
private val defaultInstaller: IInstaller
get() = sessionInstaller
fun getMicroGInstaller(): IInstaller {
return microGInstaller
}
fun getPreferredInstaller(): IInstaller {
return when (getCurrentInstaller(context)) {
Installer.SESSION -> sessionInstaller

View File

@@ -26,6 +26,10 @@ object MigrationHelper {
override fun migrate(db: SupportSQLiteDatabase) = migrateFrom4To5(db)
}
val MIGRATION_5_6 = object : Migration(5, 6) {
override fun migrate(db: SupportSQLiteDatabase) = migrateFrom5To6(db)
}
private const val TAG = "MigrationHelper"
private fun migrateFrom1To2(database: SupportSQLiteDatabase) {
@@ -83,4 +87,21 @@ object MigrationHelper {
database.endTransaction()
}
}
/**
* Add requiresGMS column to download table for checking if app requires GMS to install.
*/
private fun migrateFrom5To6(database: SupportSQLiteDatabase) {
database.beginTransaction()
try {
database.execSQL(
"ALTER TABLE `download` ADD COLUMN requiresGMS INTEGER NOT NULL DEFAULT 0"
)
database.setTransactionSuccessful()
} catch (exception: Exception) {
Log.e(TAG, "Failed while migrating from database version 5 to 6", exception)
} finally {
database.endTransaction()
}
}
}

View File

@@ -6,6 +6,7 @@ import com.aurora.store.data.room.MigrationHelper.MIGRATION_1_2
import com.aurora.store.data.room.MigrationHelper.MIGRATION_2_3
import com.aurora.store.data.room.MigrationHelper.MIGRATION_3_4
import com.aurora.store.data.room.MigrationHelper.MIGRATION_4_5
import com.aurora.store.data.room.MigrationHelper.MIGRATION_5_6
import com.aurora.store.data.room.download.DownloadConverter
import com.aurora.store.data.room.download.DownloadDao
import com.aurora.store.data.room.favourite.FavouriteDao
@@ -30,7 +31,7 @@ object RoomModule {
downloadConverter: DownloadConverter
): AuroraDatabase {
return Room.databaseBuilder(context, AuroraDatabase::class.java, DATABASE)
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5)
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6)
.addTypeConverter(downloadConverter)
.build()
}

View File

@@ -5,6 +5,7 @@ import android.os.Parcelable
import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.PrimaryKey
import com.aurora.extensions.requiresGMS
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.PlayFile
import com.aurora.store.data.model.DownloadStatus
@@ -35,7 +36,8 @@ data class Download(
var fileList: List<PlayFile>,
val sharedLibs: List<SharedLib>,
val targetSdk: Int = 1,
val downloadedAt: Long = 0
val downloadedAt: Long = 0,
val requiresGMS: Boolean = false
) : Parcelable {
val isFinished get() = status in DownloadStatus.finished
val isRunning get() = status in DownloadStatus.running
@@ -61,7 +63,8 @@ data class Download(
app.fileList.filterNot { it.url.isBlank() },
app.dependencies.dependentLibraries.map { SharedLib.fromApp(it) },
app.targetSdk,
Date().time
Date().time,
app.requiresGMS()
)
}

View File

@@ -19,6 +19,7 @@ import androidx.work.ForegroundInfo
import androidx.work.WorkInfo.Companion.STOP_REASON_CANCELLED_BY_APP
import androidx.work.WorkInfo.Companion.STOP_REASON_USER
import androidx.work.WorkerParameters
import com.aurora.Constants.FLAVOUR_HUAWEI
import com.aurora.extensions.copyTo
import com.aurora.extensions.isPAndAbove
import com.aurora.extensions.isQAndAbove
@@ -28,6 +29,7 @@ import com.aurora.gplayapi.data.models.PlayFile
import com.aurora.gplayapi.helpers.PurchaseHelper
import com.aurora.gplayapi.network.IHttpClient
import com.aurora.store.AuroraApp
import com.aurora.store.BuildConfig
import com.aurora.store.R
import com.aurora.store.data.event.InstallerEvent
import com.aurora.store.data.helper.DownloadHelper
@@ -41,6 +43,7 @@ import com.aurora.store.data.room.download.Download
import com.aurora.store.data.room.download.DownloadDao
import com.aurora.store.util.CertUtil
import com.aurora.store.util.NotificationUtil
import com.aurora.store.util.PackageUtil
import com.aurora.store.util.PathUtil
import dagger.assisted.Assisted
import dagger.assisted.AssistedInject
@@ -88,8 +91,11 @@ class DownloadWorker @AssistedInject constructor(
inner class NoNetworkException : Exception(context.getString(R.string.title_no_network))
inner class NothingToDownloadException : Exception(context.getString(R.string.purchase_no_file))
inner class DownloadFailedException : Exception(context.getString(R.string.download_failed))
inner class DownloadCancelledException : Exception(context.getString(R.string.download_canceled))
inner class VerificationFailedException : Exception(context.getString(R.string.verification_failed))
inner class DownloadCancelledException :
Exception(context.getString(R.string.download_canceled))
inner class VerificationFailedException :
Exception(context.getString(R.string.verification_failed))
override suspend fun doWork(): Result {
super.doWork()
@@ -198,7 +204,10 @@ class DownloadWorker @AssistedInject constructor(
private suspend fun onSuccess(): Result {
return withContext(NonCancellable) {
return@withContext try {
appInstaller.getPreferredInstaller().install(download)
if (BuildConfig.FLAVOR == FLAVOUR_HUAWEI && download.requiresGMS && PackageUtil.hasMicroGCompanion(context))
appInstaller.getMicroGInstaller().install(download)
else
appInstaller.getPreferredInstaller().install(download)
Result.success()
} catch (exception: Exception) {
Log.e(TAG, "Failed to install ${download.packageName}", exception)

View File

@@ -5,31 +5,60 @@
package com.aurora.store.viewmodel.onboarding
import android.content.Context
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
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 com.aurora.store.util.PackageUtil
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch
import javax.inject.Inject
data class MicroGUIState(
var isDownloading: Boolean = false,
var isInstalled: Boolean = false
)
@HiltViewModel
class MicroGViewModel @Inject constructor(
@ApplicationContext
private val context: Context,
private val downloadHelper: DownloadHelper
) : ViewModel() {
companion object {
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 const val MICROG_VERSION = "v0.3.11.250932"
private const val GMS_VERSION_CODE = 250932022
private const val COMPANION_VERSION_CODE = 84022622
}
init {
AuroraApp.events.installerEvent.onEach {
when (it) {
is InstallerEvent.Installed -> confirmBundleInstall(it.packageName)
else -> {}
}
}.launchIn(AuroraApp.scope)
}
var uiState: MicroGUIState by mutableStateOf(MicroGUIState())
private set
private val microGServiceApk = ExternalApk(
packageName = PACKAGE_NAME_GMS,
versionCode = GMS_VERSION_CODE.toLong(),
@@ -41,8 +70,8 @@ class MicroGViewModel @Inject constructor(
PlayFile(
url = "$MICROG_DOWNLOAD_URL/$MICROG_VERSION/$PACKAGE_NAME_GMS-$GMS_VERSION_CODE-hw.apk",
name = "$PACKAGE_NAME_GMS-$GMS_VERSION_CODE-hw.apk",
size = 32509431,
sha256 = "2f14df2974811b576bfafa6167a97e3b3032f2bd6e6ec3887a833fd2fa350dda"
size = 92386474,
sha256 = "2894a93544a8d7ca8f6ca96e7cc697647a7e0862165b6a02f8cd26822759b9cc"
)
)
)
@@ -58,16 +87,33 @@ class MicroGViewModel @Inject constructor(
PlayFile(
url = "$MICROG_DOWNLOAD_URL/$MICROG_VERSION/$PACKAGE_NAME_PLAY_STORE-$COMPANION_VERSION_CODE-hw.apk",
name = "$PACKAGE_NAME_PLAY_STORE-$COMPANION_VERSION_CODE-hw.apk",
size = 3915551,
sha256 = "6835b09016cef0fc3469b4a36b1720427ad3f81161cf20b188f0dadb5f8594e1"
size = 4626291,
sha256 = "b9623b8da8791c7e887efca941434b20e517c8a42ca4fda713625957edcc84eb"
)
)
)
fun downloadMicroG() {
viewModelScope.launch(Dispatchers.IO) {
downloadHelper.enqueueStandalone(microGServiceApk)
downloadHelper.enqueueStandalone(microGCompanionApk)
if (microGCompanionApk.isInstalled(context)) {
AuroraApp.events.send(InstallerEvent.Installed(PACKAGE_NAME_PLAY_STORE))
} else {
downloadHelper.enqueueStandalone(microGCompanionApk)
uiState = uiState.copy(isDownloading = true)
}
if (microGServiceApk.isInstalled(context)) {
AuroraApp.events.send(InstallerEvent.Installed(PACKAGE_NAME_GMS))
} else {
downloadHelper.enqueueStandalone(microGServiceApk)
uiState = uiState.copy(isDownloading = true)
}
}
}
fun confirmBundleInstall(packageName: String) {
if (PackageUtil.isMicroGBundleInstalled(context)) {
uiState = uiState.copy(isInstalled = true, isDownloading = false)
}
}
}

View File

@@ -7,13 +7,24 @@ package com.aurora.store.viewmodel.onboarding
import android.content.Context
import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel
import com.aurora.Constants.FLAVOUR_HUAWEI
import com.aurora.Constants.PACKAGE_NAME_GMS
import com.aurora.Constants.PACKAGE_NAME_PLAY_STORE
import com.aurora.extensions.areNotificationsEnabled
import com.aurora.extensions.isIgnoringBatteryOptimizations
import com.aurora.store.AuroraApp
import com.aurora.store.BuildConfig
import com.aurora.store.data.event.InstallerEvent
import com.aurora.store.data.helper.UpdateHelper
import com.aurora.store.data.model.UpdateMode
import com.aurora.store.data.providers.BlacklistProvider
import com.aurora.store.data.work.CacheWorker
import com.aurora.store.util.FlavouredUtil
import com.aurora.store.util.PackageUtil
import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB
@@ -32,19 +43,51 @@ import com.aurora.store.util.save
import com.jakewharton.processphoenix.ProcessPhoenix
import dagger.hilt.android.lifecycle.HiltViewModel
import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject
data class OnboardingUiState(
val isMicroBundleChecked: Boolean = false,
val isMicroGBundleInstalled: Boolean = false
)
@HiltViewModel
class OnboardingViewModel @Inject constructor(
val updateHelper: UpdateHelper,
val blacklistProvider: BlacklistProvider,
@ApplicationContext private val context: Context
) : ViewModel() {
private val TAG = OnboardingViewModel::class.java.simpleName
val isMicroGPromptRequired = FlavouredUtil.promptMicroGInstall(context)
var uiState by mutableStateOf(OnboardingUiState())
private set
init {
AuroraApp.events.installerEvent.onEach {
when (it) {
is InstallerEvent.Installed -> confirmBundleInstall()
else -> {}
}
}.launchIn(AuroraApp.scope)
}
fun onMicrogTOSChecked(value: Boolean) {
uiState = uiState.copy(isMicroBundleChecked = value)
}
fun finishOnboarding() {
Log.i(TAG, "Finishing onboarding with defaults")
context.saveDefaultPreferences()
if (BuildConfig.FLAVOR == FLAVOUR_HUAWEI) {
blacklistProvider.blacklist(PACKAGE_NAME_GMS)
blacklistProvider.blacklist(PACKAGE_NAME_PLAY_STORE)
}
setupAutoUpdates()
CacheWorker.scheduleAutomatedCacheCleanup(context)
Preferences.putBooleanNow(context, PREFERENCE_INTRO, true)
@@ -53,11 +96,17 @@ class OnboardingViewModel @Inject constructor(
ProcessPhoenix.triggerRebirth(context)
}
private fun confirmBundleInstall() {
if (PackageUtil.isMicroGBundleInstalled(context)) {
uiState = uiState.copy(isMicroGBundleInstalled = true)
}
}
private fun setupAutoUpdates() {
val updateMode = when {
context.isIgnoringBatteryOptimizations() -> UpdateMode.CHECK_AND_INSTALL
context.areNotificationsEnabled() -> UpdateMode.CHECK_AND_NOTIFY
else -> UpdateMode.DISABLED
context.areNotificationsEnabled() -> UpdateMode.CHECK_AND_NOTIFY
else -> UpdateMode.DISABLED
}
context.save(PREFERENCE_UPDATES_AUTO, updateMode.ordinal)

View File

@@ -25,7 +25,8 @@ dependencyResolutionManagement {
// Only included in huawei variants
maven("https://developer.huawei.com/repo/") {
content {
includeModule("com.huawei.hms", "ag-coreservice")
includeGroup("com.huawei.hms")
includeGroup("com.huawei.android.hms")
}
}
}