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, "formatVersion": 1,
"database": { "database": {
"version": 5, "version": 5,
"identityHash": "d5da6e54113409ba811bafebc85632e6", "identityHash": "82935f33f6fe476bd752dd3643095d69",
"entities": [ "entities": [
{ {
"tableName": "download", "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": [ "fields": [
{ {
"fieldPath": "packageName", "fieldPath": "packageName",
@@ -57,7 +57,7 @@
"notNull": true "notNull": true
}, },
{ {
"fieldPath": "downloadStatus", "fieldPath": "status",
"columnName": "downloadStatus", "columnName": "downloadStatus",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
@@ -115,6 +115,12 @@
"columnName": "downloadedAt", "columnName": "downloadedAt",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
},
{
"fieldPath": "requiresGMS",
"columnName": "requiresGMS",
"affinity": "INTEGER",
"notNull": true
} }
], ],
"primaryKey": { "primaryKey": {
@@ -122,9 +128,7 @@
"columnNames": [ "columnNames": [
"packageName" "packageName"
] ]
}, }
"indices": [],
"foreignKeys": []
}, },
{ {
"tableName": "favourite", "tableName": "favourite",
@@ -166,9 +170,7 @@
"columnNames": [ "columnNames": [
"packageName" "packageName"
] ]
}, }
"indices": [],
"foreignKeys": []
}, },
{ {
"tableName": "update", "tableName": "update",
@@ -270,15 +272,12 @@
"columnNames": [ "columnNames": [
"packageName" "packageName"
] ]
}, }
"indices": [],
"foreignKeys": []
} }
], ],
"views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "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) super.postStatus(status, packageName, extra, context)
if (::apiClient.isInitialized && apiClient.isConnected) { if (::apiClient.isInitialized && apiClient.isConnected) {

View File

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

View File

@@ -69,4 +69,9 @@ object Constants {
const val PACKAGE_NAME_GMS = "com.google.android.gms" const val PACKAGE_NAME_GMS = "com.google.android.gms"
const val PACKAGE_NAME_PLAY_STORE = "com.android.vending" const val PACKAGE_NAME_PLAY_STORE = "com.android.vending"
const val PACKAGE_NAME_APP_GALLERY = "com.huawei.appmarket" 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.foundation.layout.padding
import androidx.compose.material3.Button import androidx.compose.material3.Button
import androidx.compose.material3.Checkbox import androidx.compose.material3.Checkbox
import androidx.compose.material3.LinearProgressIndicator
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
@@ -32,21 +33,22 @@ import com.aurora.extensions.browse
import com.aurora.store.R import com.aurora.store.R
import com.aurora.store.compose.preview.PreviewTemplate import com.aurora.store.compose.preview.PreviewTemplate
import com.aurora.store.data.model.Link import com.aurora.store.data.model.Link
import com.aurora.store.viewmodel.onboarding.MicroGUIState
/** /**
* Composable to display suggestion to install microG * Composable to display suggestion to install microG
* @param modifier Modifier for the composable * @param modifier Modifier for the composable
* @param isTOSChecked Whether the TOS checkbox is checked
* @param onInstall Callback when user requests installing microG bundle * @param onInstall Callback when user requests installing microG bundle
*/ */
@Composable @Composable
fun MicroG( fun MicroG(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
isTOSChecked: Boolean = false, uiState: MicroGUIState,
onInstall: () -> Unit = {} onInstall: () -> Unit = {},
onTOSChecked: (Boolean) -> Unit = {}
) { ) {
val context = LocalContext.current val context = LocalContext.current
var isChecked by rememberSaveable { mutableStateOf(isTOSChecked) } var isChecked by rememberSaveable { mutableStateOf(false) }
val links = listOf( val links = listOf(
Link( Link(
id = 2, id = 2,
@@ -101,7 +103,6 @@ fun MicroG(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(vertical = dimensionResource(R.dimen.padding_xlarge)), .padding(vertical = dimensionResource(R.dimen.padding_xlarge)),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small)) verticalArrangement = Arrangement.spacedBy(dimensionResource(R.dimen.margin_small))
) { ) {
Row( Row(
@@ -110,7 +111,14 @@ fun MicroG(
dimensionResource(R.dimen.padding_small) 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(
text = stringResource(R.string.onboarding_gms_agreement), text = stringResource(R.string.onboarding_gms_agreement),
style = MaterialTheme.typography.bodyMedium, style = MaterialTheme.typography.bodyMedium,
@@ -118,9 +126,21 @@ fun MicroG(
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
} }
Button(onClick = onInstall, enabled = isChecked) { Button(
onClick = onInstall,
enabled = isChecked && !uiState.isDownloading && !uiState.isInstalled,
modifier = Modifier.fillMaxWidth()
) {
Text( 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 @Composable
private fun MicroGPreview() { private fun MicroGPreview() {
PreviewTemplate { PreviewTemplate {
MicroG(isTOSChecked = true) MicroG(uiState = MicroGUIState())
} }
} }

View File

@@ -492,7 +492,7 @@ private fun ScreenContentApp(
is ExtraScreen.MicroG -> MicroGScreen( is ExtraScreen.MicroG -> MicroGScreen(
packageName = app.packageName, packageName = app.packageName,
onNavigateUp = ::onNavigateBack, onNavigateUp = ::onNavigateBack,
onIgnore = { onInstall(ignoreMicroG = true) } onIgnore = { onInstall(ignoreMicroG = it) }
) )
is Screen.DevProfile -> DevProfileScreen( 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.model.PermissionType
import com.aurora.store.data.providers.PermissionProvider import com.aurora.store.data.providers.PermissionProvider
import com.aurora.store.viewmodel.details.AppDetailsViewModel import com.aurora.store.viewmodel.details.AppDetailsViewModel
import com.aurora.store.viewmodel.onboarding.MicroGUIState
import com.aurora.store.viewmodel.onboarding.MicroGViewModel import com.aurora.store.viewmodel.onboarding.MicroGViewModel
@Composable @Composable
fun MicroGScreen( fun MicroGScreen(
packageName: String, packageName: String,
onNavigateUp: () -> Unit, onNavigateUp: () -> Unit,
onIgnore: () -> Unit, onIgnore: (Boolean) -> Unit,
appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName), appDetailsViewModel: AppDetailsViewModel = hiltViewModel(key = packageName),
viewModel: MicroGViewModel = hiltViewModel(), viewModel: MicroGViewModel = hiltViewModel(),
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
@@ -61,6 +62,7 @@ fun MicroGScreen(
ScreenContent( ScreenContent(
topAppBarTitle = topAppBarTitle, topAppBarTitle = topAppBarTitle,
uiState = viewModel.uiState,
onNavigateUp = onNavigateUp, onNavigateUp = onNavigateUp,
onInstall = { viewModel.downloadMicroG() }, onInstall = { viewModel.downloadMicroG() },
onIgnore = onIgnore onIgnore = onIgnore
@@ -70,9 +72,10 @@ fun MicroGScreen(
@Composable @Composable
private fun ScreenContent( private fun ScreenContent(
topAppBarTitle: String? = null, topAppBarTitle: String? = null,
uiState: MicroGUIState = MicroGUIState(),
onNavigateUp: () -> Unit = {}, onNavigateUp: () -> Unit = {},
onInstall: () -> Unit = {}, onInstall: () -> Unit = {},
onIgnore: () -> Unit = {}, onIgnore: (Boolean) -> Unit = {},
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
) { ) {
val context = LocalContext.current val context = LocalContext.current
@@ -98,13 +101,17 @@ private fun ScreenContent(
modifier = Modifier.weight(1F), modifier = Modifier.weight(1F),
onInstall = { onInstall = {
when { when {
PermissionProvider.isGranted(context, PermissionType.INSTALL_UNKNOWN_APPS) -> { PermissionProvider.isGranted(
context,
PermissionType.INSTALL_UNKNOWN_APPS
) -> {
onInstall() onInstall()
} }
else -> context.toast(R.string.permissions_denied) else -> context.toast(R.string.permissions_denied)
} }
} },
uiState = uiState
) )
Row( Row(
@@ -124,10 +131,14 @@ private fun ScreenContent(
Button( Button(
modifier = Modifier.weight(1F), modifier = Modifier.weight(1F),
onClick = onIgnore onClick = { onIgnore(uiState.isInstalled) },
enabled = !uiState.isDownloading
) { ) {
Text( Text(
text = stringResource(R.string.action_ignore), text = if (uiState.isInstalled)
stringResource(R.string.action_install)
else
stringResource(R.string.action_ignore),
maxLines = 1, maxLines = 1,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@@ -139,7 +150,7 @@ private fun ScreenContent(
@Preview @Preview
@Composable @Composable
private fun MicroGScreenPreview(@PreviewParameter(AppPreviewProvider ::class) app: App) { private fun MicroGScreenPreview(@PreviewParameter(AppPreviewProvider::class) app: App) {
PreviewTemplate { PreviewTemplate {
ScreenContent(topAppBarTitle = app.displayName) 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.compose.preview.PreviewTemplate
import com.aurora.store.data.model.PermissionType import com.aurora.store.data.model.PermissionType
import com.aurora.store.data.providers.PermissionProvider import com.aurora.store.data.providers.PermissionProvider
import com.aurora.store.viewmodel.onboarding.MicroGUIState
import com.aurora.store.viewmodel.onboarding.MicroGViewModel import com.aurora.store.viewmodel.onboarding.MicroGViewModel
@Composable @Composable
fun MicroGPage(viewModel: MicroGViewModel = hiltViewModel()) { fun MicroGPage(
onMicrogTOSChecked: (Boolean) -> Unit = {},
viewModel: MicroGViewModel = hiltViewModel(),
) {
ScreenContent( ScreenContent(
onInstall = { viewModel.downloadMicroG() } uiState = viewModel.uiState,
onInstall = { viewModel.downloadMicroG() },
onMicrogTOSChecked = onMicrogTOSChecked,
) )
} }
@Composable @Composable
private fun ScreenContent(onInstall: () -> Unit = {}) { private fun ScreenContent(
uiState: MicroGUIState,
onInstall: () -> Unit = {},
onMicrogTOSChecked: (Boolean) -> Unit = {}
) {
val context = LocalContext.current val context = LocalContext.current
Column( Column(
@@ -58,7 +68,7 @@ private fun ScreenContent(onInstall: () -> Unit = {}) {
) )
Text( Text(
text = stringResource(R.string.onboarding_title_gsf_desc), text = stringResource(R.string.onboarding_title_gsf_desc),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleMedium,
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
@@ -73,7 +83,9 @@ private fun ScreenContent(onInstall: () -> Unit = {}) {
else -> context.toast(R.string.permissions_denied) else -> context.toast(R.string.permissions_denied)
} }
} },
uiState = uiState,
onTOSChecked = onMicrogTOSChecked
) )
} }
@@ -83,6 +95,8 @@ private fun ScreenContent(onInstall: () -> Unit = {}) {
@Composable @Composable
private fun MicroGPagePreview() { private fun MicroGPagePreview() {
PreviewTemplate { 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.Row
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.widthIn import androidx.compose.foundation.layout.widthIn
import androidx.compose.foundation.pager.HorizontalPager import androidx.compose.foundation.pager.HorizontalPager
@@ -25,11 +26,11 @@ 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
import androidx.compose.ui.tooling.preview.Preview import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel import androidx.hilt.lifecycle.viewmodel.compose.hiltViewModel
import com.aurora.extensions.isWindowCompact import com.aurora.extensions.isWindowCompact
import com.aurora.store.R 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.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.OnboardingUiState
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 if (viewModel.isMicroGPromptRequired) OnboardingPage.MICRO_G else null
) )
ScreenContent( ScreenContent(
pages = pages, 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( private fun ScreenContent(
pages: List<OnboardingPage> = emptyList(), pages: List<OnboardingPage> = emptyList(),
onFinishOnboarding: () -> Unit = {}, onFinishOnboarding: () -> Unit = {},
uiState: OnboardingUiState,
onMicrogTOSChecked: (Boolean) -> Unit,
windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo() windowAdaptiveInfo: WindowAdaptiveInfo = currentWindowAdaptiveInfo()
) { ) {
val pagerState = rememberPagerState { pages.size } val pagerState = rememberPagerState { pages.size }
@@ -70,7 +76,7 @@ private fun ScreenContent(
} }
@Composable @Composable
fun SetupActions() { fun SetupActions(uiState: OnboardingUiState) {
val horizontalButtonPadding = when { val horizontalButtonPadding = when {
windowAdaptiveInfo.isWindowCompact -> dimensionResource(R.dimen.padding_medium) windowAdaptiveInfo.isWindowCompact -> dimensionResource(R.dimen.padding_medium)
else -> dimensionResource(R.dimen.padding_xlarge) else -> dimensionResource(R.dimen.padding_xlarge)
@@ -119,6 +125,7 @@ private fun ScreenContent(
Button( Button(
modifier = buttonWidthModifier, modifier = buttonWidthModifier,
enabled = !isFinalPage() || (!uiState.isMicroBundleChecked || uiState.isMicroGBundleInstalled),
onClick = { onClick = {
when { when {
isFinalPage() -> onFinishOnboarding() isFinalPage() -> onFinishOnboarding()
@@ -145,6 +152,7 @@ private fun ScreenContent(
Scaffold( Scaffold(
topBar = { topBar = {
TopAppBar( TopAppBar(
modifier = Modifier.height(64.dp),
title = { title = {
PageIndicator(totalPages = pages.size, currentPage = pagerState.currentPage) PageIndicator(totalPages = pages.size, currentPage = pagerState.currentPage)
} }
@@ -175,12 +183,12 @@ 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() OnboardingPage.MICRO_G -> MicroGPage(onMicrogTOSChecked = onMicrogTOSChecked)
} }
} }
} }
SetupActions() SetupActions(uiState)
} }
} }
} }
@@ -190,7 +198,9 @@ private fun ScreenContent(
private fun OnboardingScreenPreview() { private fun OnboardingScreenPreview() {
PreviewTemplate { PreviewTemplate {
ScreenContent( 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(
text = stringResource(R.string.onboarding_permission_select), text = stringResource(R.string.onboarding_permission_select),
style = MaterialTheme.typography.titleLarge, style = MaterialTheme.typography.titleMedium,
maxLines = 2, maxLines = 2,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )

View File

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

View File

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

View File

@@ -26,6 +26,10 @@ object MigrationHelper {
override fun migrate(db: SupportSQLiteDatabase) = migrateFrom4To5(db) 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 const val TAG = "MigrationHelper"
private fun migrateFrom1To2(database: SupportSQLiteDatabase) { private fun migrateFrom1To2(database: SupportSQLiteDatabase) {
@@ -83,4 +87,21 @@ object MigrationHelper {
database.endTransaction() 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_2_3
import com.aurora.store.data.room.MigrationHelper.MIGRATION_3_4 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_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.DownloadConverter
import com.aurora.store.data.room.download.DownloadDao import com.aurora.store.data.room.download.DownloadDao
import com.aurora.store.data.room.favourite.FavouriteDao import com.aurora.store.data.room.favourite.FavouriteDao
@@ -30,7 +31,7 @@ object RoomModule {
downloadConverter: DownloadConverter downloadConverter: DownloadConverter
): AuroraDatabase { ): AuroraDatabase {
return Room.databaseBuilder(context, AuroraDatabase::class.java, DATABASE) 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) .addTypeConverter(downloadConverter)
.build() .build()
} }

View File

@@ -5,6 +5,7 @@ import android.os.Parcelable
import androidx.room.ColumnInfo import androidx.room.ColumnInfo
import androidx.room.Entity import androidx.room.Entity
import androidx.room.PrimaryKey import androidx.room.PrimaryKey
import com.aurora.extensions.requiresGMS
import com.aurora.gplayapi.data.models.App import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.PlayFile import com.aurora.gplayapi.data.models.PlayFile
import com.aurora.store.data.model.DownloadStatus import com.aurora.store.data.model.DownloadStatus
@@ -35,7 +36,8 @@ data class Download(
var fileList: List<PlayFile>, var fileList: List<PlayFile>,
val sharedLibs: List<SharedLib>, val sharedLibs: List<SharedLib>,
val targetSdk: Int = 1, val targetSdk: Int = 1,
val downloadedAt: Long = 0 val downloadedAt: Long = 0,
val requiresGMS: Boolean = false
) : Parcelable { ) : Parcelable {
val isFinished get() = status in DownloadStatus.finished val isFinished get() = status in DownloadStatus.finished
val isRunning get() = status in DownloadStatus.running val isRunning get() = status in DownloadStatus.running
@@ -61,7 +63,8 @@ data class Download(
app.fileList.filterNot { it.url.isBlank() }, app.fileList.filterNot { it.url.isBlank() },
app.dependencies.dependentLibraries.map { SharedLib.fromApp(it) }, app.dependencies.dependentLibraries.map { SharedLib.fromApp(it) },
app.targetSdk, 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_CANCELLED_BY_APP
import androidx.work.WorkInfo.Companion.STOP_REASON_USER import androidx.work.WorkInfo.Companion.STOP_REASON_USER
import androidx.work.WorkerParameters import androidx.work.WorkerParameters
import com.aurora.Constants.FLAVOUR_HUAWEI
import com.aurora.extensions.copyTo import com.aurora.extensions.copyTo
import com.aurora.extensions.isPAndAbove import com.aurora.extensions.isPAndAbove
import com.aurora.extensions.isQAndAbove 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.helpers.PurchaseHelper
import com.aurora.gplayapi.network.IHttpClient import com.aurora.gplayapi.network.IHttpClient
import com.aurora.store.AuroraApp import com.aurora.store.AuroraApp
import com.aurora.store.BuildConfig
import com.aurora.store.R import com.aurora.store.R
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
@@ -41,6 +43,7 @@ import com.aurora.store.data.room.download.Download
import com.aurora.store.data.room.download.DownloadDao import com.aurora.store.data.room.download.DownloadDao
import com.aurora.store.util.CertUtil import com.aurora.store.util.CertUtil
import com.aurora.store.util.NotificationUtil import com.aurora.store.util.NotificationUtil
import com.aurora.store.util.PackageUtil
import com.aurora.store.util.PathUtil import com.aurora.store.util.PathUtil
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject 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 NoNetworkException : Exception(context.getString(R.string.title_no_network))
inner class NothingToDownloadException : Exception(context.getString(R.string.purchase_no_file)) inner class NothingToDownloadException : Exception(context.getString(R.string.purchase_no_file))
inner class DownloadFailedException : Exception(context.getString(R.string.download_failed)) inner class DownloadFailedException : Exception(context.getString(R.string.download_failed))
inner class DownloadCancelledException : Exception(context.getString(R.string.download_canceled)) inner class DownloadCancelledException :
inner class VerificationFailedException : Exception(context.getString(R.string.verification_failed)) Exception(context.getString(R.string.download_canceled))
inner class VerificationFailedException :
Exception(context.getString(R.string.verification_failed))
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
super.doWork() super.doWork()
@@ -198,7 +204,10 @@ class DownloadWorker @AssistedInject constructor(
private suspend fun onSuccess(): Result { private suspend fun onSuccess(): Result {
return withContext(NonCancellable) { return withContext(NonCancellable) {
return@withContext try { 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() Result.success()
} catch (exception: Exception) { } catch (exception: Exception) {
Log.e(TAG, "Failed to install ${download.packageName}", exception) Log.e(TAG, "Failed to install ${download.packageName}", exception)

View File

@@ -5,31 +5,60 @@
package com.aurora.store.viewmodel.onboarding 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.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 com.aurora.store.util.PackageUtil
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.Dispatchers
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject import javax.inject.Inject
data class MicroGUIState(
var isDownloading: Boolean = false,
var isInstalled: Boolean = false
)
@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 {
private const val MICROG_DOWNLOAD_URL = private const val MICROG_DOWNLOAD_URL =
"https://github.com/microg/GmsCore/releases/download" "https://github.com/microg/GmsCore/releases/download"
private const val MICROG_VERSION = "v0.3.6.244735" private const val MICROG_VERSION = "v0.3.11.250932"
private const val GMS_VERSION_CODE = 244735012 private const val GMS_VERSION_CODE = 250932022
private const val COMPANION_VERSION_CODE = 84022612 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( private val microGServiceApk = ExternalApk(
packageName = PACKAGE_NAME_GMS, packageName = PACKAGE_NAME_GMS,
versionCode = GMS_VERSION_CODE.toLong(), versionCode = GMS_VERSION_CODE.toLong(),
@@ -41,8 +70,8 @@ class MicroGViewModel @Inject constructor(
PlayFile( PlayFile(
url = "$MICROG_DOWNLOAD_URL/$MICROG_VERSION/$PACKAGE_NAME_GMS-$GMS_VERSION_CODE-hw.apk", url = "$MICROG_DOWNLOAD_URL/$MICROG_VERSION/$PACKAGE_NAME_GMS-$GMS_VERSION_CODE-hw.apk",
name = "$PACKAGE_NAME_GMS-$GMS_VERSION_CODE-hw.apk", name = "$PACKAGE_NAME_GMS-$GMS_VERSION_CODE-hw.apk",
size = 32509431, size = 92386474,
sha256 = "2f14df2974811b576bfafa6167a97e3b3032f2bd6e6ec3887a833fd2fa350dda" sha256 = "2894a93544a8d7ca8f6ca96e7cc697647a7e0862165b6a02f8cd26822759b9cc"
) )
) )
) )
@@ -58,16 +87,33 @@ class MicroGViewModel @Inject constructor(
PlayFile( PlayFile(
url = "$MICROG_DOWNLOAD_URL/$MICROG_VERSION/$PACKAGE_NAME_PLAY_STORE-$COMPANION_VERSION_CODE-hw.apk", 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", name = "$PACKAGE_NAME_PLAY_STORE-$COMPANION_VERSION_CODE-hw.apk",
size = 3915551, size = 4626291,
sha256 = "6835b09016cef0fc3469b4a36b1720427ad3f81161cf20b188f0dadb5f8594e1" sha256 = "b9623b8da8791c7e887efca941434b20e517c8a42ca4fda713625957edcc84eb"
) )
) )
) )
fun downloadMicroG() { fun downloadMicroG() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
downloadHelper.enqueueStandalone(microGServiceApk) if (microGCompanionApk.isInstalled(context)) {
downloadHelper.enqueueStandalone(microGCompanionApk) 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.content.Context
import android.util.Log import android.util.Log
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel 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.areNotificationsEnabled
import com.aurora.extensions.isIgnoringBatteryOptimizations 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.helper.UpdateHelper
import com.aurora.store.data.model.UpdateMode 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.data.work.CacheWorker
import com.aurora.store.util.FlavouredUtil import com.aurora.store.util.FlavouredUtil
import com.aurora.store.util.PackageUtil
import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE import com.aurora.store.util.Preferences.PREFERENCE_AUTO_DELETE
import com.aurora.store.util.Preferences.PREFERENCE_DEFAULT_SELECTED_TAB 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 com.jakewharton.processphoenix.ProcessPhoenix
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.flow.launchIn
import kotlinx.coroutines.flow.onEach
import javax.inject.Inject import javax.inject.Inject
data class OnboardingUiState(
val isMicroBundleChecked: Boolean = false,
val isMicroGBundleInstalled: Boolean = false
)
@HiltViewModel @HiltViewModel
class OnboardingViewModel @Inject constructor( class OnboardingViewModel @Inject constructor(
val updateHelper: UpdateHelper, val updateHelper: UpdateHelper,
val blacklistProvider: BlacklistProvider,
@ApplicationContext private val context: Context @ApplicationContext private val context: Context
) : ViewModel() { ) : ViewModel() {
private val TAG = OnboardingViewModel::class.java.simpleName 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() { fun finishOnboarding() {
Log.i(TAG, "Finishing onboarding with defaults") Log.i(TAG, "Finishing onboarding with defaults")
context.saveDefaultPreferences() context.saveDefaultPreferences()
if (BuildConfig.FLAVOR == FLAVOUR_HUAWEI) {
blacklistProvider.blacklist(PACKAGE_NAME_GMS)
blacklistProvider.blacklist(PACKAGE_NAME_PLAY_STORE)
}
setupAutoUpdates() setupAutoUpdates()
CacheWorker.scheduleAutomatedCacheCleanup(context) CacheWorker.scheduleAutomatedCacheCleanup(context)
Preferences.putBooleanNow(context, PREFERENCE_INTRO, true) Preferences.putBooleanNow(context, PREFERENCE_INTRO, true)
@@ -53,11 +96,17 @@ class OnboardingViewModel @Inject constructor(
ProcessPhoenix.triggerRebirth(context) ProcessPhoenix.triggerRebirth(context)
} }
private fun confirmBundleInstall() {
if (PackageUtil.isMicroGBundleInstalled(context)) {
uiState = uiState.copy(isMicroGBundleInstalled = true)
}
}
private fun setupAutoUpdates() { private fun setupAutoUpdates() {
val updateMode = when { val updateMode = when {
context.isIgnoringBatteryOptimizations() -> UpdateMode.CHECK_AND_INSTALL context.isIgnoringBatteryOptimizations() -> UpdateMode.CHECK_AND_INSTALL
context.areNotificationsEnabled() -> UpdateMode.CHECK_AND_NOTIFY context.areNotificationsEnabled() -> UpdateMode.CHECK_AND_NOTIFY
else -> UpdateMode.DISABLED else -> UpdateMode.DISABLED
} }
context.save(PREFERENCE_UPDATES_AUTO, updateMode.ordinal) context.save(PREFERENCE_UPDATES_AUTO, updateMode.ordinal)

View File

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