ktlint: Reformat data directory

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2026-01-01 11:41:09 +08:00
parent be13f86a37
commit 5452385c6d
51 changed files with 558 additions and 551 deletions

View File

@@ -44,6 +44,6 @@ open class InstallerEvent(open val packageName: String) : Event() {
data class Failed( data class Failed(
override val packageName: String, override val packageName: String,
val error: String? = null, val error: String? = null,
val extra: String? = null, val extra: String? = null
) : InstallerEvent(packageName) ) : InstallerEvent(packageName)
} }

View File

@@ -2,9 +2,9 @@ package com.aurora.store.data.event
import android.util.Log import android.util.Log
import com.aurora.extensions.TAG import com.aurora.extensions.TAG
import javax.inject.Singleton
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.asSharedFlow import kotlinx.coroutines.flow.asSharedFlow
import javax.inject.Singleton
@Singleton @Singleton
class EventFlow { class EventFlow {

View File

@@ -18,13 +18,13 @@ import com.aurora.store.data.room.update.Update
import com.aurora.store.data.work.DownloadWorker import com.aurora.store.data.work.DownloadWorker
import com.aurora.store.util.PathUtil import com.aurora.store.util.PathUtil
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
/** /**
* Helper class to work with the [DownloadWorker]. * Helper class to work with the [DownloadWorker].

View File

@@ -25,6 +25,10 @@ import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_IDLE
import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_METERED import com.aurora.store.util.Preferences.PREFERENCES_UPDATES_RESTRICTIONS_METERED
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_CHECK_INTERVAL
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import java.util.UUID
import java.util.concurrent.TimeUnit.HOURS
import java.util.concurrent.TimeUnit.MINUTES
import javax.inject.Inject
import kotlinx.coroutines.flow.SharingStarted import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.firstOrNull import kotlinx.coroutines.flow.firstOrNull
import kotlinx.coroutines.flow.launchIn import kotlinx.coroutines.flow.launchIn
@@ -32,10 +36,6 @@ import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.onEach import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.stateIn import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.UUID
import java.util.concurrent.TimeUnit.HOURS
import java.util.concurrent.TimeUnit.MINUTES
import javax.inject.Inject
/** /**
* Helper class to work with the [UpdateWorker]. * Helper class to work with the [UpdateWorker].

View File

@@ -31,6 +31,7 @@ import com.aurora.extensions.isOAndAbove
import com.aurora.extensions.isPAndAbove import com.aurora.extensions.isPAndAbove
import com.aurora.extensions.isSAndAbove import com.aurora.extensions.isSAndAbove
import com.aurora.store.BuildConfig import com.aurora.store.BuildConfig
import com.aurora.store.data.installer.ShizukuInstaller.Companion.SHIZUKU_PACKAGE_NAME
import com.aurora.store.data.installer.base.IInstaller import com.aurora.store.data.installer.base.IInstaller
import com.aurora.store.data.model.Installer import com.aurora.store.data.model.Installer
import com.aurora.store.data.model.InstallerInfo import com.aurora.store.data.model.InstallerInfo
@@ -40,10 +41,10 @@ import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID import com.aurora.store.util.Preferences.PREFERENCE_INSTALLER_ID
import com.topjohnwu.superuser.Shell import com.topjohnwu.superuser.Shell
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import rikka.shizuku.Shizuku
import rikka.sui.Sui
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import rikka.shizuku.Shizuku
import rikka.sui.Sui
@Singleton @Singleton
class AppInstaller @Inject constructor( class AppInstaller @Inject constructor(
@@ -58,27 +59,28 @@ class AppInstaller @Inject constructor(
) { ) {
companion object { companion object {
const val ACTION_INSTALL_STATUS = "com.aurora.store.data.installer.AppInstaller.INSTALL_STATUS" const val ACTION_INSTALL_STATUS =
"com.aurora.store.data.installer.AppInstaller.INSTALL_STATUS"
const val EXTRA_PACKAGE_NAME = "com.aurora.store.data.installer.AppInstaller.EXTRA_PACKAGE_NAME" const val EXTRA_PACKAGE_NAME =
const val EXTRA_VERSION_CODE = "com.aurora.store.data.installer.AppInstaller.EXTRA_VERSION_CODE" "com.aurora.store.data.installer.AppInstaller.EXTRA_PACKAGE_NAME"
const val EXTRA_DISPLAY_NAME = "com.aurora.store.data.installer.AppInstaller.EXTRA_DISPLAY_NAME" const val EXTRA_VERSION_CODE =
"com.aurora.store.data.installer.AppInstaller.EXTRA_VERSION_CODE"
const val EXTRA_DISPLAY_NAME =
"com.aurora.store.data.installer.AppInstaller.EXTRA_DISPLAY_NAME"
fun getCurrentInstaller(context: Context): Installer { fun getCurrentInstaller(context: Context): Installer =
return Installer.entries[Preferences.getInteger(context, PREFERENCE_INSTALLER_ID)] Installer.entries[Preferences.getInteger(context, PREFERENCE_INSTALLER_ID)]
}
fun getAvailableInstallersInfo(context: Context): List<InstallerInfo> { fun getAvailableInstallersInfo(context: Context): List<InstallerInfo> = listOfNotNull(
return listOfNotNull( SessionInstaller.installerInfo,
SessionInstaller.installerInfo, NativeInstaller.installerInfo,
NativeInstaller.installerInfo, if (hasRootAccess()) RootInstaller.installerInfo else null,
if (hasRootAccess()) RootInstaller.installerInfo else null, if (hasAuroraService(context)) ServiceInstaller.installerInfo else null,
if (hasAuroraService(context)) ServiceInstaller.installerInfo else null, if (hasAppManager(context)) AMInstaller.installerInfo else null,
if (hasAppManager(context)) AMInstaller.installerInfo else null, if (hasShizukuOrSui(context)) ShizukuInstaller.installerInfo else null,
if (hasShizukuOrSui(context)) ShizukuInstaller.installerInfo else null, if (hasMicroGCompanion(context)) MicroGInstaller.installerInfo else null
if (hasMicroGCompanion(context)) MicroGInstaller.installerInfo else null )
)
}
/** /**
* Checks if the given package can be silently installed * Checks if the given package can be silently installed
@@ -92,60 +94,71 @@ class AppInstaller @Inject constructor(
if (!PackageUtil.isInstalled(context, packageName) || !isSAndAbove) return false if (!PackageUtil.isInstalled(context, packageName) || !isSAndAbove) return false
// We cannot do silent updates if we are not the update owner // We cannot do silent updates if we are not the update owner
if (context.packageManager.getUpdateOwnerPackageNameCompat(packageName) != BuildConfig.APPLICATION_ID) return false if (context.packageManager.getUpdateOwnerPackageNameCompat(packageName) !=
BuildConfig.APPLICATION_ID
) {
return false
}
// Ensure app being installed satisfies Android's requirement for targetSdk level // Ensure app being installed satisfies Android's requirement for targetSdk level
when (Build.VERSION.SDK_INT) { when (Build.VERSION.SDK_INT) {
Build.VERSION_CODES.BAKLAVA -> targetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE Build.VERSION_CODES.BAKLAVA -> {
Build.VERSION_CODES.VANILLA_ICE_CREAM -> targetSdk >= Build.VERSION_CODES.TIRAMISU targetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
}
Build.VERSION_CODES.VANILLA_ICE_CREAM -> {
targetSdk >= Build.VERSION_CODES.TIRAMISU
}
Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> targetSdk >= Build.VERSION_CODES.S Build.VERSION_CODES.UPSIDE_DOWN_CAKE -> targetSdk >= Build.VERSION_CODES.S
Build.VERSION_CODES.TIRAMISU -> targetSdk >= Build.VERSION_CODES.R Build.VERSION_CODES.TIRAMISU -> targetSdk >= Build.VERSION_CODES.R
Build.VERSION_CODES.S -> targetSdk >= Build.VERSION_CODES.Q Build.VERSION_CODES.S -> targetSdk >= Build.VERSION_CODES.Q
else -> false // Only Android version above 12 can silently update apps else -> false // Only Android version above 12 can silently update apps
} }
} }
Installer.NATIVE -> false // Native installer requires user interaction
// Native installer requires user interaction
Installer.NATIVE -> false
Installer.ROOT -> hasRootAccess() Installer.ROOT -> hasRootAccess()
Installer.SERVICE -> hasAuroraService(context) Installer.SERVICE -> hasAuroraService(context)
Installer.AM -> false // We cannot check if AppManager has ability to auto-update
// We cannot check if AppManager has ability to auto-update
Installer.AM -> false
Installer.SHIZUKU -> isOAndAbove && hasShizukuOrSui(context) && hasShizukuPerm() Installer.SHIZUKU -> isOAndAbove && hasShizukuOrSui(context) && hasShizukuPerm()
Installer.MICROG -> false Installer.MICROG -> false
} }
} }
fun hasRootAccess(): Boolean { fun hasRootAccess(): Boolean = Shell.getShell().isRoot
return Shell.getShell().isRoot
}
fun hasAuroraService(context: Context): Boolean { fun hasAuroraService(context: Context): Boolean = try {
return try { val packageInfo = PackageUtil.getPackageInfo(
val packageInfo = PackageUtil.getPackageInfo(
context,
ServiceInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME
)
val version = PackageInfoCompat.getLongVersionCode(packageInfo)
packageInfo.applicationInfo!!.enabled && version >= 9
} catch (exception: Exception) {
false
}
}
fun hasAppManager(context: Context): Boolean {
return PackageUtil.isInstalled(context, AMInstaller.AM_PACKAGE_NAME) or
PackageUtil.isInstalled(context, AMInstaller.AM_DEBUG_PACKAGE_NAME)
}
fun hasShizukuOrSui(context: Context): Boolean {
return isOAndAbove && (PackageUtil.isInstalled(
context, context,
ShizukuInstaller.SHIZUKU_PACKAGE_NAME ServiceInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME
) || Sui.isSui()) )
val version = PackageInfoCompat.getLongVersionCode(packageInfo)
packageInfo.applicationInfo!!.enabled && version >= 9
} catch (_: Exception) {
false
} }
fun hasShizukuPerm(): Boolean { fun hasAppManager(context: Context): Boolean =
return Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED PackageUtil.isInstalled(context, AMInstaller.AM_PACKAGE_NAME) or
} PackageUtil.isInstalled(context, AMInstaller.AM_DEBUG_PACKAGE_NAME)
fun hasShizukuOrSui(context: Context): Boolean = isOAndAbove &&
(PackageUtil.isInstalled(context, SHIZUKU_PACKAGE_NAME) || Sui.isSui())
fun hasShizukuPerm(): Boolean =
Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED
fun uninstall(context: Context, packageName: String) { fun uninstall(context: Context, packageName: String) {
val intent = Intent().apply { val intent = Intent().apply {
@@ -166,25 +179,33 @@ class AppInstaller @Inject constructor(
private val defaultInstaller: IInstaller private val defaultInstaller: IInstaller
get() = sessionInstaller get() = sessionInstaller
fun getMicroGInstaller(): IInstaller { fun getMicroGInstaller(): IInstaller = microGInstaller
return microGInstaller
}
fun getPreferredInstaller(): IInstaller { fun getPreferredInstaller(): IInstaller = when (getCurrentInstaller(context)) {
return when (getCurrentInstaller(context)) { Installer.SESSION -> sessionInstaller
Installer.SESSION -> sessionInstaller
Installer.NATIVE -> nativeInstaller Installer.NATIVE -> nativeInstaller
Installer.ROOT -> if (hasRootAccess()) rootInstaller else defaultInstaller
Installer.SERVICE -> if (hasAuroraService(context)) serviceInstaller else defaultInstaller Installer.ROOT -> if (hasRootAccess()) rootInstaller else defaultInstaller
Installer.AM -> if (hasAppManager(context)) amInstaller else defaultInstaller
Installer.SHIZUKU -> { Installer.SERVICE -> if (hasAuroraService(context)) {
if (hasShizukuOrSui(context) && hasShizukuPerm()) { serviceInstaller
shizukuInstaller } else {
} else { defaultInstaller
defaultInstaller }
}
} Installer.AM -> if (hasAppManager(context)) amInstaller else defaultInstaller
Installer.MICROG -> if(hasMicroGCompanion(context)) microGInstaller else defaultInstaller
Installer.SHIZUKU -> if (hasShizukuOrSui(context) && hasShizukuPerm()) {
shizukuInstaller
} else {
defaultInstaller
}
Installer.MICROG -> if (hasMicroGCompanion(context)) {
microGInstaller
} else {
defaultInstaller
} }
} }
} }

View File

@@ -53,13 +53,12 @@ class MicroGInstaller @Inject constructor(
description = R.string.microg_installer_desc description = R.string.microg_installer_desc
) )
fun buildMicroGInstallIntent(uris: ArrayList<Uri>): Intent { fun buildMicroGInstallIntent(uris: ArrayList<Uri>): Intent =
return Intent("org.microg.vending.action.INSTALL_PACKAGE").apply { Intent("org.microg.vending.action.INSTALL_PACKAGE").apply {
setPackage(PACKAGE_NAME_PLAY_STORE) setPackage(PACKAGE_NAME_PLAY_STORE)
setType("application/vnd.android.package-archive") setType("application/vnd.android.package-archive")
putExtra(Intent.EXTRA_STREAM, uris) putExtra(Intent.EXTRA_STREAM, uris)
} }
}
} }
override fun install(download: Download) { override fun install(download: Download) {

View File

@@ -75,7 +75,10 @@ class RootInstaller @Inject constructor(
context.getString(R.string.installer_status_failure), context.getString(R.string.installer_status_failure),
context.getString(R.string.installer_root_unavailable) context.getString(R.string.installer_root_unavailable)
) )
Log.e(TAG, " >>>>>>>>>>>>>>>>>>>>>>>>>> NO ROOT ACCESS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<") Log.e(
TAG,
" >>>>>>>>>>>>>>>>>>>>>>>>>> NO ROOT ACCESS <<<<<<<<<<<<<<<<<<<<<<<<<<<<<"
)
} }
} }
} }
@@ -83,8 +86,9 @@ class RootInstaller @Inject constructor(
private fun xInstall(packageName: String, versionCode: Long, sharedLibPkgName: String = "") { private fun xInstall(packageName: String, versionCode: Long, sharedLibPkgName: String = "") {
var totalSize = 0 var totalSize = 0
for (file in getFiles(packageName, versionCode, sharedLibPkgName)) for (file in getFiles(packageName, versionCode, sharedLibPkgName)) {
totalSize += file.length().toInt() totalSize += file.length().toInt()
}
val result: Shell.Result = val result: Shell.Result =
Shell.cmd("pm install-create -i $PLAY_PACKAGE_NAME --user 0 -r -S $totalSize") Shell.cmd("pm install-create -i $PLAY_PACKAGE_NAME --user 0 -r -S $totalSize")
@@ -100,7 +104,9 @@ class RootInstaller @Inject constructor(
val sessionId = sessionIdMatcher.group(1)?.toInt() val sessionId = sessionIdMatcher.group(1)?.toInt()
if (Shell.getShell().isRoot && sessionId != null) { if (Shell.getShell().isRoot && sessionId != null) {
for (file in getFiles(packageName, versionCode, sharedLibPkgName)) { for (file in getFiles(packageName, versionCode, sharedLibPkgName)) {
Shell.cmd("cat \"${file.absoluteFile}\" | pm install-write -S ${file.length()} $sessionId \"${file.name}\"") Shell.cmd(
"cat \"${file.absoluteFile}\" | pm install-write -S ${file.length()} $sessionId \"${file.name}\""
)
.exec() .exec()
} }
@@ -136,7 +142,5 @@ class RootInstaller @Inject constructor(
} }
} }
private fun parseError(result: Shell.Result): String { private fun parseError(result: Shell.Result): String = result.err.joinToString(separator = "\n")
return result.err.joinToString(separator = "\n")
}
} }

View File

@@ -216,8 +216,8 @@ class SessionInstaller @Inject constructor(
} }
} }
private fun buildSessionParams(packageName: String): SessionParams { private fun buildSessionParams(packageName: String): SessionParams =
return SessionParams(SessionParams.MODE_FULL_INSTALL).apply { SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
setAppPackageName(packageName) setAppPackageName(packageName)
setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO) setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO)
if (isNAndAbove) { if (isNAndAbove) {
@@ -238,7 +238,6 @@ class SessionInstaller @Inject constructor(
setApplicationEnabledSettingPersistent() setApplicationEnabledSettingPersistent()
} }
} }
}
private fun commitInstall(sessionInfo: SessionInfo) { private fun commitInstall(sessionInfo: SessionInfo) {
try { try {

View File

@@ -51,10 +51,10 @@ import com.aurora.store.data.room.download.Download
import com.aurora.store.util.PackageUtil.isSharedLibraryInstalled import com.aurora.store.util.PackageUtil.isSharedLibraryInstalled
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dev.rikka.tools.refine.Refine import dev.rikka.tools.refine.Refine
import rikka.shizuku.ShizukuBinderWrapper
import rikka.shizuku.SystemServiceHelper
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import rikka.shizuku.ShizukuBinderWrapper
import rikka.shizuku.SystemServiceHelper
@Singleton @Singleton
@RequiresApi(Build.VERSION_CODES.O) @RequiresApi(Build.VERSION_CODES.O)
@@ -99,7 +99,9 @@ class ShizukuInstaller @Inject constructor(
Refine.unsafeCast<PackageInstaller>( Refine.unsafeCast<PackageInstaller>(
PackageInstallerHidden(iPackageInstaller, PLAY_PACKAGE_NAME, 0) PackageInstallerHidden(iPackageInstaller, PLAY_PACKAGE_NAME, 0)
) )
} else null } else {
null
}
} }
override fun install(download: Download) { override fun install(download: Download) {

View File

@@ -43,20 +43,40 @@ abstract class InstallerBase(private val context: Context) : IInstaller {
companion object { companion object {
fun notifyInstallation(context: Context, displayName: String, packageName: String) { fun notifyInstallation(context: Context, displayName: String, packageName: String) {
val notificationManager = context.getSystemService<NotificationManager>() val notificationManager = context.getSystemService<NotificationManager>()
val notification = NotificationUtil.getInstallNotification(context, displayName, packageName) val notification = NotificationUtil.getInstallNotification(
context,
displayName,
packageName
)
notificationManager!!.notify(packageName.hashCode(), notification) notificationManager!!.notify(packageName.hashCode(), notification)
} }
fun getErrorString(context: Context, status: Int): String { fun getErrorString(context: Context, status: Int): String = when (status) {
return when (status) { PackageInstaller.STATUS_FAILURE_ABORTED -> context.getString(
PackageInstaller.STATUS_FAILURE_ABORTED -> context.getString(R.string.installer_status_user_action) R.string.installer_status_user_action
PackageInstaller.STATUS_FAILURE_BLOCKED -> context.getString(R.string.installer_status_failure_blocked) )
PackageInstaller.STATUS_FAILURE_CONFLICT -> context.getString(R.string.installer_status_failure_conflict)
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> context.getString(R.string.installer_status_failure_incompatible) PackageInstaller.STATUS_FAILURE_BLOCKED -> context.getString(
PackageInstaller.STATUS_FAILURE_INVALID -> context.getString(R.string.installer_status_failure_invalid) R.string.installer_status_failure_blocked
PackageInstaller.STATUS_FAILURE_STORAGE -> context.getString(R.string.installer_status_failure_storage) )
else -> context.getString(R.string.installer_status_failure)
} PackageInstaller.STATUS_FAILURE_CONFLICT -> context.getString(
R.string.installer_status_failure_conflict
)
PackageInstaller.STATUS_FAILURE_INCOMPATIBLE -> context.getString(
R.string.installer_status_failure_incompatible
)
PackageInstaller.STATUS_FAILURE_INVALID -> context.getString(
R.string.installer_status_failure_invalid
)
PackageInstaller.STATUS_FAILURE_STORAGE -> context.getString(
R.string.installer_status_failure_storage
)
else -> context.getString(R.string.installer_status_failure)
} }
} }
@@ -71,9 +91,8 @@ abstract class InstallerBase(private val context: Context) : IInstaller {
AuroraApp.enqueuedInstalls.clear() AuroraApp.enqueuedInstalls.clear()
} }
override fun isAlreadyQueued(packageName: String): Boolean { override fun isAlreadyQueued(packageName: String): Boolean =
return AuroraApp.enqueuedInstalls.contains(packageName) AuroraApp.enqueuedInstalls.contains(packageName)
}
override fun removeFromInstallQueue(packageName: String) { override fun removeFromInstallQueue(packageName: String) {
AuroraApp.enqueuedInstalls.remove(packageName) AuroraApp.enqueuedInstalls.remove(packageName)
@@ -113,11 +132,9 @@ abstract class InstallerBase(private val context: Context) : IInstaller {
return downloadDir.listFiles()!!.filter { it.path.endsWith(".apk") } return downloadDir.listFiles()!!.filter { it.path.endsWith(".apk") }
} }
fun getUri(file: File): Uri { fun getUri(file: File): Uri = FileProvider.getUriForFile(
return FileProvider.getUriForFile( context,
context, "${BuildConfig.APPLICATION_ID}.fileProvider",
"${BuildConfig.APPLICATION_ID}.fileProvider", file
file )
)
}
} }

View File

@@ -27,14 +27,10 @@ data class Black(val packageName: String) {
var versionName: String = String() var versionName: String = String()
var versionCode: Long = 0 var versionCode: Long = 0
override fun hashCode(): Int { override fun hashCode(): Int = packageName.hashCode()
return packageName.hashCode()
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean = when (other) {
return when (other) { is Black -> other.packageName == packageName
is Black -> other.packageName == packageName else -> false
else -> false
}
} }
} }

View File

@@ -55,14 +55,10 @@ data class ExodusTracker(
val categories: List<String> = emptyList() val categories: List<String> = emptyList()
) { ) {
override fun hashCode(): Int { override fun hashCode(): Int = id
return id
}
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean = when (other) {
return when (other) { is ExodusTracker -> other.id == id
is ExodusTracker -> other.id == id else -> false
else -> false
}
} }
} }

View File

@@ -33,14 +33,10 @@ data class InstallerInfo(
@StringRes val subtitle: Int, @StringRes val subtitle: Int,
@StringRes val description: Int @StringRes val description: Int
) { ) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean = when (other) {
return when (other) { is InstallerInfo -> other.id == id
is InstallerInfo -> other.id == id else -> false
else -> false
}
} }
override fun hashCode(): Int { override fun hashCode(): Int = id.hashCode()
return id.hashCode()
}
} }

View File

@@ -24,16 +24,12 @@ data class Link(
var title: String, var title: String,
var subtitle: String, var subtitle: String,
var url: String, var url: String,
var icon: Int, var icon: Int
) { ) {
override fun equals(other: Any?): Boolean { override fun equals(other: Any?): Boolean = when (other) {
return when (other) { is Link -> other.id == id
is Link -> other.id == id else -> false
else -> false
}
} }
override fun hashCode(): Int { override fun hashCode(): Int = id.hashCode()
return id.hashCode()
}
} }

View File

@@ -19,23 +19,18 @@ data class MinimalApp(
companion object { companion object {
fun fromApp(app: App): MinimalApp { fun fromApp(app: App): MinimalApp = MinimalApp(
return MinimalApp( app.packageName,
app.packageName, app.versionName,
app.versionName, app.versionCode,
app.versionCode, app.displayName
app.displayName )
)
}
fun fromUpdate(update: Update): MinimalApp {
return MinimalApp(
update.packageName,
update.versionName,
update.versionCode,
update.displayName
)
}
fun fromUpdate(update: Update): MinimalApp = MinimalApp(
update.packageName,
update.versionName,
update.versionCode,
update.displayName
)
} }
} }

View File

@@ -74,7 +74,8 @@ data class SelfUpdate(
isFree = true, isFree = true,
isInstalled = true, isInstalled = true,
certificateSetList = CertUtil.getEncodedCertificateHashes( certificateSetList = CertUtil.getEncodedCertificateHashes(
context, context.packageName context,
context.packageName
).map { ).map {
EncodedCertificateSet(certificateSet = it, sha256 = String()) EncodedCertificateSet(certificateSet = it, sha256 = String())
}.toMutableList() }.toMutableList()

View File

@@ -19,11 +19,8 @@
package com.aurora.store.data.model package com.aurora.store.data.model
sealed class ViewState { sealed class ViewState {
inline fun <reified T> ViewState.getDataAs(): T { inline fun <reified T> ViewState.getDataAs(): T = (this as? Success<*>)?.data as T
return (this as? Success<*>)?.data as T
}
data object Loading : ViewState() data object Loading : ViewState()
data object Empty : ViewState() data object Empty : ViewState()
@@ -33,7 +30,7 @@ sealed class ViewState {
} }
sealed class AuthState { sealed class AuthState {
data object Init: AuthState() data object Init : AuthState()
data object Available : AuthState() data object Available : AuthState()
data object Unavailable : AuthState() data object Unavailable : AuthState()
data object SignedIn : AuthState() data object SignedIn : AuthState()
@@ -68,18 +65,15 @@ sealed class AppState {
/** /**
* Whether there is some sort of ongoing process related to the app * Whether there is some sort of ongoing process related to the app
*/ */
fun inProgress(): Boolean { fun inProgress(): Boolean =
return this is Downloading || this is Installing || this is Purchasing || this is Queued this is Downloading || this is Installing || this is Purchasing || this is Queued
}
/** /**
* Progress of the process related to the app; 0 otherwise * Progress of the process related to the app; 0 otherwise
*/ */
fun progress(): Float { fun progress(): Float = when (this) {
return when (this) { is Downloading -> progress
is Downloading -> progress is Installing -> progress
is Installing -> progress else -> 0F
else -> 0F
}
} }
} }

