diff --git a/app/schemas/com.aurora.store.data.room.AuroraDatabase/5.json b/app/schemas/com.aurora.store.data.room.AuroraDatabase/5.json index 9dcbe5a0f..cee2b2277 100644 --- a/app/schemas/com.aurora.store.data.room.AuroraDatabase/5.json +++ b/app/schemas/com.aurora.store.data.room.AuroraDatabase/5.json @@ -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')" ] } } \ No newline at end of file diff --git a/app/src/huawei/java/com/aurora/store/data/receiver/InstallerStatusReceiver.kt b/app/src/huawei/java/com/aurora/store/data/receiver/InstallerStatusReceiver.kt index 2bb55e67c..a7e0d64fd 100644 --- a/app/src/huawei/java/com/aurora/store/data/receiver/InstallerStatusReceiver.kt +++ b/app/src/huawei/java/com/aurora/store/data/receiver/InstallerStatusReceiver.kt @@ -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) { diff --git a/app/src/huawei/java/com/aurora/store/util/FlavouredUtil.kt b/app/src/huawei/java/com/aurora/store/util/FlavouredUtil.kt index 62a58bae9..520725f9a 100644 --- a/app/src/huawei/java/com/aurora/store/util/FlavouredUtil.kt +++ b/app/src/huawei/java/com/aurora/store/util/FlavouredUtil.kt @@ -5,7 +5,7 @@ import com.aurora.extensions.isHuawei object FlavouredUtil : IFlavouredUtil { - override val defaultDispensers = emptySet() + override val defaultDispensers: Set = emptySet() override fun promptMicroGInstall(context: Context): Boolean { return isHuawei && diff --git a/app/src/main/java/com/aurora/Constants.kt b/app/src/main/java/com/aurora/Constants.kt index 7945f2034..cd9270005 100644 --- a/app/src/main/java/com/aurora/Constants.kt +++ b/app/src/main/java/com/aurora/Constants.kt @@ -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" } 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 index 2eb0b94cb..b68e62edc 100644 --- a/app/src/main/java/com/aurora/store/compose/composable/MicroG.kt +++ b/app/src/main/java/com/aurora/store/compose/composable/MicroG.kt @@ -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()) } } diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt index a7195a194..683337284 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/AppDetailsScreen.kt @@ -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( diff --git a/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt b/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt index bf6e92c6f..95af7c428 100644 --- a/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt +++ b/app/src/main/java/com/aurora/store/compose/ui/details/MicroGScreen.kt @@ -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) } 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 index c3bfe7a5e..f26fdac17 100644 --- 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 @@ -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(), + ) } } 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 b1003e2c2..53cf8a5cb 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 @@ -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 = 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 = {} ) } } 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 4bb98ae75..d9e1d1e9b 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 @@ -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 ) 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 1d650af83..e400d062d 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 @@ -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 ) diff --git a/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt b/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt index 91105ec67..72b54cda1 100644 --- a/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt +++ b/app/src/main/java/com/aurora/store/data/installer/AppInstaller.kt @@ -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 diff --git a/app/src/main/java/com/aurora/store/data/room/MigrationHelper.kt b/app/src/main/java/com/aurora/store/data/room/MigrationHelper.kt index a5dffd411..57725012e 100644 --- a/app/src/main/java/com/aurora/store/data/room/MigrationHelper.kt +++ b/app/src/main/java/com/aurora/store/data/room/MigrationHelper.kt @@ -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() + } + } } diff --git a/app/src/main/java/com/aurora/store/data/room/RoomModule.kt b/app/src/main/java/com/aurora/store/data/room/RoomModule.kt index 00cffc0bd..652cdddd0 100644 --- a/app/src/main/java/com/aurora/store/data/room/RoomModule.kt +++ b/app/src/main/java/com/aurora/store/data/room/RoomModule.kt @@ -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() } diff --git a/app/src/main/java/com/aurora/store/data/room/download/Download.kt b/app/src/main/java/com/aurora/store/data/room/download/Download.kt index 62fbed92d..7f40ade9d 100644 --- a/app/src/main/java/com/aurora/store/data/room/download/Download.kt +++ b/app/src/main/java/com/aurora/store/data/room/download/Download.kt @@ -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, val sharedLibs: List, 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() ) } diff --git a/app/src/main/java/com/aurora/store/data/work/DownloadWorker.kt b/app/src/main/java/com/aurora/store/data/work/DownloadWorker.kt index 9dd3bb30b..55dda1168 100644 --- a/app/src/main/java/com/aurora/store/data/work/DownloadWorker.kt +++ b/app/src/main/java/com/aurora/store/data/work/DownloadWorker.kt @@ -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) 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 9a85cb7fc..b34e646fb 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,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) } } } diff --git a/app/src/main/java/com/aurora/store/viewmodel/onboarding/OnboardingViewModel.kt b/app/src/main/java/com/aurora/store/viewmodel/onboarding/OnboardingViewModel.kt index bba0298f2..0753102dc 100644 --- a/app/src/main/java/com/aurora/store/viewmodel/onboarding/OnboardingViewModel.kt +++ b/app/src/main/java/com/aurora/store/viewmodel/onboarding/OnboardingViewModel.kt @@ -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) diff --git a/settings.gradle.kts b/settings.gradle.kts index b490e3305..9b265199c 100644 --- a/settings.gradle.kts +++ b/settings.gradle.kts @@ -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") } } }