View File

@@ -4,5 +4,5 @@ enum class UpdateMode {
DISABLED, DISABLED,
CHECK_AND_NOTIFY, CHECK_AND_NOTIFY,
CHECK_AND_INSTALL, CHECK_AND_INSTALL,
CHECK_ONLY, CHECK_ONLY
} }

View File

@@ -26,6 +26,9 @@ import com.aurora.gplayapi.network.IHttpClient
import com.aurora.store.BuildConfig.APPLICATION_ID import com.aurora.store.BuildConfig.APPLICATION_ID
import com.aurora.store.BuildConfig.VERSION_CODE import com.aurora.store.BuildConfig.VERSION_CODE
import com.aurora.store.BuildConfig.VERSION_NAME import com.aurora.store.BuildConfig.VERSION_NAME
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.asStateFlow import kotlinx.coroutines.flow.asStateFlow
@@ -38,12 +41,9 @@ import okhttp3.Request
import okhttp3.RequestBody import okhttp3.RequestBody
import okhttp3.RequestBody.Companion.toRequestBody import okhttp3.RequestBody.Companion.toRequestBody
import okhttp3.Response import okhttp3.Response
import java.io.IOException
import javax.inject.Inject
import javax.inject.Singleton
@Singleton @Singleton
class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient): IHttpClient { class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient) : IHttpClient {
companion object { companion object {
private const val POST = "POST" private const val POST = "POST"
@@ -69,7 +69,7 @@ class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient): IH
fun call(url: String, headers: Map<String, String> = emptyMap()): Response { fun call(url: String, headers: Map<String, String> = emptyMap()): Response {
val request = Request( val request = Request(
url = url.toHttpUrl(), url = url.toHttpUrl(),
headers = headers.toHeaders(), headers = headers.toHeaders()
) )
return okHttpClient.newCall(request).execute() return okHttpClient.newCall(request).execute()
} }
@@ -102,14 +102,12 @@ class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient): IH
} }
@Throws(IOException::class) @Throws(IOException::class)
override fun post(url: String, headers: Map<String, String>, body: ByteArray): PlayResponse { override fun post(url: String, headers: Map<String, String>, body: ByteArray): PlayResponse =
return post(url, headers, body.toRequestBody()) post(url, headers, body.toRequestBody())
}
@Throws(IOException::class) @Throws(IOException::class)
override fun get(url: String, headers: Map<String, String>): PlayResponse { override fun get(url: String, headers: Map<String, String>): PlayResponse =
return get(url, headers, mapOf()) get(url, headers, mapOf())
}
@Throws(IOException::class) @Throws(IOException::class)
override fun get( override fun get(
@@ -136,11 +134,7 @@ class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient): IH
} }
@Throws(IOException::class) @Throws(IOException::class)
override fun get( override fun get(url: String, headers: Map<String, String>, paramString: String): PlayResponse {
url: String,
headers: Map<String, String>,
paramString: String
): PlayResponse {
val request = Request( val request = Request(
url = "$url$paramString".toHttpUrl(), url = "$url$paramString".toHttpUrl(),
headers = headers.toHeaders(), headers = headers.toHeaders(),
@@ -165,16 +159,14 @@ class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient): IH
return urlBuilder.build() return urlBuilder.build()
} }
private fun buildPlayResponse(response: Response): PlayResponse { private fun buildPlayResponse(response: Response): PlayResponse = PlayResponse(
return PlayResponse( isSuccessful = response.isSuccessful,
isSuccessful = response.isSuccessful, code = response.code,
code = response.code, responseBytes = response.body.bytes(),
responseBytes = response.body.bytes(), errorString = if (!response.isSuccessful) response.message else String()
errorString = if (!response.isSuccessful) response.message else String() ).also {
).also { val isCached = if (response.cacheResponse != null) "CACHED" else "NETWORK"
val isCached = if (response.cacheResponse != null) "CACHED" else "NETWORK" _responseCode.value = response.code
_responseCode.value = response.code Log.i(TAG, "OKHTTP [$isCached] [${response.code}] ${response.request.url}")
Log.i(TAG, "OKHTTP [$isCached] [${response.code}] ${response.request.url}")
}
} }
} }

View File

@@ -13,7 +13,5 @@ object IHttpClientModule {
@Provides @Provides
@Singleton @Singleton
fun providesIHttpClientInstance(httpClient: HttpClient): IHttpClient { fun providesIHttpClientInstance(httpClient: HttpClient): IHttpClient = httpClient
return httpClient
}
} }

View File

@@ -33,10 +33,6 @@ import dagger.Provides
import dagger.hilt.InstallIn import dagger.hilt.InstallIn
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import dagger.hilt.components.SingletonComponent import dagger.hilt.components.SingletonComponent
import kotlinx.serialization.json.Json
import okhttp3.Cache
import okhttp3.CertificatePinner
import okhttp3.OkHttpClient
import java.io.ByteArrayInputStream import java.io.ByteArrayInputStream
import java.io.File import java.io.File
import java.io.InputStream import java.io.InputStream
@@ -49,6 +45,10 @@ import java.security.cert.CertificateFactory
import java.security.cert.X509Certificate import java.security.cert.X509Certificate
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
import javax.inject.Singleton import javax.inject.Singleton
import kotlinx.serialization.json.Json
import okhttp3.Cache
import okhttp3.CertificatePinner
import okhttp3.OkHttpClient
@Module @Module
@InstallIn(SingletonComponent::class) @InstallIn(SingletonComponent::class)
@@ -90,13 +90,13 @@ object OkHttpClientModule {
val googleRootCerts = getGoogleRootCertHashes(context).map { "sha256/$it" } val googleRootCerts = getGoogleRootCertHashes(context).map { "sha256/$it" }
.toTypedArray() .toTypedArray()
return CertificatePinner.Builder() return CertificatePinner.Builder()
.add("*.googleapis.com", *googleRootCerts) .add("*.googleapis.com", *googleRootCerts)
.add("*.google.com", *googleRootCerts) .add("*.google.com", *googleRootCerts)
.add("auroraoss.com", "sha256/mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=") // GTS Root R4 .add("auroraoss.com", "sha256/mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=")
.add("*.exodus-privacy.eu.org", "sha256/C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=") // ISRG Root X1 .add("*.exodus-privacy.eu.org", "sha256/C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=")
.add("gitlab.com", "sha256/x4QzPSC810K5/cMjb05Qm4k3Bw5zBn4lTdO/nEW/Td4=") // USERTrust RSA Certification Authority .add("gitlab.com", "sha256/x4QzPSC810K5/cMjb05Qm4k3Bw5zBn4lTdO/nEW/Td4=")
.add("plexus.techlore.tech", "sha256/C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=") // ISRG Root X1 .add("plexus.techlore.tech", "sha256/C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=")
.build() .build()
} }
@@ -108,7 +108,11 @@ object OkHttpClientModule {
val proxyInfo = json.decodeFromString<ProxyInfo>(proxyInfoString) val proxyInfo = json.decodeFromString<ProxyInfo>(proxyInfoString)
val proxy = Proxy( val proxy = Proxy(
if (proxyInfo.protocol.removeSuffix("5") == "SOCKS") Proxy.Type.SOCKS else Proxy.Type.HTTP, if (proxyInfo.protocol.removeSuffix("5") == "SOCKS") {
Proxy.Type.SOCKS
} else {
Proxy.Type.HTTP
},
InetSocketAddress.createUnresolved(proxyInfo.host, proxyInfo.port) InetSocketAddress.createUnresolved(proxyInfo.host, proxyInfo.port)
) )
@@ -117,9 +121,8 @@ object OkHttpClientModule {
if (!proxyUser.isNullOrBlank() && !proxyPassword.isNullOrBlank()) { if (!proxyUser.isNullOrBlank() && !proxyPassword.isNullOrBlank()) {
Authenticator.setDefault(object : Authenticator() { Authenticator.setDefault(object : Authenticator() {
override fun getPasswordAuthentication(): PasswordAuthentication { override fun getPasswordAuthentication(): PasswordAuthentication =
return PasswordAuthentication(proxyUser, proxyPassword.toCharArray()) PasswordAuthentication(proxyUser, proxyPassword.toCharArray())
}
}) })
} }
return proxy return proxy
@@ -131,25 +134,22 @@ object OkHttpClientModule {
@Provides @Provides
@Singleton @Singleton
fun providesCacheDir(@ApplicationContext context: Context): Cache { fun providesCacheDir(@ApplicationContext context: Context): Cache = Cache(
return Cache( directory = File(context.cacheDir, "http_cache"),
directory = File(context.cacheDir, "http_cache"), maxSize = 100L * 1024 * 1024
maxSize = 100L * 1024 * 1024 )
)
}
private fun getGoogleRootCertHashes(context: Context): List<String> { private fun getGoogleRootCertHashes(context: Context): List<String> = try {
return try { val certs =
val certs = getX509Certificates(context.resources.openRawResource(R.raw.google_roots_ca)) getX509Certificates(context.resources.openRawResource(R.raw.google_roots_ca))
certs.map { certs.map {
val messageDigest = MessageDigest.getInstance(Algorithm.SHA256.value) val messageDigest = MessageDigest.getInstance(Algorithm.SHA256.value)
messageDigest.update(it.publicKey.encoded) messageDigest.update(it.publicKey.encoded)
Base64.encodeToString(messageDigest.digest(), Base64.NO_WRAP) Base64.encodeToString(messageDigest.digest(), Base64.NO_WRAP)
}
} catch (exception: Exception) {
Log.e(TAG, "Failed to get SHA256 certificate hash", exception)
emptyList()
} }
} catch (exception: Exception) {
Log.e(TAG, "Failed to get SHA256 certificate hash", exception)
emptyList()
} }
private fun getX509Certificates(inputStream: InputStream): List<X509Certificate> { private fun getX509Certificates(inputStream: InputStream): List<X509Certificate> {

View File

@@ -78,10 +78,9 @@ class GenericPagingSource<T : Any>(
} }
} }
override fun getRefreshKey(state: PagingState<Int, T>): Int? { override fun getRefreshKey(state: PagingState<Int, T>): Int? =
return state.anchorPosition?.let { anchorPosition -> state.anchorPosition?.let { anchorPosition ->
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1) state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1) ?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
} }
}
} }

View File

@@ -36,9 +36,8 @@ object AccountProvider {
} }
} }
fun isLoggedIn(context: Context): Boolean { fun isLoggedIn(context: Context): Boolean =
return Preferences.getBoolean(context, Constants.ACCOUNT_SIGNED_IN, false) Preferences.getBoolean(context, Constants.ACCOUNT_SIGNED_IN, false)
}
fun getLoginEmail(context: Context): String? { fun getLoginEmail(context: Context): String? {
val email = Preferences.getString(context, Constants.ACCOUNT_EMAIL_PLAIN) val email = Preferences.getString(context, Constants.ACCOUNT_EMAIL_PLAIN)

View File

@@ -33,11 +33,11 @@ import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_AUTH_DATA import com.aurora.store.util.Preferences.PREFERENCE_AUTH_DATA
import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS import com.aurora.store.util.Preferences.PREFERENCE_DISPENSER_URLS
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import javax.inject.Inject
import javax.inject.Singleton
@Singleton @Singleton
class AuthProvider @Inject constructor( class AuthProvider @Inject constructor(
@@ -70,9 +70,7 @@ class AuthProvider @Inject constructor(
/** /**
* Checks whether saved AuthData is valid or not * Checks whether saved AuthData is valid or not
*/ */
fun isSavedAuthDataValid(): Boolean { fun isSavedAuthDataValid(): Boolean = AuthHelper.isValid(authData!!)
return AuthHelper.isValid(authData!!)
}
/** /**
* Builds [AuthData] for login using personal Google account * Builds [AuthData] for login using personal Google account
@@ -94,7 +92,7 @@ class AuthProvider @Inject constructor(
token = token, token = token,
tokenType = tokenType, tokenType = tokenType,
properties = spoofProvider.deviceProperties, properties = spoofProvider.deviceProperties,
locale = spoofProvider.locale, locale = spoofProvider.locale
) )
) )
} catch (exception: Exception) { } catch (exception: Exception) {
@@ -154,10 +152,15 @@ class AuthProvider @Inject constructor(
private fun throwError(playResponse: PlayResponse, context: Context) { private fun throwError(playResponse: PlayResponse, context: Context) {
when (playResponse.code) { when (playResponse.code) {
400 -> throw Exception(context.getString(R.string.bad_request)) 400 -> throw Exception(context.getString(R.string.bad_request))
403 -> throw Exception(context.getString(R.string.access_denied_using_vpn)) 403 -> throw Exception(context.getString(R.string.access_denied_using_vpn))
404 -> throw Exception(context.getString(R.string.server_unreachable)) 404 -> throw Exception(context.getString(R.string.server_unreachable))
429 -> throw Exception(context.getString(R.string.login_rate_limited)) 429 -> throw Exception(context.getString(R.string.login_rate_limited))
503 -> throw Exception(context.getString(R.string.server_maintenance)) 503 -> throw Exception(context.getString(R.string.server_maintenance))
else -> { else -> {
if (playResponse.errorString.isNotBlank()) { if (playResponse.errorString.isNotBlank()) {
throw Exception(playResponse.errorString) throw Exception(playResponse.errorString)

View File

@@ -24,15 +24,15 @@ import android.content.SharedPreferences
import com.aurora.extensions.isNAndAbove import com.aurora.extensions.isNAndAbove
import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.serialization.json.Json
import java.io.File import java.io.File
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlinx.serialization.json.Json
@Singleton @Singleton
class BlacklistProvider @Inject constructor( class BlacklistProvider @Inject constructor(
private val json: Json, private val json: Json,
@ApplicationContext val context: Context, @ApplicationContext val context: Context
) { ) {
companion object { companion object {
@@ -67,19 +67,17 @@ class BlacklistProvider @Inject constructor(
} else { } else {
Preferences.getString(context, PREFERENCE_BLACKLIST) Preferences.getString(context, PREFERENCE_BLACKLIST)
} }
if (rawBlacklist!!.isEmpty()) if (rawBlacklist!!.isEmpty()) {
mutableSetOf() mutableSetOf()
else } else {
json.decodeFromString<MutableSet<String>>(rawBlacklist) json.decodeFromString<MutableSet<String>>(rawBlacklist)
}
} catch (e: Exception) { } catch (e: Exception) {
mutableSetOf() mutableSetOf()
} }
} }
fun isBlacklisted(packageName: String): Boolean { fun isBlacklisted(packageName: String): Boolean = blacklist.contains(packageName)
return blacklist.contains(packageName)
}
fun blacklist(packageName: String) { fun blacklist(packageName: String) {
blacklist = blacklist.apply { blacklist = blacklist.apply {

View File

@@ -41,8 +41,10 @@ object EglExtensionProvider {
val configs = arrayOfNulls<EGLConfig>(configCount[0]) val configs = arrayOfNulls<EGLConfig>(configCount[0])
if (egl.eglGetConfigs(display, configs, configCount[0], configCount)) { if (egl.eglGetConfigs(display, configs, configCount[0], configCount)) {
val pbufferAttribs = intArrayOf( val pbufferAttribs = intArrayOf(
EGL10.EGL_WIDTH, EGL10.EGL_PBUFFER_BIT, EGL10.EGL_WIDTH,
EGL10.EGL_HEIGHT, EGL10.EGL_PBUFFER_BIT, EGL10.EGL_PBUFFER_BIT,
EGL10.EGL_HEIGHT,
EGL10.EGL_PBUFFER_BIT,
EGL10.EGL_NONE EGL10.EGL_NONE
) )
val contextAttributes = intArrayOf(12440, EGL10.EGL_PIXMAP_BIT, EGL10.EGL_NONE) val contextAttributes = intArrayOf(12440, EGL10.EGL_PIXMAP_BIT, EGL10.EGL_NONE)

View File

@@ -30,15 +30,16 @@ object NativeDeviceInfoProvider {
fun getNativeDeviceProperties(context: Context, isExport: Boolean = false): Properties { fun getNativeDeviceProperties(context: Context, isExport: Boolean = false): Properties {
val properties = Properties().apply { val properties = Properties().apply {
//Build Props // Build Props
setProperty("UserReadableName", "${Build.MANUFACTURER} ${Build.MODEL}") setProperty("UserReadableName", "${Build.MANUFACTURER} ${Build.MODEL}")
setProperty("Build.HARDWARE", Build.HARDWARE) setProperty("Build.HARDWARE", Build.HARDWARE)
setProperty( setProperty(
"Build.RADIO", "Build.RADIO",
if (Build.getRadioVersion() != null) if (Build.getRadioVersion() != null) {
Build.getRadioVersion() Build.getRadioVersion()
else } else {
"unknown" "unknown"
}
) )
setProperty("Build.FINGERPRINT", Build.FINGERPRINT) setProperty("Build.FINGERPRINT", Build.FINGERPRINT)
setProperty("Build.BRAND", Build.BRAND) setProperty("Build.BRAND", Build.BRAND)
@@ -62,21 +63,24 @@ object NativeDeviceInfoProvider {
"${config.navigation == Configuration.NAVIGATIONHIDDEN_YES}" "${config.navigation == Configuration.NAVIGATIONHIDDEN_YES}"
) )
//Display Metrics // Display Metrics
val metrics = context.resources.displayMetrics val metrics = context.resources.displayMetrics
setProperty("Screen.Density", "${metrics.densityDpi}") setProperty("Screen.Density", "${metrics.densityDpi}")
setProperty("Screen.Width", "${metrics.widthPixels}") setProperty("Screen.Width", "${metrics.widthPixels}")
setProperty("Screen.Height", "${metrics.heightPixels}") setProperty("Screen.Height", "${metrics.heightPixels}")
//Supported Platforms // Supported Platforms
setProperty("Platforms", Build.SUPPORTED_ABIS.joinToString(separator = ",")) setProperty("Platforms", Build.SUPPORTED_ABIS.joinToString(separator = ","))
//Supported Features // Supported Features
setProperty("Features", getFeatures(context).joinToString(separator = ",")) setProperty("Features", getFeatures(context).joinToString(separator = ","))
//Shared Locales // Shared Locales
setProperty("Locales", getLocales(context).joinToString(separator = ",")) setProperty("Locales", getLocales(context).joinToString(separator = ","))
//Shared Libraries // Shared Libraries
setProperty("SharedLibraries", getSharedLibraries(context).joinToString(separator = ",")) setProperty(
//GL Extensions "SharedLibraries",
getSharedLibraries(context).joinToString(separator = ",")
)
// GL Extensions
val activityManager = context.getSystemService<ActivityManager>() val activityManager = context.getSystemService<ActivityManager>()
setProperty( setProperty(
"GL.Version", "GL.Version",
@@ -87,7 +91,7 @@ object NativeDeviceInfoProvider {
EglExtensionProvider.eglExtensions.joinToString(separator = ",") EglExtensionProvider.eglExtensions.joinToString(separator = ",")
) )
//Google Related Props // Google Related Props
setProperty("Client", "android-google") setProperty("Client", "android-google")
val gsfVersionProvider = NativeGsfVersionProvider(context, isExport) val gsfVersionProvider = NativeGsfVersionProvider(context, isExport)
@@ -95,11 +99,11 @@ object NativeDeviceInfoProvider {
setProperty("Vending.version", gsfVersionProvider.vendingVersionCode.toString()) setProperty("Vending.version", gsfVersionProvider.vendingVersionCode.toString())
setProperty("Vending.versionString", gsfVersionProvider.vendingVersionString) setProperty("Vending.versionString", gsfVersionProvider.vendingVersionString)
//MISC // MISC
setProperty("Roaming", "mobile-notroaming") setProperty("Roaming", "mobile-notroaming")
setProperty("TimeZone", "UTC-10") setProperty("TimeZone", "UTC-10")
//Telephony (USA 3650 AT&T) // Telephony (USA 3650 AT&T)
setProperty("CellOperator", "310") setProperty("CellOperator", "310")
setProperty("SimOperator", "38") setProperty("SimOperator", "38")
} }
@@ -108,29 +112,23 @@ object NativeDeviceInfoProvider {
return properties return properties
} }
private fun getFeatures(context: Context): List<String> { private fun getFeatures(context: Context): List<String> = context
return context .packageManager
.packageManager .systemAvailableFeatures
.systemAvailableFeatures .mapNotNull { it.name }
.mapNotNull { it.name }
}
private fun getLocales(context: Context): List<String> { private fun getLocales(context: Context): List<String> = context
return context .assets
.assets .locales
.locales .mapNotNull { it.replace("-", "_") }
.mapNotNull { it.replace("-", "_") }
}
private fun getSharedLibraries(context: Context): List<String> { private fun getSharedLibraries(context: Context): List<String> = context
return context .packageManager
.packageManager .systemSharedLibraryNames
.systemSharedLibraryNames ?.toList() ?: emptyList()
?.toList() ?: emptyList()
}
private fun stripHuaweiProperties(properties: Properties): Properties { private fun stripHuaweiProperties(properties: Properties): Properties {
//Add Pixel 7a properties // Add Pixel 7a properties
properties["Build.HARDWARE"] = "lynx" properties["Build.HARDWARE"] = "lynx"
properties["Build.BOOTLOADER"] = "lynx-1.0-9716681" properties["Build.BOOTLOADER"] = "lynx-1.0-9716681"
properties["Build.BRAND"] = "google" properties["Build.BRAND"] = "google"

View File

@@ -27,12 +27,12 @@ import androidx.core.content.getSystemService
import com.aurora.extensions.isNAndAbove import com.aurora.extensions.isNAndAbove
import com.aurora.store.data.model.NetworkStatus import com.aurora.store.data.model.NetworkStatus
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import javax.inject.Inject
import javax.inject.Singleton
import kotlinx.coroutines.channels.awaitClose import kotlinx.coroutines.channels.awaitClose
import kotlinx.coroutines.flow.Flow import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.callbackFlow import kotlinx.coroutines.flow.callbackFlow
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import javax.inject.Inject
import javax.inject.Singleton
/** /**
* A simple provider with a flow to observe internet connectivity changes * A simple provider with a flow to observe internet connectivity changes

View File

@@ -54,8 +54,8 @@ class PermissionProvider(private val fragment: Fragment) :
/** /**
* Checks whether a known permission has been granted * Checks whether a known permission has been granted
*/ */
fun isGranted(context: Context, permissionType: PermissionType): Boolean { fun isGranted(context: Context, permissionType: PermissionType): Boolean =
return when (permissionType) { when (permissionType) {
PermissionType.EXTERNAL_STORAGE, PermissionType.EXTERNAL_STORAGE,
PermissionType.STORAGE_MANAGER -> { PermissionType.STORAGE_MANAGER -> {
context.isExternalStorageAccessible() context.isExternalStorageAccessible()
@@ -76,9 +76,8 @@ class PermissionProvider(private val fragment: Fragment) :
PermissionType.DOZE_WHITELIST -> context.isIgnoringBatteryOptimizations() PermissionType.DOZE_WHITELIST -> context.isIgnoringBatteryOptimizations()
PermissionType.APP_LINKS -> context.isDomainVerified("play.google.com") && PermissionType.APP_LINKS -> context.isDomainVerified("play.google.com") &&
context.isDomainVerified("market.android.com") context.isDomainVerified("market.android.com")
} }
}
/** /**
* Returns all known permissions that can be requested by Aurora Store * Returns all known permissions that can be requested by Aurora Store
@@ -132,7 +131,9 @@ class PermissionProvider(private val fragment: Fragment) :
Permission( Permission(
type = PermissionType.POST_NOTIFICATIONS, type = PermissionType.POST_NOTIFICATIONS,
title = context.getString(R.string.onboarding_permission_notifications), title = context.getString(R.string.onboarding_permission_notifications),
subtitle = context.getString(R.string.onboarding_permission_notifications_desc), subtitle = context.getString(
R.string.onboarding_permission_notifications_desc
),
optional = true, optional = true,
isGranted = isGranted(context, PermissionType.POST_NOTIFICATIONS) isGranted = isGranted(context, PermissionType.POST_NOTIFICATIONS)
) )
@@ -147,7 +148,7 @@ class PermissionProvider(private val fragment: Fragment) :
subtitle = context.getString(R.string.app_links_desc), subtitle = context.getString(R.string.app_links_desc),
optional = true, optional = true,
isGranted = isGranted(context, PermissionType.APP_LINKS) isGranted = isGranted(context, PermissionType.APP_LINKS)
), )
) )
} }
@@ -195,7 +196,7 @@ class PermissionProvider(private val fragment: Fragment) :
if (!isGranted(context, PermissionType.INSTALL_UNKNOWN_APPS)) { if (!isGranted(context, PermissionType.INSTALL_UNKNOWN_APPS)) {
context.toast(R.string.toast_permission_installer_required) context.toast(R.string.toast_permission_installer_required)
} else { } else {
/** /*
* I don't know why, but for storage manager permission on Android 11 & 12, * I don't know why, but for storage manager permission on Android 11 & 12,
* we need to request both permissions otherwise the permission is not granted, * we need to request both permissions otherwise the permission is not granted,
* even though OS says it is granted. * even though OS says it is granted.
@@ -231,18 +232,16 @@ class PermissionProvider(private val fragment: Fragment) :
} }
@SuppressLint("InlinedApi") @SuppressLint("InlinedApi")
private fun knownPermissions(): Map<PermissionType, Intent> { private fun knownPermissions(): Map<PermissionType, Intent> = mapOf(
return mapOf( PermissionType.STORAGE_MANAGER to PackageUtil.getStorageManagerIntent(context),
PermissionType.STORAGE_MANAGER to PackageUtil.getStorageManagerIntent(context), PermissionType.INSTALL_UNKNOWN_APPS to PackageUtil.getInstallUnknownAppsIntent(),
PermissionType.INSTALL_UNKNOWN_APPS to PackageUtil.getInstallUnknownAppsIntent(), PermissionType.DOZE_WHITELIST to Intent(
PermissionType.DOZE_WHITELIST to Intent( Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS, "package:${BuildConfig.APPLICATION_ID}".toUri()
"package:${BuildConfig.APPLICATION_ID}".toUri() ),
), PermissionType.APP_LINKS to Intent(
PermissionType.APP_LINKS to Intent( ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
ACTION_APP_OPEN_BY_DEFAULT_SETTINGS, "package:${BuildConfig.APPLICATION_ID}".toUri()
"package:${BuildConfig.APPLICATION_ID}".toUri()
)
) )
} )
} }

View File

@@ -137,12 +137,9 @@ open class SpoofDeviceProvider(private val context: Context) {
return File(sourceDir) return File(sourceDir)
} }
} catch (ignored: Exception) { } catch (ignored: Exception) {
} }
return null return null
} }
private fun filenameValid(filename: String): Boolean { private fun filenameValid(filename: String): Boolean = filename.endsWith(SUFFIX)
return filename.endsWith(SUFFIX)
}
} }

View File

@@ -24,11 +24,11 @@ import com.aurora.store.R
import com.aurora.store.util.Preferences import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
import dagger.hilt.android.qualifiers.ApplicationContext import dagger.hilt.android.qualifiers.ApplicationContext
import kotlinx.serialization.json.Json
import java.util.Locale import java.util.Locale
import java.util.Properties import java.util.Properties
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlinx.serialization.json.Json
/** /**
* Provider class to work with device and locale spoofs * Provider class to work with device and locale spoofs
@@ -36,7 +36,7 @@ import javax.inject.Singleton
@Singleton @Singleton
class SpoofProvider @Inject constructor( class SpoofProvider @Inject constructor(
private val json: Json, private val json: Json,
@ApplicationContext val context: Context, @ApplicationContext val context: Context
) : SpoofDeviceProvider(context) { ) : SpoofDeviceProvider(context) {
companion object { companion object {
@@ -119,7 +119,10 @@ class SpoofProvider @Inject constructor(
val versionStrings = resources.getStringArray(R.array.pref_vending_version) val versionStrings = resources.getStringArray(R.array.pref_vending_version)
currentProperties.setProperty("Vending.version", versionCodes[vendingVersionIndex]) currentProperties.setProperty("Vending.version", versionCodes[vendingVersionIndex])
currentProperties.setProperty("Vending.versionString", versionStrings[vendingVersionIndex]) currentProperties.setProperty(
"Vending.versionString",
versionStrings[vendingVersionIndex]
)
} }
} }
} }

View File

@@ -13,9 +13,9 @@ import com.aurora.extensions.TAG
import com.aurora.store.AuroraApp import com.aurora.store.AuroraApp
import com.aurora.store.data.helper.DownloadHelper import com.aurora.store.data.helper.DownloadHelper
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
@AndroidEntryPoint @AndroidEntryPoint
class DownloadCancelReceiver : BroadcastReceiver() { class DownloadCancelReceiver : BroadcastReceiver() {

View File

@@ -75,7 +75,8 @@ class MigrationReceiver : BroadcastReceiver() {
// 63 -> 64 // 63 -> 64
if (currentVersion == 2) { if (currentVersion == 2) {
if (isOAndAbove) { if (isOAndAbove) {
with(context.getSystemService<NotificationManager>()!!) { // !1189 with(context.getSystemService<NotificationManager>()!!) {
// !1189
deleteNotificationChannel("NOTIFICATION_CHANNEL_GENERAL") deleteNotificationChannel("NOTIFICATION_CHANNEL_GENERAL")
deleteNotificationChannel("NOTIFICATION_CHANNEL_ALERT") deleteNotificationChannel("NOTIFICATION_CHANNEL_ALERT")
} }
@@ -87,10 +88,11 @@ class MigrationReceiver : BroadcastReceiver() {
if (currentVersion == 3) { if (currentVersion == 3) {
val updateMode = UpdateMode.entries[ val updateMode = UpdateMode.entries[
Preferences.getInteger( Preferences.getInteger(
context, context,
PREFERENCE_UPDATES_AUTO, PREFERENCE_UPDATES_AUTO,
UpdateMode.DISABLED.ordinal UpdateMode.DISABLED.ordinal
)] )
]
if (updateMode != UpdateMode.DISABLED) { if (updateMode != UpdateMode.DISABLED) {
runCatching { runCatching {

View File

@@ -48,7 +48,7 @@ open class PackageManagerReceiver : BroadcastReceiver() {
} }
} }
//Clear installation queue // Clear installation queue
appInstaller.getPreferredInstaller().removeFromInstallQueue(packageName) appInstaller.getPreferredInstaller().removeFromInstallQueue(packageName)
} }
} }

View File

@@ -15,15 +15,15 @@ import com.aurora.store.data.helper.DownloadHelper
import com.aurora.store.data.providers.AccountProvider import com.aurora.store.data.providers.AccountProvider
import com.aurora.store.util.NotificationUtil import com.aurora.store.util.NotificationUtil
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
import javax.inject.Inject
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import javax.inject.Inject
/** /**
* Triggers re-install/unarchive of a previously archived app on Android 15+ devices. * Triggers re-install/unarchive of a previously archived app on Android 15+ devices.
*/ */
@AndroidEntryPoint @AndroidEntryPoint
class UnarchivePackageReceiver: BroadcastReceiver() { class UnarchivePackageReceiver : BroadcastReceiver() {
@Inject @Inject
lateinit var appDetailsHelper: AppDetailsHelper lateinit var appDetailsHelper: AppDetailsHelper

View File

@@ -35,7 +35,9 @@ object MigrationHelper {
private fun migrateFrom1To2(database: SupportSQLiteDatabase) { private fun migrateFrom1To2(database: SupportSQLiteDatabase) {
database.beginTransaction() database.beginTransaction()
try { try {
database.execSQL("CREATE TABLE `favourite` (`packageName` TEXT NOT NULL, `displayName` TEXT NOT NULL, `iconURL` TEXT NOT NULL, `added` INTEGER NOT NULL, `mode` TEXT NOT NULL, PRIMARY KEY(`packageName`))") database.execSQL(
"CREATE TABLE `favourite` (`packageName` TEXT NOT NULL, `displayName` TEXT NOT NULL, `iconURL` TEXT NOT NULL, `added` INTEGER NOT NULL, `mode` TEXT NOT NULL, PRIMARY KEY(`packageName`))"
)
database.setTransactionSuccessful() database.setTransactionSuccessful()
} catch (exception: Exception) { } catch (exception: Exception) {
Log.e(TAG, "Failed while migrating from database version 1 to 2", exception) Log.e(TAG, "Failed while migrating from database version 1 to 2", exception)
@@ -47,7 +49,9 @@ object MigrationHelper {
private fun migrateFrom2To3(database: SupportSQLiteDatabase) { private fun migrateFrom2To3(database: SupportSQLiteDatabase) {
database.beginTransaction() database.beginTransaction()
try { try {
database.execSQL("CREATE TABLE `update` (`packageName` TEXT NOT NULL, `versionCode` INTEGER NOT NULL, `versionName` TEXT NOT NULL, `displayName` TEXT NOT NULL, `iconURL` TEXT NOT NULL, `changelog` TEXT NOT NULL, `id` INTEGER NOT NULL, `developerName` TEXT NOT NULL, `size` INTEGER NOT NULL, `updatedOn` TEXT NOT NULL, `hasValidCert` INTEGER NOT NULL, `offerType` INTEGER NOT NULL, `fileList` TEXT NOT NULL, `sharedLibs` TEXT NOT NULL, PRIMARY KEY(`packageName`))") database.execSQL(
"CREATE TABLE `update` (`packageName` TEXT NOT NULL, `versionCode` INTEGER NOT NULL, `versionName` TEXT NOT NULL, `displayName` TEXT NOT NULL, `iconURL` TEXT NOT NULL, `changelog` TEXT NOT NULL, `id` INTEGER NOT NULL, `developerName` TEXT NOT NULL, `size` INTEGER NOT NULL, `updatedOn` TEXT NOT NULL, `hasValidCert` INTEGER NOT NULL, `offerType` INTEGER NOT NULL, `fileList` TEXT NOT NULL, `sharedLibs` TEXT NOT NULL, PRIMARY KEY(`packageName`))"
)
database.setTransactionSuccessful() database.setTransactionSuccessful()
} catch (exception: Exception) { } catch (exception: Exception) {
Log.e(TAG, "Failed while migrating from database version 2 to 3", exception) Log.e(TAG, "Failed while migrating from database version 2 to 3", exception)
@@ -63,7 +67,9 @@ object MigrationHelper {
database.beginTransaction() database.beginTransaction()
try { try {
listOf("download", "update").forEach { listOf("download", "update").forEach {
database.execSQL("ALTER TABLE `$it` ADD COLUMN targetSdk INTEGER NOT NULL DEFAULT 1") database.execSQL(
"ALTER TABLE `$it` ADD COLUMN targetSdk INTEGER NOT NULL DEFAULT 1"
)
} }
database.setTransactionSuccessful() database.setTransactionSuccessful()
} catch (exception: Exception) { } catch (exception: Exception) {
@@ -79,7 +85,9 @@ object MigrationHelper {
private fun migrateFrom4To5(database: SupportSQLiteDatabase) { private fun migrateFrom4To5(database: SupportSQLiteDatabase) {
database.beginTransaction() database.beginTransaction()
try { try {
database.execSQL("ALTER TABLE `download` ADD COLUMN downloadedAt INTEGER NOT NULL DEFAULT 0") database.execSQL(
"ALTER TABLE `download` ADD COLUMN downloadedAt INTEGER NOT NULL DEFAULT 0"
)
database.setTransactionSuccessful() database.setTransactionSuccessful()
} catch (exception: Exception) { } catch (exception: Exception) {
Log.e(TAG, "Failed while migrating from database version 4 to 5", exception) Log.e(TAG, "Failed while migrating from database version 4 to 5", exception)

View File

@@ -29,25 +29,25 @@ object RoomModule {
fun providesRoomInstance( fun providesRoomInstance(
@ApplicationContext context: Context, @ApplicationContext context: Context,
downloadConverter: DownloadConverter downloadConverter: DownloadConverter
): AuroraDatabase { ): AuroraDatabase = Room.databaseBuilder(context, AuroraDatabase::class.java, DATABASE)
return Room.databaseBuilder(context, AuroraDatabase::class.java, DATABASE) .addMigrations(
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6) MIGRATION_1_2,
.addTypeConverter(downloadConverter) MIGRATION_2_3,
.build() MIGRATION_3_4,
} MIGRATION_4_5,
MIGRATION_5_6
)
.addTypeConverter(downloadConverter)
.build()
@Provides @Provides
fun providesDownloadDao(auroraDatabase: AuroraDatabase): DownloadDao { fun providesDownloadDao(auroraDatabase: AuroraDatabase): DownloadDao =
return auroraDatabase.downloadDao() auroraDatabase.downloadDao()
}
@Provides @Provides
fun providesFavouriteDao(auroraDatabase: AuroraDatabase): FavouriteDao { fun providesFavouriteDao(auroraDatabase: AuroraDatabase): FavouriteDao =
return auroraDatabase.favouriteDao() auroraDatabase.favouriteDao()
}
@Provides @Provides
fun providesUpdateDao(auroraDatabase: AuroraDatabase): UpdateDao { fun providesUpdateDao(auroraDatabase: AuroraDatabase): UpdateDao = auroraDatabase.updateDao()
return auroraDatabase.updateDao()
}
} }

View File

@@ -12,8 +12,8 @@ import com.aurora.store.data.model.DownloadStatus
import com.aurora.store.data.room.suite.ExternalApk import com.aurora.store.data.room.suite.ExternalApk
import com.aurora.store.data.room.update.Update import com.aurora.store.data.room.update.Update
import com.aurora.store.util.PathUtil import com.aurora.store.util.PathUtil
import kotlinx.parcelize.Parcelize
import java.util.Date import java.util.Date
import kotlinx.parcelize.Parcelize
@Parcelize @Parcelize
@Entity(tableName = "download") @Entity(tableName = "download")
@@ -44,73 +44,67 @@ data class Download(
private val isSuccessful get() = status == DownloadStatus.COMPLETED private val isSuccessful get() = status == DownloadStatus.COMPLETED
companion object { companion object {
fun fromApp(app: App): Download { fun fromApp(app: App): Download = Download(
return Download( app.packageName,
app.packageName, app.versionCode,
app.versionCode, app.offerType,
app.offerType, app.isInstalled,
app.isInstalled, app.displayName,
app.displayName, app.iconArtwork.url,
app.iconArtwork.url, app.size,
app.size, app.id,
app.id, DownloadStatus.QUEUED,
DownloadStatus.QUEUED, 0,
0, 0L,
0L, 0L,
0L, 0,
0, 0,
0, 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()
app.requiresGMS() )
)
}
fun fromUpdate(update: Update): Download { fun fromUpdate(update: Update): Download = Download(
return Download( update.packageName,
update.packageName, update.versionCode,
update.versionCode, update.offerType,
update.offerType, true,
true, update.displayName,
update.displayName, update.iconURL,
update.iconURL, update.size,
update.size, update.id,
update.id, DownloadStatus.QUEUED,
DownloadStatus.QUEUED, 0,
0, 0L,
0L, 0L,
0L, 0,
0, 0,
0, update.fileList,
update.fileList, update.sharedLibs,
update.sharedLibs, update.targetSdk,
update.targetSdk, Date().time
Date().time )
)
}
fun fromExternalApk(externalApk: ExternalApk): Download { fun fromExternalApk(externalApk: ExternalApk): Download = Download(
return Download( packageName = externalApk.packageName,
packageName = externalApk.packageName, versionCode = externalApk.versionCode,
versionCode = externalApk.versionCode, offerType = 0,
offerType = 0, isInstalled = false,
isInstalled = false, displayName = externalApk.displayName,
displayName = externalApk.displayName, iconURL = externalApk.iconURL,
iconURL = externalApk.iconURL, size = 0,
size = 0, id = 0,
id = 0, status = DownloadStatus.QUEUED,
status = DownloadStatus.QUEUED, progress = 0,
progress = 0, speed = 0L,
speed = 0L, timeRemaining = 0L,
timeRemaining = 0L, totalFiles = 1,
totalFiles = 1, downloadedFiles = 0,
downloadedFiles = 0, fileList = externalApk.fileList,
fileList = externalApk.fileList, sharedLibs = emptyList()
sharedLibs = emptyList(), )
)
}
} }
fun canInstall(context: Context): Boolean { fun canInstall(context: Context): Boolean {

View File

@@ -3,31 +3,25 @@ package com.aurora.store.data.room.download
import androidx.room.ProvidedTypeConverter import androidx.room.ProvidedTypeConverter
import androidx.room.TypeConverter import androidx.room.TypeConverter
import com.aurora.gplayapi.data.models.PlayFile import com.aurora.gplayapi.data.models.PlayFile
import kotlinx.serialization.json.Json
import javax.inject.Inject import javax.inject.Inject
import javax.inject.Singleton import javax.inject.Singleton
import kotlinx.serialization.json.Json
@Singleton @Singleton
@ProvidedTypeConverter @ProvidedTypeConverter
class DownloadConverter @Inject constructor(private val json: Json) { class DownloadConverter @Inject constructor(private val json: Json) {
@TypeConverter @TypeConverter
fun toSharedLibList(string: String): List<SharedLib> { fun toSharedLibList(string: String): List<SharedLib> =
return json.decodeFromString<List<SharedLib>>(string) json.decodeFromString<List<SharedLib>>(string)
}
@TypeConverter @TypeConverter
fun fromSharedLibList(list: List<SharedLib>): String { fun fromSharedLibList(list: List<SharedLib>): String = json.encodeToString(list)
return json.encodeToString(list)
}
@TypeConverter @TypeConverter
fun toGPlayFileList(string: String): List<PlayFile> { fun toGPlayFileList(string: String): List<PlayFile> =
return json.decodeFromString<List<PlayFile>>(string) json.decodeFromString<List<PlayFile>>(string)
}
@TypeConverter @TypeConverter
fun fromGPlayFileList(list: List<PlayFile>): String { fun fromGPlayFileList(list: List<PlayFile>): String = json.encodeToString(list)
return json.encodeToString(list)
}
} }

View File

@@ -31,12 +31,7 @@ interface DownloadDao {
WHERE packageName=:packageName WHERE packageName=:packageName
""" """
) )
suspend fun updateProgress( suspend fun updateProgress(packageName: String, progress: Int, speed: Long, timeRemaining: Long)
packageName: String,
progress: Int,
speed: Long,
timeRemaining: Long
)
@Query("SELECT * FROM download") @Query("SELECT * FROM download")
fun downloads(): Flow<List<Download>> fun downloads(): Flow<List<Download>>

View File

@@ -14,12 +14,10 @@ data class SharedLib(
var fileList: List<PlayFile> var fileList: List<PlayFile>
) : Parcelable { ) : Parcelable {
companion object { companion object {
fun fromApp(app: App): SharedLib { fun fromApp(app: App): SharedLib = SharedLib(
return SharedLib( app.packageName,
app.packageName, app.versionCode,
app.versionCode, app.fileList.filterNot { it.url.isBlank() }
app.fileList.filterNot { it.url.isBlank() } )
)
}
} }
} }

View File

@@ -21,23 +21,19 @@ data class Favourite(
) : Parcelable { ) : Parcelable {
companion object { companion object {
fun fromApp(app: App, mode: Mode): Favourite { fun fromApp(app: App, mode: Mode): Favourite = Favourite(
return Favourite( packageName = app.packageName,
packageName = app.packageName, displayName = app.displayName,
displayName = app.displayName, iconURL = app.iconArtwork.url,
iconURL = app.iconArtwork.url, added = System.currentTimeMillis(),
added = System.currentTimeMillis(), mode = mode
mode = mode )
)
}
fun Favourite.toApp(): App { fun Favourite.toApp(): App = App(
return App( packageName = packageName,
packageName = packageName, displayName = displayName,
displayName = displayName, iconArtwork = Artwork(url = iconURL)
iconArtwork = Artwork(url = iconURL) )
)
}
} }
enum class Mode { enum class Mode {

View File

@@ -6,5 +6,5 @@ import kotlinx.serialization.Serializable
@Serializable @Serializable
data class ImportExport( data class ImportExport(
val favourites: List<Favourite>, val favourites: List<Favourite>,
val auroraStoreVersion: Int = BuildConfig.VERSION_CODE, val auroraStoreVersion: Int = BuildConfig.VERSION_CODE
) )

View File

@@ -21,7 +21,5 @@ data class ExternalApk(
var fileList: List<PlayFile> var fileList: List<PlayFile>
) : Parcelable { ) : Parcelable {
fun isInstalled(context: Context): Boolean { fun isInstalled(context: Context): Boolean = PackageUtil.isInstalled(context, packageName)
return PackageUtil.isInstalled(context, packageName)
}
} }

View File

@@ -33,40 +33,34 @@ data class Update(
) : Parcelable { ) : Parcelable {
companion object { companion object {
fun fromApp(context: Context, app: App): Update { fun fromApp(context: Context, app: App): Update = Update(
return Update( app.packageName,
app.packageName, app.versionCode,
app.versionCode, app.versionName,
app.versionName, app.displayName,
app.displayName, app.iconArtwork.url,
app.iconArtwork.url, app.changes,
app.changes, app.id,
app.id, app.developerName,
app.developerName, app.size,
app.size, app.updatedOn,
app.updatedOn, app.certificateSetList.any {
app.certificateSetList.any { it.certificateSet in CertUtil.getEncodedCertificateHashes(
it.certificateSet in CertUtil.getEncodedCertificateHashes( context,
context, app.packageName app.packageName
) )
}, },
app.offerType, app.offerType,
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
) )
}
} }
fun isSelfUpdate(context: Context): Boolean { fun isSelfUpdate(context: Context): Boolean = packageName == context.packageName
return packageName == context.packageName
}
fun isInstalled(context: Context): Boolean { fun isInstalled(context: Context): Boolean = PackageUtil.isInstalled(context, packageName)
return PackageUtil.isInstalled(context, packageName)
}
fun isUpToDate(context: Context): Boolean { fun isUpToDate(context: Context): Boolean =
return PackageUtil.isInstalled(context, packageName, versionCode) PackageUtil.isInstalled(context, packageName, versionCode)
}
} }

View File

@@ -100,8 +100,8 @@ open class AuthWorker @AssistedInject constructor(
} }
} }
private suspend fun fetchAuthToken(email: String, oldToken: String? = null): String { private suspend fun fetchAuthToken(email: String, oldToken: String? = null): String =
return suspendCoroutine { continuation -> suspendCoroutine { continuation ->
fetchAuthToken(email, oldToken) { future -> fetchAuthToken(email, oldToken) { future ->
try { try {
val bundle = future.result val bundle = future.result
@@ -110,14 +110,15 @@ open class AuthWorker @AssistedInject constructor(
if (token != null) { if (token != null) {
continuation.resume(token) continuation.resume(token)
} else { } else {
continuation.resumeWithException(IllegalStateException("Auth token is null")) continuation.resumeWithException(
IllegalStateException("Auth token is null")
)
} }
} catch (e: Exception) { } catch (e: Exception) {
continuation.resumeWithException(e) continuation.resumeWithException(e)
} }
} }
} }
}
private fun fetchAuthToken( private fun fetchAuthToken(
email: String, email: String,
@@ -155,8 +156,8 @@ open class AuthWorker @AssistedInject constructor(
} }
} }
private fun verifyAndSaveAuth(authData: AuthData, accountType: AccountType): AuthData? { private fun verifyAndSaveAuth(authData: AuthData, accountType: AccountType): AuthData? =
return if (authData.authToken.isNotEmpty() && authData.deviceConfigToken.isNotEmpty()) { if (authData.authToken.isNotEmpty() && authData.deviceConfigToken.isNotEmpty()) {
authProvider.saveAuthData(authData) authProvider.saveAuthData(authData)
AccountProvider.login( AccountProvider.login(
context, context,
@@ -171,5 +172,4 @@ open class AuthWorker @AssistedInject constructor(
AccountProvider.logout(context) AccountProvider.logout(context)
null null
} }
}
} }

View File

@@ -58,19 +58,23 @@ class CacheWorker @AssistedInject constructor(
override suspend fun doWork(): Result { override suspend fun doWork(): Result {
Log.i(TAG, "Cleaning cache") Log.i(TAG, "Cleaning cache")
PathUtil.getOldDownloadDirectories(context).filter { it.exists() }.forEach { dir -> // Downloads PathUtil.getOldDownloadDirectories(context).filter { it.exists() }.forEach { dir ->
// Downloads
Log.i(TAG, "Deleting old unused download directory: $dir") Log.i(TAG, "Deleting old unused download directory: $dir")
dir.deleteRecursively() dir.deleteRecursively()
} }
PathUtil.getDownloadDirectory(context).listFiles()?.forEach { download -> // com.example.app PathUtil.getDownloadDirectory(context).listFiles()?.forEach { download ->
// com.example.app
// Delete if the download directory is empty // Delete if the download directory is empty
if (download.listFiles().isNullOrEmpty()) { if (download.listFiles().isNullOrEmpty()) {
Log.i(TAG, "Removing empty download directory for ${download.name}") Log.i(TAG, "Removing empty download directory for ${download.name}")
download.deleteRecursively(); return@forEach download.deleteRecursively()
return@forEach
} }
download.listFiles()!!.forEach { versionCode -> // 20240325 download.listFiles()!!.forEach { versionCode ->
// 20240325
if (versionCode.listFiles().isNullOrEmpty()) { if (versionCode.listFiles().isNullOrEmpty()) {
// Purge empty non-accessible directory // Purge empty non-accessible directory
Log.i(TAG, "Removing empty directory for ${download.name}, ${versionCode.name}") Log.i(TAG, "Removing empty directory for ${download.name}, ${versionCode.name}")

View File

@@ -48,9 +48,6 @@ 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
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.io.FileOutputStream import java.io.FileOutputStream
import java.net.SocketException import java.net.SocketException
@@ -60,6 +57,9 @@ import java.security.DigestInputStream
import java.security.MessageDigest import java.security.MessageDigest
import kotlin.coroutines.cancellation.CancellationException import kotlin.coroutines.cancellation.CancellationException
import kotlin.properties.Delegates import kotlin.properties.Delegates
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.withContext
/** /**
* An expedited long-running worker to download and trigger installation for given apps. * An expedited long-running worker to download and trigger installation for given apps.
@@ -206,10 +206,13 @@ 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 {
if (BuildConfig.FLAVOR == FLAVOUR_HUAWEI && download.requiresGMS && PackageUtil.hasMicroGCompanion(context)) if (BuildConfig.FLAVOR == FLAVOUR_HUAWEI && download.requiresGMS &&
PackageUtil.hasMicroGCompanion(context)
) {
appInstaller.getMicroGInstaller().install(download) appInstaller.getMicroGInstaller().install(download)
else } else {
appInstaller.getPreferredInstaller().install(download) 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)
@@ -425,7 +428,10 @@ class DownloadWorker @AssistedInject constructor(
} }
val notification = NotificationUtil.getDownloadNotification( val notification = NotificationUtil.getDownloadNotification(
context, download, icon, exception?.message context,
download,
icon,
exception?.message
) )
notificationManager.notify( notificationManager.notify(
if (isProgress) NOTIFICATION_ID else download.packageName.hashCode(), if (isProgress) NOTIFICATION_ID else download.packageName.hashCode(),
@@ -453,7 +459,10 @@ class DownloadWorker @AssistedInject constructor(
file.inputStream().use { fis -> file.inputStream().use { fis ->
DigestInputStream(fis, messageDigest).use { dis -> DigestInputStream(fis, messageDigest).use { dis ->
val buffer = ByteArray(DEFAULT_BUFFER_SIZE) val buffer = ByteArray(DEFAULT_BUFFER_SIZE)
while (dis.read(buffer) != -1) { /* Just read, digest updates automatically */ while (dis.read(buffer) !=
-1
) {
/* Just read, digest updates automatically */
} }
} }
} }

View File

@@ -104,6 +104,7 @@ class ExportWorker @AssistedInject constructor(
notificationManager = context.getSystemService<NotificationManager>()!! notificationManager = context.getSystemService<NotificationManager>()!!
@Suppress("ktlint:standard:mixed-condition-operators")
if (packageName.isNullOrEmpty() || isDownload && versionCode == -1L) { if (packageName.isNullOrEmpty() || isDownload && versionCode == -1L) {
Log.e(TAG, "Input data is corrupt, bailing out!") Log.e(TAG, "Input data is corrupt, bailing out!")
notifyStatus(displayName ?: String(), uri, false) notifyStatus(displayName ?: String(), uri, false)
@@ -126,12 +127,10 @@ class ExportWorker @AssistedInject constructor(
return Result.success() return Result.success()
} }
override suspend fun getForegroundInfo(): ForegroundInfo { override suspend fun getForegroundInfo(): ForegroundInfo = ForegroundInfo(
return ForegroundInfo( NOTIFICATION_ID_FGS,
NOTIFICATION_ID_FGS, NotificationUtil.getExportNotification(context)
NotificationUtil.getExportNotification(context) )
)
}
private fun notifyStatus(packageName: String, uri: Uri, success: Boolean = true) { private fun notifyStatus(packageName: String, uri: Uri, success: Boolean = true) {
notificationManager.notify( notificationManager.notify(
@@ -157,12 +156,10 @@ class ExportWorker @AssistedInject constructor(
bundleAllAPKs(fileList.filterNotNull(), uri) bundleAllAPKs(fileList.filterNotNull(), uri)
} }
private fun copyDownloadedApp(packageName: String, versionCode: Long, uri: Uri) { private fun copyDownloadedApp(packageName: String, versionCode: Long, uri: Uri) = bundleAllAPKs(
return bundleAllAPKs( PathUtil.getAppDownloadDir(context, packageName, versionCode).listFiles()!!.toList(),
PathUtil.getAppDownloadDir(context, packageName, versionCode).listFiles()!!.toList(), uri
uri )
)
}
/** /**
* Bundles all the given APKs to a zip file * Bundles all the given APKs to a zip file

View File

@@ -32,10 +32,10 @@ import com.aurora.store.util.Preferences
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_AUTO import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_AUTO
import dagger.assisted.Assisted import dagger.assisted.Assisted
import dagger.assisted.AssistedInject import dagger.assisted.AssistedInject
import java.util.Locale
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext import kotlinx.coroutines.withContext
import kotlinx.serialization.json.Json import kotlinx.serialization.json.Json
import java.util.Locale
/** /**
* A worker to check for updates for installed apps based on saved authentication data, * A worker to check for updates for installed apps based on saved authentication data,
@@ -61,8 +61,8 @@ class UpdateWorker @AssistedInject constructor(
private val notificationID = 100 private val notificationID = 100
private val canSelfUpdate = !CertUtil.isFDroidApp(context, BuildConfig.APPLICATION_ID) && private val canSelfUpdate = !CertUtil.isFDroidApp(context, BuildConfig.APPLICATION_ID) &&
!CertUtil.isAppGalleryApp(context, BuildConfig.APPLICATION_ID) && !CertUtil.isAppGalleryApp(context, BuildConfig.APPLICATION_ID) &&
BuildType.CURRENT != BuildType.DEBUG BuildType.CURRENT != BuildType.DEBUG
private val isAuroraOnlyFilterEnabled: Boolean private val isAuroraOnlyFilterEnabled: Boolean
get() = Preferences.getBoolean(context, Preferences.PREFERENCE_FILTER_AURORA_ONLY, false) get() = Preferences.getBoolean(context, Preferences.PREFERENCE_FILTER_AURORA_ONLY, false)
@@ -77,14 +77,16 @@ class UpdateWorker @AssistedInject constructor(
super.doWork() super.doWork()
Log.i(TAG, "Checking for app updates") Log.i(TAG, "Checking for app updates")
val updateMode = UpdateMode.entries[inputData.getInt( val updateMode = UpdateMode.entries[
UpdateHelper.UPDATE_MODE, inputData.getInt(
Preferences.getInteger( UpdateHelper.UPDATE_MODE,
context, Preferences.getInteger(
PREFERENCE_UPDATES_AUTO, context,
UpdateMode.CHECK_AND_INSTALL.ordinal PREFERENCE_UPDATES_AUTO,
UpdateMode.CHECK_AND_INSTALL.ordinal
)
) )
)] ]
if (updateMode == UpdateMode.DISABLED || !AccountProvider.isLoggedIn(context)) { if (updateMode == UpdateMode.DISABLED || !AccountProvider.isLoggedIn(context)) {
Log.i(TAG, "Updates are disabled, bailing out!") Log.i(TAG, "Updates are disabled, bailing out!")
@@ -107,7 +109,9 @@ class UpdateWorker @AssistedInject constructor(
} }
// Notify and exit if we are only checking for updates or if battery optimizations are enabled // Notify and exit if we are only checking for updates or if battery optimizations are enabled
if (updateMode == UpdateMode.CHECK_AND_NOTIFY || !context.isIgnoringBatteryOptimizations()) { if (updateMode == UpdateMode.CHECK_AND_NOTIFY ||
!context.isIgnoringBatteryOptimizations()
) {
Log.i(TAG, "Found ${updates.size} updates, notifying!") Log.i(TAG, "Found ${updates.size} updates, notifying!")
notifyUpdates(updates) notifyUpdates(updates)
return Result.success() return Result.success()
@@ -123,7 +127,10 @@ class UpdateWorker @AssistedInject constructor(
// Notify about apps that cannot be auto-updated // Notify about apps that cannot be auto-updated
if (filteredUpdates.second.isNotEmpty()) { if (filteredUpdates.second.isNotEmpty()) {
Log.i(TAG, "Found ${updates.size} updates out of which ${filteredUpdates.second.size} cannot be auto-updated") Log.i(
TAG,
"Found ${updates.size} updates out of which ${filteredUpdates.second.size} cannot be auto-updated"
)
notifyUpdates(filteredUpdates.second) notifyUpdates(filteredUpdates.second)
} }
@@ -137,12 +144,10 @@ class UpdateWorker @AssistedInject constructor(
} }
} }
override suspend fun getForegroundInfo(): ForegroundInfo { override suspend fun getForegroundInfo(): ForegroundInfo = ForegroundInfo(
return ForegroundInfo( notificationID,
notificationID, NotificationUtil.getUpdateNotification(context)
NotificationUtil.getUpdateNotification(context) )
)
}
/** /**
* Checks and returns updates for all possible apps * Checks and returns updates for all possible apps
@@ -157,12 +162,18 @@ class UpdateWorker @AssistedInject constructor(
val filteredPackages = if (isAuroraOnlyFilterEnabled) { val filteredPackages = if (isAuroraOnlyFilterEnabled) {
packages.filter { CertUtil.isAuroraStoreApp(context, it.packageName) } packages.filter { CertUtil.isAuroraStoreApp(context, it.packageName) }
} else { } else {
packages.filterNot { if (isFDroidFilterEnabled) CertUtil.isFDroidApp(context, it.packageName) else false } packages.filterNot {
} if (isFDroidFilterEnabled) {
CertUtil.isFDroidApp(context, it.packageName)
} else {
false
}
}
}.map { it.packageName }
val updates = appDetailsHelper.getAppByPackageName(filteredPackages.map { it.packageName }) val updates = appDetailsHelper.getAppByPackageName(filteredPackages)
.filter { it.displayName.isNotEmpty() } .filter { it.displayName.isNotEmpty() }
.filter { PackageUtil.isUpdatable(context, it.packageName, it.versionCode.toLong()) } .filter { PackageUtil.isUpdatable(context, it.packageName, it.versionCode) }
.toMutableList() .toMutableList()
if (canSelfUpdate) getSelfUpdate()?.let { updates.add(it) } if (canSelfUpdate) getSelfUpdate()?.let { updates.add(it) }
@@ -179,7 +190,9 @@ class UpdateWorker @AssistedInject constructor(
return withContext(Dispatchers.IO) { return withContext(Dispatchers.IO) {
val updateUrl = when (BuildType.CURRENT) { val updateUrl = when (BuildType.CURRENT) {
BuildType.RELEASE -> Constants.UPDATE_URL_STABLE BuildType.RELEASE -> Constants.UPDATE_URL_STABLE
BuildType.NIGHTLY -> Constants.UPDATE_URL_NIGHTLY BuildType.NIGHTLY -> Constants.UPDATE_URL_NIGHTLY
else -> { else -> {
Log.i(TAG, "Self-updates are not available for this build!") Log.i(TAG, "Self-updates are not available for this build!")
return@withContext null return@withContext null
@@ -193,6 +206,7 @@ class UpdateWorker @AssistedInject constructor(
val isUpdate = when (BuildType.CURRENT) { val isUpdate = when (BuildType.CURRENT) {
BuildType.NIGHTLY, BuildType.NIGHTLY,
BuildType.RELEASE -> selfUpdate.versionCode > BuildConfig.VERSION_CODE BuildType.RELEASE -> selfUpdate.versionCode > BuildConfig.VERSION_CODE
else -> false else -> false
} }