ktlint: Reformat data directory
Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -44,6 +44,6 @@ open class InstallerEvent(open val packageName: String) : Event() {
|
||||
data class Failed(
|
||||
override val packageName: String,
|
||||
val error: String? = null,
|
||||
val extra: String? = null,
|
||||
val extra: String? = null
|
||||
) : InstallerEvent(packageName)
|
||||
}
|
||||
|
||||
@@ -2,9 +2,9 @@ package com.aurora.store.data.event
|
||||
|
||||
import android.util.Log
|
||||
import com.aurora.extensions.TAG
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||
import kotlinx.coroutines.flow.asSharedFlow
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class EventFlow {
|
||||
|
||||
@@ -18,13 +18,13 @@ import com.aurora.store.data.room.update.Update
|
||||
import com.aurora.store.data.work.DownloadWorker
|
||||
import com.aurora.store.util.PathUtil
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.flow.SharingStarted
|
||||
import kotlinx.coroutines.flow.firstOrNull
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Helper class to work with the [DownloadWorker].
|
||||
|
||||
@@ -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.PREFERENCE_UPDATES_CHECK_INTERVAL
|
||||
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.firstOrNull
|
||||
import kotlinx.coroutines.flow.launchIn
|
||||
@@ -32,10 +36,6 @@ import kotlinx.coroutines.flow.map
|
||||
import kotlinx.coroutines.flow.onEach
|
||||
import kotlinx.coroutines.flow.stateIn
|
||||
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].
|
||||
|
||||
@@ -31,6 +31,7 @@ import com.aurora.extensions.isOAndAbove
|
||||
import com.aurora.extensions.isPAndAbove
|
||||
import com.aurora.extensions.isSAndAbove
|
||||
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.model.Installer
|
||||
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.topjohnwu.superuser.Shell
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import rikka.shizuku.Shizuku
|
||||
import rikka.sui.Sui
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import rikka.shizuku.Shizuku
|
||||
import rikka.sui.Sui
|
||||
|
||||
@Singleton
|
||||
class AppInstaller @Inject constructor(
|
||||
@@ -58,27 +59,28 @@ class AppInstaller @Inject constructor(
|
||||
) {
|
||||
|
||||
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_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"
|
||||
const val EXTRA_PACKAGE_NAME =
|
||||
"com.aurora.store.data.installer.AppInstaller.EXTRA_PACKAGE_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 {
|
||||
return Installer.entries[Preferences.getInteger(context, PREFERENCE_INSTALLER_ID)]
|
||||
}
|
||||
fun getCurrentInstaller(context: Context): Installer =
|
||||
Installer.entries[Preferences.getInteger(context, PREFERENCE_INSTALLER_ID)]
|
||||
|
||||
fun getAvailableInstallersInfo(context: Context): List<InstallerInfo> {
|
||||
return listOfNotNull(
|
||||
SessionInstaller.installerInfo,
|
||||
NativeInstaller.installerInfo,
|
||||
if (hasRootAccess()) RootInstaller.installerInfo else null,
|
||||
if (hasAuroraService(context)) ServiceInstaller.installerInfo else null,
|
||||
if (hasAppManager(context)) AMInstaller.installerInfo else null,
|
||||
if (hasShizukuOrSui(context)) ShizukuInstaller.installerInfo else null,
|
||||
if (hasMicroGCompanion(context)) MicroGInstaller.installerInfo else null
|
||||
)
|
||||
}
|
||||
fun getAvailableInstallersInfo(context: Context): List<InstallerInfo> = listOfNotNull(
|
||||
SessionInstaller.installerInfo,
|
||||
NativeInstaller.installerInfo,
|
||||
if (hasRootAccess()) RootInstaller.installerInfo else null,
|
||||
if (hasAuroraService(context)) ServiceInstaller.installerInfo else null,
|
||||
if (hasAppManager(context)) AMInstaller.installerInfo else null,
|
||||
if (hasShizukuOrSui(context)) ShizukuInstaller.installerInfo else null,
|
||||
if (hasMicroGCompanion(context)) MicroGInstaller.installerInfo else null
|
||||
)
|
||||
|
||||
/**
|
||||
* 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
|
||||
|
||||
// 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
|
||||
when (Build.VERSION.SDK_INT) {
|
||||
Build.VERSION_CODES.BAKLAVA -> targetSdk >= Build.VERSION_CODES.UPSIDE_DOWN_CAKE
|
||||
Build.VERSION_CODES.VANILLA_ICE_CREAM -> targetSdk >= Build.VERSION_CODES.TIRAMISU
|
||||
Build.VERSION_CODES.BAKLAVA -> {
|
||||
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.TIRAMISU -> targetSdk >= Build.VERSION_CODES.R
|
||||
|
||||
Build.VERSION_CODES.S -> targetSdk >= Build.VERSION_CODES.Q
|
||||
|
||||
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.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.MICROG -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun hasRootAccess(): Boolean {
|
||||
return Shell.getShell().isRoot
|
||||
}
|
||||
fun hasRootAccess(): Boolean = Shell.getShell().isRoot
|
||||
|
||||
fun hasAuroraService(context: Context): Boolean {
|
||||
return try {
|
||||
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(
|
||||
fun hasAuroraService(context: Context): Boolean = try {
|
||||
val packageInfo = PackageUtil.getPackageInfo(
|
||||
context,
|
||||
ShizukuInstaller.SHIZUKU_PACKAGE_NAME
|
||||
) || Sui.isSui())
|
||||
ServiceInstaller.PRIVILEGED_EXTENSION_PACKAGE_NAME
|
||||
)
|
||||
val version = PackageInfoCompat.getLongVersionCode(packageInfo)
|
||||
|
||||
packageInfo.applicationInfo!!.enabled && version >= 9
|
||||
} catch (_: Exception) {
|
||||
false
|
||||
}
|
||||
|
||||
fun hasShizukuPerm(): Boolean {
|
||||
return Shizuku.checkSelfPermission() == PackageManager.PERMISSION_GRANTED
|
||||
}
|
||||
fun hasAppManager(context: Context): Boolean =
|
||||
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) {
|
||||
val intent = Intent().apply {
|
||||
@@ -166,25 +179,33 @@ class AppInstaller @Inject constructor(
|
||||
private val defaultInstaller: IInstaller
|
||||
get() = sessionInstaller
|
||||
|
||||
fun getMicroGInstaller(): IInstaller {
|
||||
return microGInstaller
|
||||
}
|
||||
fun getMicroGInstaller(): IInstaller = microGInstaller
|
||||
|
||||
fun getPreferredInstaller(): IInstaller {
|
||||
return when (getCurrentInstaller(context)) {
|
||||
Installer.SESSION -> sessionInstaller
|
||||
Installer.NATIVE -> nativeInstaller
|
||||
Installer.ROOT -> if (hasRootAccess()) rootInstaller else defaultInstaller
|
||||
Installer.SERVICE -> if (hasAuroraService(context)) serviceInstaller else defaultInstaller
|
||||
Installer.AM -> if (hasAppManager(context)) amInstaller else defaultInstaller
|
||||
Installer.SHIZUKU -> {
|
||||
if (hasShizukuOrSui(context) && hasShizukuPerm()) {
|
||||
shizukuInstaller
|
||||
} else {
|
||||
defaultInstaller
|
||||
}
|
||||
}
|
||||
Installer.MICROG -> if(hasMicroGCompanion(context)) microGInstaller else defaultInstaller
|
||||
fun getPreferredInstaller(): IInstaller = when (getCurrentInstaller(context)) {
|
||||
Installer.SESSION -> sessionInstaller
|
||||
|
||||
Installer.NATIVE -> nativeInstaller
|
||||
|
||||
Installer.ROOT -> if (hasRootAccess()) rootInstaller else defaultInstaller
|
||||
|
||||
Installer.SERVICE -> if (hasAuroraService(context)) {
|
||||
serviceInstaller
|
||||
} else {
|
||||
defaultInstaller
|
||||
}
|
||||
|
||||
Installer.AM -> if (hasAppManager(context)) amInstaller else defaultInstaller
|
||||
|
||||
Installer.SHIZUKU -> if (hasShizukuOrSui(context) && hasShizukuPerm()) {
|
||||
shizukuInstaller
|
||||
} else {
|
||||
defaultInstaller
|
||||
}
|
||||
|
||||
Installer.MICROG -> if (hasMicroGCompanion(context)) {
|
||||
microGInstaller
|
||||
} else {
|
||||
defaultInstaller
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -53,13 +53,12 @@ class MicroGInstaller @Inject constructor(
|
||||
description = R.string.microg_installer_desc
|
||||
)
|
||||
|
||||
fun buildMicroGInstallIntent(uris: ArrayList<Uri>): Intent {
|
||||
return Intent("org.microg.vending.action.INSTALL_PACKAGE").apply {
|
||||
fun buildMicroGInstallIntent(uris: ArrayList<Uri>): Intent =
|
||||
Intent("org.microg.vending.action.INSTALL_PACKAGE").apply {
|
||||
setPackage(PACKAGE_NAME_PLAY_STORE)
|
||||
setType("application/vnd.android.package-archive")
|
||||
putExtra(Intent.EXTRA_STREAM, uris)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun install(download: Download) {
|
||||
|
||||
@@ -75,7 +75,10 @@ class RootInstaller @Inject constructor(
|
||||
context.getString(R.string.installer_status_failure),
|
||||
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 = "") {
|
||||
var totalSize = 0
|
||||
|
||||
for (file in getFiles(packageName, versionCode, sharedLibPkgName))
|
||||
for (file in getFiles(packageName, versionCode, sharedLibPkgName)) {
|
||||
totalSize += file.length().toInt()
|
||||
}
|
||||
|
||||
val result: Shell.Result =
|
||||
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()
|
||||
if (Shell.getShell().isRoot && sessionId != null) {
|
||||
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()
|
||||
}
|
||||
|
||||
@@ -136,7 +142,5 @@ class RootInstaller @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun parseError(result: Shell.Result): String {
|
||||
return result.err.joinToString(separator = "\n")
|
||||
}
|
||||
private fun parseError(result: Shell.Result): String = result.err.joinToString(separator = "\n")
|
||||
}
|
||||
|
||||
@@ -216,8 +216,8 @@ class SessionInstaller @Inject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildSessionParams(packageName: String): SessionParams {
|
||||
return SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
|
||||
private fun buildSessionParams(packageName: String): SessionParams =
|
||||
SessionParams(SessionParams.MODE_FULL_INSTALL).apply {
|
||||
setAppPackageName(packageName)
|
||||
setInstallLocation(PackageInfo.INSTALL_LOCATION_AUTO)
|
||||
if (isNAndAbove) {
|
||||
@@ -238,7 +238,6 @@ class SessionInstaller @Inject constructor(
|
||||
setApplicationEnabledSettingPersistent()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun commitInstall(sessionInfo: SessionInfo) {
|
||||
try {
|
||||
|
||||
@@ -51,10 +51,10 @@ import com.aurora.store.data.room.download.Download
|
||||
import com.aurora.store.util.PackageUtil.isSharedLibraryInstalled
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import dev.rikka.tools.refine.Refine
|
||||
import rikka.shizuku.ShizukuBinderWrapper
|
||||
import rikka.shizuku.SystemServiceHelper
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import rikka.shizuku.ShizukuBinderWrapper
|
||||
import rikka.shizuku.SystemServiceHelper
|
||||
|
||||
@Singleton
|
||||
@RequiresApi(Build.VERSION_CODES.O)
|
||||
@@ -99,7 +99,9 @@ class ShizukuInstaller @Inject constructor(
|
||||
Refine.unsafeCast<PackageInstaller>(
|
||||
PackageInstallerHidden(iPackageInstaller, PLAY_PACKAGE_NAME, 0)
|
||||
)
|
||||
} else null
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
override fun install(download: Download) {
|
||||
|
||||
@@ -43,20 +43,40 @@ abstract class InstallerBase(private val context: Context) : IInstaller {
|
||||
companion object {
|
||||
fun notifyInstallation(context: Context, displayName: String, packageName: String) {
|
||||
val notificationManager = context.getSystemService<NotificationManager>()
|
||||
val notification = NotificationUtil.getInstallNotification(context, displayName, packageName)
|
||||
val notification = NotificationUtil.getInstallNotification(
|
||||
context,
|
||||
displayName,
|
||||
packageName
|
||||
)
|
||||
notificationManager!!.notify(packageName.hashCode(), notification)
|
||||
}
|
||||
|
||||
fun getErrorString(context: Context, status: Int): String {
|
||||
return when (status) {
|
||||
PackageInstaller.STATUS_FAILURE_ABORTED -> context.getString(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_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)
|
||||
}
|
||||
fun getErrorString(context: Context, status: Int): String = when (status) {
|
||||
PackageInstaller.STATUS_FAILURE_ABORTED -> context.getString(
|
||||
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_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()
|
||||
}
|
||||
|
||||
override fun isAlreadyQueued(packageName: String): Boolean {
|
||||
return AuroraApp.enqueuedInstalls.contains(packageName)
|
||||
}
|
||||
override fun isAlreadyQueued(packageName: String): Boolean =
|
||||
AuroraApp.enqueuedInstalls.contains(packageName)
|
||||
|
||||
override fun removeFromInstallQueue(packageName: String) {
|
||||
AuroraApp.enqueuedInstalls.remove(packageName)
|
||||
@@ -113,11 +132,9 @@ abstract class InstallerBase(private val context: Context) : IInstaller {
|
||||
return downloadDir.listFiles()!!.filter { it.path.endsWith(".apk") }
|
||||
}
|
||||
|
||||
fun getUri(file: File): Uri {
|
||||
return FileProvider.getUriForFile(
|
||||
context,
|
||||
"${BuildConfig.APPLICATION_ID}.fileProvider",
|
||||
file
|
||||
)
|
||||
}
|
||||
fun getUri(file: File): Uri = FileProvider.getUriForFile(
|
||||
context,
|
||||
"${BuildConfig.APPLICATION_ID}.fileProvider",
|
||||
file
|
||||
)
|
||||
}
|
||||
|
||||
@@ -27,14 +27,10 @@ data class Black(val packageName: String) {
|
||||
var versionName: String = String()
|
||||
var versionCode: Long = 0
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return packageName.hashCode()
|
||||
}
|
||||
override fun hashCode(): Int = packageName.hashCode()
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return when (other) {
|
||||
is Black -> other.packageName == packageName
|
||||
else -> false
|
||||
}
|
||||
override fun equals(other: Any?): Boolean = when (other) {
|
||||
is Black -> other.packageName == packageName
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -55,14 +55,10 @@ data class ExodusTracker(
|
||||
val categories: List<String> = emptyList()
|
||||
) {
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return id
|
||||
}
|
||||
override fun hashCode(): Int = id
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return when (other) {
|
||||
is ExodusTracker -> other.id == id
|
||||
else -> false
|
||||
}
|
||||
override fun equals(other: Any?): Boolean = when (other) {
|
||||
is ExodusTracker -> other.id == id
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,14 +33,10 @@ data class InstallerInfo(
|
||||
@StringRes val subtitle: Int,
|
||||
@StringRes val description: Int
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return when (other) {
|
||||
is InstallerInfo -> other.id == id
|
||||
else -> false
|
||||
}
|
||||
override fun equals(other: Any?): Boolean = when (other) {
|
||||
is InstallerInfo -> other.id == id
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return id.hashCode()
|
||||
}
|
||||
override fun hashCode(): Int = id.hashCode()
|
||||
}
|
||||
|
||||
@@ -24,16 +24,12 @@ data class Link(
|
||||
var title: String,
|
||||
var subtitle: String,
|
||||
var url: String,
|
||||
var icon: Int,
|
||||
var icon: Int
|
||||
) {
|
||||
override fun equals(other: Any?): Boolean {
|
||||
return when (other) {
|
||||
is Link -> other.id == id
|
||||
else -> false
|
||||
}
|
||||
override fun equals(other: Any?): Boolean = when (other) {
|
||||
is Link -> other.id == id
|
||||
else -> false
|
||||
}
|
||||
|
||||
override fun hashCode(): Int {
|
||||
return id.hashCode()
|
||||
}
|
||||
override fun hashCode(): Int = id.hashCode()
|
||||
}
|
||||
|
||||
@@ -19,23 +19,18 @@ data class MinimalApp(
|
||||
|
||||
companion object {
|
||||
|
||||
fun fromApp(app: App): MinimalApp {
|
||||
return MinimalApp(
|
||||
app.packageName,
|
||||
app.versionName,
|
||||
app.versionCode,
|
||||
app.displayName
|
||||
)
|
||||
}
|
||||
|
||||
fun fromUpdate(update: Update): MinimalApp {
|
||||
return MinimalApp(
|
||||
update.packageName,
|
||||
update.versionName,
|
||||
update.versionCode,
|
||||
update.displayName
|
||||
)
|
||||
}
|
||||
fun fromApp(app: App): MinimalApp = MinimalApp(
|
||||
app.packageName,
|
||||
app.versionName,
|
||||
app.versionCode,
|
||||
app.displayName
|
||||
)
|
||||
|
||||
fun fromUpdate(update: Update): MinimalApp = MinimalApp(
|
||||
update.packageName,
|
||||
update.versionName,
|
||||
update.versionCode,
|
||||
update.displayName
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,4 +5,4 @@ import com.aurora.gplayapi.data.models.App
|
||||
data class PaginatedAppList(
|
||||
val appList: MutableList<App> = mutableListOf(),
|
||||
var hasMore: Boolean
|
||||
)
|
||||
)
|
||||
|
||||
@@ -74,7 +74,8 @@ data class SelfUpdate(
|
||||
isFree = true,
|
||||
isInstalled = true,
|
||||
certificateSetList = CertUtil.getEncodedCertificateHashes(
|
||||
context, context.packageName
|
||||
context,
|
||||
context.packageName
|
||||
).map {
|
||||
EncodedCertificateSet(certificateSet = it, sha256 = String())
|
||||
}.toMutableList()
|
||||
|
||||
@@ -19,11 +19,8 @@
|
||||
|
||||
package com.aurora.store.data.model
|
||||
|
||||
|
||||
sealed class ViewState {
|
||||
inline fun <reified T> ViewState.getDataAs(): T {
|
||||
return (this as? Success<*>)?.data as T
|
||||
}
|
||||
inline fun <reified T> ViewState.getDataAs(): T = (this as? Success<*>)?.data as T
|
||||
|
||||
data object Loading : ViewState()
|
||||
data object Empty : ViewState()
|
||||
@@ -33,7 +30,7 @@ sealed class ViewState {
|
||||
}
|
||||
|
||||
sealed class AuthState {
|
||||
data object Init: AuthState()
|
||||
data object Init : AuthState()
|
||||
data object Available : AuthState()
|
||||
data object Unavailable : AuthState()
|
||||
data object SignedIn : AuthState()
|
||||
@@ -68,18 +65,15 @@ sealed class AppState {
|
||||
/**
|
||||
* Whether there is some sort of ongoing process related to the app
|
||||
*/
|
||||
fun inProgress(): Boolean {
|
||||
return this is Downloading || this is Installing || this is Purchasing || this is Queued
|
||||
}
|
||||
fun inProgress(): Boolean =
|
||||
this is Downloading || this is Installing || this is Purchasing || this is Queued
|
||||
|
||||
/**
|
||||
* Progress of the process related to the app; 0 otherwise
|
||||
*/
|
||||
fun progress(): Float {
|
||||
return when (this) {
|
||||
is Downloading -> progress
|
||||
is Installing -> progress
|
||||
else -> 0F
|
||||
}
|
||||
fun progress(): Float = when (this) {
|
||||
is Downloading -> progress
|
||||
is Installing -> progress
|
||||
else -> 0F
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,5 +4,5 @@ enum class UpdateMode {
|
||||
DISABLED,
|
||||
CHECK_AND_NOTIFY,
|
||||
CHECK_AND_INSTALL,
|
||||
CHECK_ONLY,
|
||||
CHECK_ONLY
|
||||
}
|
||||
|
||||
@@ -26,6 +26,9 @@ import com.aurora.gplayapi.network.IHttpClient
|
||||
import com.aurora.store.BuildConfig.APPLICATION_ID
|
||||
import com.aurora.store.BuildConfig.VERSION_CODE
|
||||
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.StateFlow
|
||||
import kotlinx.coroutines.flow.asStateFlow
|
||||
@@ -38,12 +41,9 @@ import okhttp3.Request
|
||||
import okhttp3.RequestBody
|
||||
import okhttp3.RequestBody.Companion.toRequestBody
|
||||
import okhttp3.Response
|
||||
import java.io.IOException
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient): IHttpClient {
|
||||
class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient) : IHttpClient {
|
||||
|
||||
companion object {
|
||||
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 {
|
||||
val request = Request(
|
||||
url = url.toHttpUrl(),
|
||||
headers = headers.toHeaders(),
|
||||
headers = headers.toHeaders()
|
||||
)
|
||||
return okHttpClient.newCall(request).execute()
|
||||
}
|
||||
@@ -102,14 +102,12 @@ class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient): IH
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun post(url: String, headers: Map<String, String>, body: ByteArray): PlayResponse {
|
||||
return post(url, headers, body.toRequestBody())
|
||||
}
|
||||
override fun post(url: String, headers: Map<String, String>, body: ByteArray): PlayResponse =
|
||||
post(url, headers, body.toRequestBody())
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun get(url: String, headers: Map<String, String>): PlayResponse {
|
||||
return get(url, headers, mapOf())
|
||||
}
|
||||
override fun get(url: String, headers: Map<String, String>): PlayResponse =
|
||||
get(url, headers, mapOf())
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun get(
|
||||
@@ -136,11 +134,7 @@ class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient): IH
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
override fun get(
|
||||
url: String,
|
||||
headers: Map<String, String>,
|
||||
paramString: String
|
||||
): PlayResponse {
|
||||
override fun get(url: String, headers: Map<String, String>, paramString: String): PlayResponse {
|
||||
val request = Request(
|
||||
url = "$url$paramString".toHttpUrl(),
|
||||
headers = headers.toHeaders(),
|
||||
@@ -165,16 +159,14 @@ class HttpClient @Inject constructor(private val okHttpClient: OkHttpClient): IH
|
||||
return urlBuilder.build()
|
||||
}
|
||||
|
||||
private fun buildPlayResponse(response: Response): PlayResponse {
|
||||
return PlayResponse(
|
||||
isSuccessful = response.isSuccessful,
|
||||
code = response.code,
|
||||
responseBytes = response.body.bytes(),
|
||||
errorString = if (!response.isSuccessful) response.message else String()
|
||||
).also {
|
||||
val isCached = if (response.cacheResponse != null) "CACHED" else "NETWORK"
|
||||
_responseCode.value = response.code
|
||||
Log.i(TAG, "OKHTTP [$isCached] [${response.code}] ${response.request.url}")
|
||||
}
|
||||
private fun buildPlayResponse(response: Response): PlayResponse = PlayResponse(
|
||||
isSuccessful = response.isSuccessful,
|
||||
code = response.code,
|
||||
responseBytes = response.body.bytes(),
|
||||
errorString = if (!response.isSuccessful) response.message else String()
|
||||
).also {
|
||||
val isCached = if (response.cacheResponse != null) "CACHED" else "NETWORK"
|
||||
_responseCode.value = response.code
|
||||
Log.i(TAG, "OKHTTP [$isCached] [${response.code}] ${response.request.url}")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,5 @@ object IHttpClientModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesIHttpClientInstance(httpClient: HttpClient): IHttpClient {
|
||||
return httpClient
|
||||
}
|
||||
fun providesIHttpClientInstance(httpClient: HttpClient): IHttpClient = httpClient
|
||||
}
|
||||
|
||||
@@ -33,10 +33,6 @@ import dagger.Provides
|
||||
import dagger.hilt.InstallIn
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
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.File
|
||||
import java.io.InputStream
|
||||
@@ -49,6 +45,10 @@ import java.security.cert.CertificateFactory
|
||||
import java.security.cert.X509Certificate
|
||||
import java.util.concurrent.TimeUnit
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.serialization.json.Json
|
||||
import okhttp3.Cache
|
||||
import okhttp3.CertificatePinner
|
||||
import okhttp3.OkHttpClient
|
||||
|
||||
@Module
|
||||
@InstallIn(SingletonComponent::class)
|
||||
@@ -90,13 +90,13 @@ object OkHttpClientModule {
|
||||
val googleRootCerts = getGoogleRootCertHashes(context).map { "sha256/$it" }
|
||||
.toTypedArray()
|
||||
|
||||
return CertificatePinner.Builder()
|
||||
return CertificatePinner.Builder()
|
||||
.add("*.googleapis.com", *googleRootCerts)
|
||||
.add("*.google.com", *googleRootCerts)
|
||||
.add("auroraoss.com", "sha256/mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=") // GTS Root R4
|
||||
.add("*.exodus-privacy.eu.org", "sha256/C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=") // ISRG Root X1
|
||||
.add("gitlab.com", "sha256/x4QzPSC810K5/cMjb05Qm4k3Bw5zBn4lTdO/nEW/Td4=") // USERTrust RSA Certification Authority
|
||||
.add("plexus.techlore.tech", "sha256/C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=") // ISRG Root X1
|
||||
.add("auroraoss.com", "sha256/mEflZT5enoR1FuXLgYYGqnVEoZvmf9c2bVBpiOjYQ0c=")
|
||||
.add("*.exodus-privacy.eu.org", "sha256/C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=")
|
||||
.add("gitlab.com", "sha256/x4QzPSC810K5/cMjb05Qm4k3Bw5zBn4lTdO/nEW/Td4=")
|
||||
.add("plexus.techlore.tech", "sha256/C5+lpZ7tcVwmwQIMcRtPbsQtWLABXhQzejna0wHFr8M=")
|
||||
.build()
|
||||
}
|
||||
|
||||
@@ -108,7 +108,11 @@ object OkHttpClientModule {
|
||||
val proxyInfo = json.decodeFromString<ProxyInfo>(proxyInfoString)
|
||||
|
||||
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)
|
||||
)
|
||||
|
||||
@@ -117,9 +121,8 @@ object OkHttpClientModule {
|
||||
|
||||
if (!proxyUser.isNullOrBlank() && !proxyPassword.isNullOrBlank()) {
|
||||
Authenticator.setDefault(object : Authenticator() {
|
||||
override fun getPasswordAuthentication(): PasswordAuthentication {
|
||||
return PasswordAuthentication(proxyUser, proxyPassword.toCharArray())
|
||||
}
|
||||
override fun getPasswordAuthentication(): PasswordAuthentication =
|
||||
PasswordAuthentication(proxyUser, proxyPassword.toCharArray())
|
||||
})
|
||||
}
|
||||
return proxy
|
||||
@@ -131,25 +134,22 @@ object OkHttpClientModule {
|
||||
|
||||
@Provides
|
||||
@Singleton
|
||||
fun providesCacheDir(@ApplicationContext context: Context): Cache {
|
||||
return Cache(
|
||||
directory = File(context.cacheDir, "http_cache"),
|
||||
maxSize = 100L * 1024 * 1024
|
||||
)
|
||||
}
|
||||
fun providesCacheDir(@ApplicationContext context: Context): Cache = Cache(
|
||||
directory = File(context.cacheDir, "http_cache"),
|
||||
maxSize = 100L * 1024 * 1024
|
||||
)
|
||||
|
||||
private fun getGoogleRootCertHashes(context: Context): List<String> {
|
||||
return try {
|
||||
val certs = getX509Certificates(context.resources.openRawResource(R.raw.google_roots_ca))
|
||||
certs.map {
|
||||
val messageDigest = MessageDigest.getInstance(Algorithm.SHA256.value)
|
||||
messageDigest.update(it.publicKey.encoded)
|
||||
Base64.encodeToString(messageDigest.digest(), Base64.NO_WRAP)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to get SHA256 certificate hash", exception)
|
||||
emptyList()
|
||||
private fun getGoogleRootCertHashes(context: Context): List<String> = try {
|
||||
val certs =
|
||||
getX509Certificates(context.resources.openRawResource(R.raw.google_roots_ca))
|
||||
certs.map {
|
||||
val messageDigest = MessageDigest.getInstance(Algorithm.SHA256.value)
|
||||
messageDigest.update(it.publicKey.encoded)
|
||||
Base64.encodeToString(messageDigest.digest(), Base64.NO_WRAP)
|
||||
}
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to get SHA256 certificate hash", exception)
|
||||
emptyList()
|
||||
}
|
||||
|
||||
private fun getX509Certificates(inputStream: InputStream): List<X509Certificate> {
|
||||
|
||||
@@ -78,10 +78,9 @@ class GenericPagingSource<T : Any>(
|
||||
}
|
||||
}
|
||||
|
||||
override fun getRefreshKey(state: PagingState<Int, T>): Int? {
|
||||
return state.anchorPosition?.let { anchorPosition ->
|
||||
override fun getRefreshKey(state: PagingState<Int, T>): Int? =
|
||||
state.anchorPosition?.let { anchorPosition ->
|
||||
state.closestPageToPosition(anchorPosition)?.prevKey?.plus(1)
|
||||
?: state.closestPageToPosition(anchorPosition)?.nextKey?.minus(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -36,9 +36,8 @@ object AccountProvider {
|
||||
}
|
||||
}
|
||||
|
||||
fun isLoggedIn(context: Context): Boolean {
|
||||
return Preferences.getBoolean(context, Constants.ACCOUNT_SIGNED_IN, false)
|
||||
}
|
||||
fun isLoggedIn(context: Context): Boolean =
|
||||
Preferences.getBoolean(context, Constants.ACCOUNT_SIGNED_IN, false)
|
||||
|
||||
fun getLoginEmail(context: Context): String? {
|
||||
val email = Preferences.getString(context, Constants.ACCOUNT_EMAIL_PLAIN)
|
||||
|
||||
@@ -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_DISPENSER_URLS
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
@Singleton
|
||||
class AuthProvider @Inject constructor(
|
||||
@@ -70,9 +70,7 @@ class AuthProvider @Inject constructor(
|
||||
/**
|
||||
* Checks whether saved AuthData is valid or not
|
||||
*/
|
||||
fun isSavedAuthDataValid(): Boolean {
|
||||
return AuthHelper.isValid(authData!!)
|
||||
}
|
||||
fun isSavedAuthDataValid(): Boolean = AuthHelper.isValid(authData!!)
|
||||
|
||||
/**
|
||||
* Builds [AuthData] for login using personal Google account
|
||||
@@ -94,7 +92,7 @@ class AuthProvider @Inject constructor(
|
||||
token = token,
|
||||
tokenType = tokenType,
|
||||
properties = spoofProvider.deviceProperties,
|
||||
locale = spoofProvider.locale,
|
||||
locale = spoofProvider.locale
|
||||
)
|
||||
)
|
||||
} catch (exception: Exception) {
|
||||
@@ -154,10 +152,15 @@ class AuthProvider @Inject constructor(
|
||||
private fun throwError(playResponse: PlayResponse, context: Context) {
|
||||
when (playResponse.code) {
|
||||
400 -> throw Exception(context.getString(R.string.bad_request))
|
||||
|
||||
403 -> throw Exception(context.getString(R.string.access_denied_using_vpn))
|
||||
|
||||
404 -> throw Exception(context.getString(R.string.server_unreachable))
|
||||
|
||||
429 -> throw Exception(context.getString(R.string.login_rate_limited))
|
||||
|
||||
503 -> throw Exception(context.getString(R.string.server_maintenance))
|
||||
|
||||
else -> {
|
||||
if (playResponse.errorString.isNotBlank()) {
|
||||
throw Exception(playResponse.errorString)
|
||||
|
||||
@@ -24,15 +24,15 @@ import android.content.SharedPreferences
|
||||
import com.aurora.extensions.isNAndAbove
|
||||
import com.aurora.store.util.Preferences
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.io.File
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Singleton
|
||||
class BlacklistProvider @Inject constructor(
|
||||
private val json: Json,
|
||||
@ApplicationContext val context: Context,
|
||||
@ApplicationContext val context: Context
|
||||
) {
|
||||
|
||||
companion object {
|
||||
@@ -67,19 +67,17 @@ class BlacklistProvider @Inject constructor(
|
||||
} else {
|
||||
Preferences.getString(context, PREFERENCE_BLACKLIST)
|
||||
}
|
||||
if (rawBlacklist!!.isEmpty())
|
||||
if (rawBlacklist!!.isEmpty()) {
|
||||
mutableSetOf()
|
||||
else
|
||||
} else {
|
||||
json.decodeFromString<MutableSet<String>>(rawBlacklist)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
mutableSetOf()
|
||||
}
|
||||
}
|
||||
|
||||
fun isBlacklisted(packageName: String): Boolean {
|
||||
return blacklist.contains(packageName)
|
||||
}
|
||||
|
||||
fun isBlacklisted(packageName: String): Boolean = blacklist.contains(packageName)
|
||||
|
||||
fun blacklist(packageName: String) {
|
||||
blacklist = blacklist.apply {
|
||||
|
||||
@@ -41,8 +41,10 @@ object EglExtensionProvider {
|
||||
val configs = arrayOfNulls<EGLConfig>(configCount[0])
|
||||
if (egl.eglGetConfigs(display, configs, configCount[0], configCount)) {
|
||||
val pbufferAttribs = intArrayOf(
|
||||
EGL10.EGL_WIDTH, EGL10.EGL_PBUFFER_BIT,
|
||||
EGL10.EGL_HEIGHT, EGL10.EGL_PBUFFER_BIT,
|
||||
EGL10.EGL_WIDTH,
|
||||
EGL10.EGL_PBUFFER_BIT,
|
||||
EGL10.EGL_HEIGHT,
|
||||
EGL10.EGL_PBUFFER_BIT,
|
||||
EGL10.EGL_NONE
|
||||
)
|
||||
val contextAttributes = intArrayOf(12440, EGL10.EGL_PIXMAP_BIT, EGL10.EGL_NONE)
|
||||
|
||||
@@ -30,15 +30,16 @@ object NativeDeviceInfoProvider {
|
||||
|
||||
fun getNativeDeviceProperties(context: Context, isExport: Boolean = false): Properties {
|
||||
val properties = Properties().apply {
|
||||
//Build Props
|
||||
// Build Props
|
||||
setProperty("UserReadableName", "${Build.MANUFACTURER} ${Build.MODEL}")
|
||||
setProperty("Build.HARDWARE", Build.HARDWARE)
|
||||
setProperty(
|
||||
"Build.RADIO",
|
||||
if (Build.getRadioVersion() != null)
|
||||
if (Build.getRadioVersion() != null) {
|
||||
Build.getRadioVersion()
|
||||
else
|
||||
} else {
|
||||
"unknown"
|
||||
}
|
||||
)
|
||||
setProperty("Build.FINGERPRINT", Build.FINGERPRINT)
|
||||
setProperty("Build.BRAND", Build.BRAND)
|
||||
@@ -62,21 +63,24 @@ object NativeDeviceInfoProvider {
|
||||
"${config.navigation == Configuration.NAVIGATIONHIDDEN_YES}"
|
||||
)
|
||||
|
||||
//Display Metrics
|
||||
// Display Metrics
|
||||
val metrics = context.resources.displayMetrics
|
||||
setProperty("Screen.Density", "${metrics.densityDpi}")
|
||||
setProperty("Screen.Width", "${metrics.widthPixels}")
|
||||
setProperty("Screen.Height", "${metrics.heightPixels}")
|
||||
|
||||
//Supported Platforms
|
||||
// Supported Platforms
|
||||
setProperty("Platforms", Build.SUPPORTED_ABIS.joinToString(separator = ","))
|
||||
//Supported Features
|
||||
// Supported Features
|
||||
setProperty("Features", getFeatures(context).joinToString(separator = ","))
|
||||
//Shared Locales
|
||||
// Shared Locales
|
||||
setProperty("Locales", getLocales(context).joinToString(separator = ","))
|
||||
//Shared Libraries
|
||||
setProperty("SharedLibraries", getSharedLibraries(context).joinToString(separator = ","))
|
||||
//GL Extensions
|
||||
// Shared Libraries
|
||||
setProperty(
|
||||
"SharedLibraries",
|
||||
getSharedLibraries(context).joinToString(separator = ",")
|
||||
)
|
||||
// GL Extensions
|
||||
val activityManager = context.getSystemService<ActivityManager>()
|
||||
setProperty(
|
||||
"GL.Version",
|
||||
@@ -87,7 +91,7 @@ object NativeDeviceInfoProvider {
|
||||
EglExtensionProvider.eglExtensions.joinToString(separator = ",")
|
||||
)
|
||||
|
||||
//Google Related Props
|
||||
// Google Related Props
|
||||
setProperty("Client", "android-google")
|
||||
|
||||
val gsfVersionProvider = NativeGsfVersionProvider(context, isExport)
|
||||
@@ -95,11 +99,11 @@ object NativeDeviceInfoProvider {
|
||||
setProperty("Vending.version", gsfVersionProvider.vendingVersionCode.toString())
|
||||
setProperty("Vending.versionString", gsfVersionProvider.vendingVersionString)
|
||||
|
||||
//MISC
|
||||
// MISC
|
||||
setProperty("Roaming", "mobile-notroaming")
|
||||
setProperty("TimeZone", "UTC-10")
|
||||
|
||||
//Telephony (USA 3650 AT&T)
|
||||
// Telephony (USA 3650 AT&T)
|
||||
setProperty("CellOperator", "310")
|
||||
setProperty("SimOperator", "38")
|
||||
}
|
||||
@@ -108,29 +112,23 @@ object NativeDeviceInfoProvider {
|
||||
return properties
|
||||
}
|
||||
|
||||
private fun getFeatures(context: Context): List<String> {
|
||||
return context
|
||||
.packageManager
|
||||
.systemAvailableFeatures
|
||||
.mapNotNull { it.name }
|
||||
}
|
||||
private fun getFeatures(context: Context): List<String> = context
|
||||
.packageManager
|
||||
.systemAvailableFeatures
|
||||
.mapNotNull { it.name }
|
||||
|
||||
private fun getLocales(context: Context): List<String> {
|
||||
return context
|
||||
.assets
|
||||
.locales
|
||||
.mapNotNull { it.replace("-", "_") }
|
||||
}
|
||||
private fun getLocales(context: Context): List<String> = context
|
||||
.assets
|
||||
.locales
|
||||
.mapNotNull { it.replace("-", "_") }
|
||||
|
||||
private fun getSharedLibraries(context: Context): List<String> {
|
||||
return context
|
||||
.packageManager
|
||||
.systemSharedLibraryNames
|
||||
?.toList() ?: emptyList()
|
||||
}
|
||||
private fun getSharedLibraries(context: Context): List<String> = context
|
||||
.packageManager
|
||||
.systemSharedLibraryNames
|
||||
?.toList() ?: emptyList()
|
||||
|
||||
private fun stripHuaweiProperties(properties: Properties): Properties {
|
||||
//Add Pixel 7a properties
|
||||
// Add Pixel 7a properties
|
||||
properties["Build.HARDWARE"] = "lynx"
|
||||
properties["Build.BOOTLOADER"] = "lynx-1.0-9716681"
|
||||
properties["Build.BRAND"] = "google"
|
||||
|
||||
@@ -27,12 +27,12 @@ import androidx.core.content.getSystemService
|
||||
import com.aurora.extensions.isNAndAbove
|
||||
import com.aurora.store.data.model.NetworkStatus
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.coroutines.channels.awaitClose
|
||||
import kotlinx.coroutines.flow.Flow
|
||||
import kotlinx.coroutines.flow.callbackFlow
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
|
||||
/**
|
||||
* A simple provider with a flow to observe internet connectivity changes
|
||||
|
||||
@@ -54,8 +54,8 @@ class PermissionProvider(private val fragment: Fragment) :
|
||||
/**
|
||||
* Checks whether a known permission has been granted
|
||||
*/
|
||||
fun isGranted(context: Context, permissionType: PermissionType): Boolean {
|
||||
return when (permissionType) {
|
||||
fun isGranted(context: Context, permissionType: PermissionType): Boolean =
|
||||
when (permissionType) {
|
||||
PermissionType.EXTERNAL_STORAGE,
|
||||
PermissionType.STORAGE_MANAGER -> {
|
||||
context.isExternalStorageAccessible()
|
||||
@@ -76,9 +76,8 @@ class PermissionProvider(private val fragment: Fragment) :
|
||||
PermissionType.DOZE_WHITELIST -> context.isIgnoringBatteryOptimizations()
|
||||
|
||||
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
|
||||
@@ -132,7 +131,9 @@ class PermissionProvider(private val fragment: Fragment) :
|
||||
Permission(
|
||||
type = PermissionType.POST_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,
|
||||
isGranted = isGranted(context, PermissionType.POST_NOTIFICATIONS)
|
||||
)
|
||||
@@ -147,7 +148,7 @@ class PermissionProvider(private val fragment: Fragment) :
|
||||
subtitle = context.getString(R.string.app_links_desc),
|
||||
optional = true,
|
||||
isGranted = isGranted(context, PermissionType.APP_LINKS)
|
||||
),
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -195,7 +196,7 @@ class PermissionProvider(private val fragment: Fragment) :
|
||||
if (!isGranted(context, PermissionType.INSTALL_UNKNOWN_APPS)) {
|
||||
context.toast(R.string.toast_permission_installer_required)
|
||||
} else {
|
||||
/**
|
||||
/*
|
||||
* 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,
|
||||
* even though OS says it is granted.
|
||||
@@ -231,18 +232,16 @@ class PermissionProvider(private val fragment: Fragment) :
|
||||
}
|
||||
|
||||
@SuppressLint("InlinedApi")
|
||||
private fun knownPermissions(): Map<PermissionType, Intent> {
|
||||
return mapOf(
|
||||
PermissionType.STORAGE_MANAGER to PackageUtil.getStorageManagerIntent(context),
|
||||
PermissionType.INSTALL_UNKNOWN_APPS to PackageUtil.getInstallUnknownAppsIntent(),
|
||||
PermissionType.DOZE_WHITELIST to Intent(
|
||||
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||
"package:${BuildConfig.APPLICATION_ID}".toUri()
|
||||
),
|
||||
PermissionType.APP_LINKS to Intent(
|
||||
ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
|
||||
"package:${BuildConfig.APPLICATION_ID}".toUri()
|
||||
)
|
||||
private fun knownPermissions(): Map<PermissionType, Intent> = mapOf(
|
||||
PermissionType.STORAGE_MANAGER to PackageUtil.getStorageManagerIntent(context),
|
||||
PermissionType.INSTALL_UNKNOWN_APPS to PackageUtil.getInstallUnknownAppsIntent(),
|
||||
PermissionType.DOZE_WHITELIST to Intent(
|
||||
Settings.ACTION_REQUEST_IGNORE_BATTERY_OPTIMIZATIONS,
|
||||
"package:${BuildConfig.APPLICATION_ID}".toUri()
|
||||
),
|
||||
PermissionType.APP_LINKS to Intent(
|
||||
ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
|
||||
"package:${BuildConfig.APPLICATION_ID}".toUri()
|
||||
)
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@@ -137,12 +137,9 @@ open class SpoofDeviceProvider(private val context: Context) {
|
||||
return File(sourceDir)
|
||||
}
|
||||
} catch (ignored: Exception) {
|
||||
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
private fun filenameValid(filename: String): Boolean {
|
||||
return filename.endsWith(SUFFIX)
|
||||
}
|
||||
private fun filenameValid(filename: String): Boolean = filename.endsWith(SUFFIX)
|
||||
}
|
||||
|
||||
@@ -24,11 +24,11 @@ import com.aurora.store.R
|
||||
import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_VENDING_VERSION
|
||||
import dagger.hilt.android.qualifiers.ApplicationContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.util.Locale
|
||||
import java.util.Properties
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
/**
|
||||
* Provider class to work with device and locale spoofs
|
||||
@@ -36,7 +36,7 @@ import javax.inject.Singleton
|
||||
@Singleton
|
||||
class SpoofProvider @Inject constructor(
|
||||
private val json: Json,
|
||||
@ApplicationContext val context: Context,
|
||||
@ApplicationContext val context: Context
|
||||
) : SpoofDeviceProvider(context) {
|
||||
|
||||
companion object {
|
||||
@@ -119,7 +119,10 @@ class SpoofProvider @Inject constructor(
|
||||
val versionStrings = resources.getStringArray(R.array.pref_vending_version)
|
||||
|
||||
currentProperties.setProperty("Vending.version", versionCodes[vendingVersionIndex])
|
||||
currentProperties.setProperty("Vending.versionString", versionStrings[vendingVersionIndex])
|
||||
currentProperties.setProperty(
|
||||
"Vending.versionString",
|
||||
versionStrings[vendingVersionIndex]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,9 +13,9 @@ import com.aurora.extensions.TAG
|
||||
import com.aurora.store.AuroraApp
|
||||
import com.aurora.store.data.helper.DownloadHelper
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
@AndroidEntryPoint
|
||||
class DownloadCancelReceiver : BroadcastReceiver() {
|
||||
|
||||
@@ -64,7 +64,7 @@ class MigrationReceiver : BroadcastReceiver() {
|
||||
if (CertUtil.isAppGalleryApp(context, context.packageName)) {
|
||||
val dispensers = Preferences.getStringSet(context, PREFERENCE_DISPENSER_URLS)
|
||||
.toMutableSet()
|
||||
|
||||
|
||||
dispensers.remove(Constants.URL_DISPENSER)
|
||||
|
||||
context.save(PREFERENCE_DISPENSER_URLS, dispensers)
|
||||
@@ -75,7 +75,8 @@ class MigrationReceiver : BroadcastReceiver() {
|
||||
// 63 -> 64
|
||||
if (currentVersion == 2) {
|
||||
if (isOAndAbove) {
|
||||
with(context.getSystemService<NotificationManager>()!!) { // !1189
|
||||
with(context.getSystemService<NotificationManager>()!!) {
|
||||
// !1189
|
||||
deleteNotificationChannel("NOTIFICATION_CHANNEL_GENERAL")
|
||||
deleteNotificationChannel("NOTIFICATION_CHANNEL_ALERT")
|
||||
}
|
||||
@@ -87,10 +88,11 @@ class MigrationReceiver : BroadcastReceiver() {
|
||||
if (currentVersion == 3) {
|
||||
val updateMode = UpdateMode.entries[
|
||||
Preferences.getInteger(
|
||||
context,
|
||||
PREFERENCE_UPDATES_AUTO,
|
||||
UpdateMode.DISABLED.ordinal
|
||||
)]
|
||||
context,
|
||||
PREFERENCE_UPDATES_AUTO,
|
||||
UpdateMode.DISABLED.ordinal
|
||||
)
|
||||
]
|
||||
|
||||
if (updateMode != UpdateMode.DISABLED) {
|
||||
runCatching {
|
||||
|
||||
@@ -48,7 +48,7 @@ open class PackageManagerReceiver : BroadcastReceiver() {
|
||||
}
|
||||
}
|
||||
|
||||
//Clear installation queue
|
||||
// Clear installation queue
|
||||
appInstaller.getPreferredInstaller().removeFromInstallQueue(packageName)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,15 +15,15 @@ import com.aurora.store.data.helper.DownloadHelper
|
||||
import com.aurora.store.data.providers.AccountProvider
|
||||
import com.aurora.store.util.NotificationUtil
|
||||
import dagger.hilt.android.AndroidEntryPoint
|
||||
import javax.inject.Inject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.launch
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Triggers re-install/unarchive of a previously archived app on Android 15+ devices.
|
||||
*/
|
||||
@AndroidEntryPoint
|
||||
class UnarchivePackageReceiver: BroadcastReceiver() {
|
||||
class UnarchivePackageReceiver : BroadcastReceiver() {
|
||||
|
||||
@Inject
|
||||
lateinit var appDetailsHelper: AppDetailsHelper
|
||||
|
||||
@@ -35,7 +35,9 @@ object MigrationHelper {
|
||||
private fun migrateFrom1To2(database: SupportSQLiteDatabase) {
|
||||
database.beginTransaction()
|
||||
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()
|
||||
} catch (exception: 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) {
|
||||
database.beginTransaction()
|
||||
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()
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed while migrating from database version 2 to 3", exception)
|
||||
@@ -63,7 +67,9 @@ object MigrationHelper {
|
||||
database.beginTransaction()
|
||||
try {
|
||||
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()
|
||||
} catch (exception: Exception) {
|
||||
@@ -79,7 +85,9 @@ object MigrationHelper {
|
||||
private fun migrateFrom4To5(database: SupportSQLiteDatabase) {
|
||||
database.beginTransaction()
|
||||
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()
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed while migrating from database version 4 to 5", exception)
|
||||
|
||||
@@ -29,25 +29,25 @@ object RoomModule {
|
||||
fun providesRoomInstance(
|
||||
@ApplicationContext context: Context,
|
||||
downloadConverter: DownloadConverter
|
||||
): AuroraDatabase {
|
||||
return Room.databaseBuilder(context, AuroraDatabase::class.java, DATABASE)
|
||||
.addMigrations(MIGRATION_1_2, MIGRATION_2_3, MIGRATION_3_4, MIGRATION_4_5, MIGRATION_5_6)
|
||||
.addTypeConverter(downloadConverter)
|
||||
.build()
|
||||
}
|
||||
): AuroraDatabase = Room.databaseBuilder(context, AuroraDatabase::class.java, DATABASE)
|
||||
.addMigrations(
|
||||
MIGRATION_1_2,
|
||||
MIGRATION_2_3,
|
||||
MIGRATION_3_4,
|
||||
MIGRATION_4_5,
|
||||
MIGRATION_5_6
|
||||
)
|
||||
.addTypeConverter(downloadConverter)
|
||||
.build()
|
||||
|
||||
@Provides
|
||||
fun providesDownloadDao(auroraDatabase: AuroraDatabase): DownloadDao {
|
||||
return auroraDatabase.downloadDao()
|
||||
}
|
||||
fun providesDownloadDao(auroraDatabase: AuroraDatabase): DownloadDao =
|
||||
auroraDatabase.downloadDao()
|
||||
|
||||
@Provides
|
||||
fun providesFavouriteDao(auroraDatabase: AuroraDatabase): FavouriteDao {
|
||||
return auroraDatabase.favouriteDao()
|
||||
}
|
||||
fun providesFavouriteDao(auroraDatabase: AuroraDatabase): FavouriteDao =
|
||||
auroraDatabase.favouriteDao()
|
||||
|
||||
@Provides
|
||||
fun providesUpdateDao(auroraDatabase: AuroraDatabase): UpdateDao {
|
||||
return auroraDatabase.updateDao()
|
||||
}
|
||||
fun providesUpdateDao(auroraDatabase: AuroraDatabase): UpdateDao = auroraDatabase.updateDao()
|
||||
}
|
||||
|
||||
@@ -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.update.Update
|
||||
import com.aurora.store.util.PathUtil
|
||||
import kotlinx.parcelize.Parcelize
|
||||
import java.util.Date
|
||||
import kotlinx.parcelize.Parcelize
|
||||
|
||||
@Parcelize
|
||||
@Entity(tableName = "download")
|
||||
@@ -44,73 +44,67 @@ data class Download(
|
||||
private val isSuccessful get() = status == DownloadStatus.COMPLETED
|
||||
|
||||
companion object {
|
||||
fun fromApp(app: App): Download {
|
||||
return Download(
|
||||
app.packageName,
|
||||
app.versionCode,
|
||||
app.offerType,
|
||||
app.isInstalled,
|
||||
app.displayName,
|
||||
app.iconArtwork.url,
|
||||
app.size,
|
||||
app.id,
|
||||
DownloadStatus.QUEUED,
|
||||
0,
|
||||
0L,
|
||||
0L,
|
||||
0,
|
||||
0,
|
||||
app.fileList.filterNot { it.url.isBlank() },
|
||||
app.dependencies.dependentLibraries.map { SharedLib.fromApp(it) },
|
||||
app.targetSdk,
|
||||
Date().time,
|
||||
app.requiresGMS()
|
||||
)
|
||||
}
|
||||
fun fromApp(app: App): Download = Download(
|
||||
app.packageName,
|
||||
app.versionCode,
|
||||
app.offerType,
|
||||
app.isInstalled,
|
||||
app.displayName,
|
||||
app.iconArtwork.url,
|
||||
app.size,
|
||||
app.id,
|
||||
DownloadStatus.QUEUED,
|
||||
0,
|
||||
0L,
|
||||
0L,
|
||||
0,
|
||||
0,
|
||||
app.fileList.filterNot { it.url.isBlank() },
|
||||
app.dependencies.dependentLibraries.map { SharedLib.fromApp(it) },
|
||||
app.targetSdk,
|
||||
Date().time,
|
||||
app.requiresGMS()
|
||||
)
|
||||
|
||||
fun fromUpdate(update: Update): Download {
|
||||
return Download(
|
||||
update.packageName,
|
||||
update.versionCode,
|
||||
update.offerType,
|
||||
true,
|
||||
update.displayName,
|
||||
update.iconURL,
|
||||
update.size,
|
||||
update.id,
|
||||
DownloadStatus.QUEUED,
|
||||
0,
|
||||
0L,
|
||||
0L,
|
||||
0,
|
||||
0,
|
||||
update.fileList,
|
||||
update.sharedLibs,
|
||||
update.targetSdk,
|
||||
Date().time
|
||||
)
|
||||
}
|
||||
fun fromUpdate(update: Update): Download = Download(
|
||||
update.packageName,
|
||||
update.versionCode,
|
||||
update.offerType,
|
||||
true,
|
||||
update.displayName,
|
||||
update.iconURL,
|
||||
update.size,
|
||||
update.id,
|
||||
DownloadStatus.QUEUED,
|
||||
0,
|
||||
0L,
|
||||
0L,
|
||||
0,
|
||||
0,
|
||||
update.fileList,
|
||||
update.sharedLibs,
|
||||
update.targetSdk,
|
||||
Date().time
|
||||
)
|
||||
|
||||
fun fromExternalApk(externalApk: ExternalApk): Download {
|
||||
return Download(
|
||||
packageName = externalApk.packageName,
|
||||
versionCode = externalApk.versionCode,
|
||||
offerType = 0,
|
||||
isInstalled = false,
|
||||
displayName = externalApk.displayName,
|
||||
iconURL = externalApk.iconURL,
|
||||
size = 0,
|
||||
id = 0,
|
||||
status = DownloadStatus.QUEUED,
|
||||
progress = 0,
|
||||
speed = 0L,
|
||||
timeRemaining = 0L,
|
||||
totalFiles = 1,
|
||||
downloadedFiles = 0,
|
||||
fileList = externalApk.fileList,
|
||||
sharedLibs = emptyList(),
|
||||
)
|
||||
}
|
||||
fun fromExternalApk(externalApk: ExternalApk): Download = Download(
|
||||
packageName = externalApk.packageName,
|
||||
versionCode = externalApk.versionCode,
|
||||
offerType = 0,
|
||||
isInstalled = false,
|
||||
displayName = externalApk.displayName,
|
||||
iconURL = externalApk.iconURL,
|
||||
size = 0,
|
||||
id = 0,
|
||||
status = DownloadStatus.QUEUED,
|
||||
progress = 0,
|
||||
speed = 0L,
|
||||
timeRemaining = 0L,
|
||||
totalFiles = 1,
|
||||
downloadedFiles = 0,
|
||||
fileList = externalApk.fileList,
|
||||
sharedLibs = emptyList()
|
||||
)
|
||||
}
|
||||
|
||||
fun canInstall(context: Context): Boolean {
|
||||
|
||||
@@ -3,31 +3,25 @@ package com.aurora.store.data.room.download
|
||||
import androidx.room.ProvidedTypeConverter
|
||||
import androidx.room.TypeConverter
|
||||
import com.aurora.gplayapi.data.models.PlayFile
|
||||
import kotlinx.serialization.json.Json
|
||||
import javax.inject.Inject
|
||||
import javax.inject.Singleton
|
||||
import kotlinx.serialization.json.Json
|
||||
|
||||
@Singleton
|
||||
@ProvidedTypeConverter
|
||||
class DownloadConverter @Inject constructor(private val json: Json) {
|
||||
|
||||
@TypeConverter
|
||||
fun toSharedLibList(string: String): List<SharedLib> {
|
||||
return json.decodeFromString<List<SharedLib>>(string)
|
||||
}
|
||||
fun toSharedLibList(string: String): List<SharedLib> =
|
||||
json.decodeFromString<List<SharedLib>>(string)
|
||||
|
||||
@TypeConverter
|
||||
fun fromSharedLibList(list: List<SharedLib>): String {
|
||||
return json.encodeToString(list)
|
||||
}
|
||||
fun fromSharedLibList(list: List<SharedLib>): String = json.encodeToString(list)
|
||||
|
||||
@TypeConverter
|
||||
fun toGPlayFileList(string: String): List<PlayFile> {
|
||||
return json.decodeFromString<List<PlayFile>>(string)
|
||||
}
|
||||
fun toGPlayFileList(string: String): List<PlayFile> =
|
||||
json.decodeFromString<List<PlayFile>>(string)
|
||||
|
||||
@TypeConverter
|
||||
fun fromGPlayFileList(list: List<PlayFile>): String {
|
||||
return json.encodeToString(list)
|
||||
}
|
||||
fun fromGPlayFileList(list: List<PlayFile>): String = json.encodeToString(list)
|
||||
}
|
||||
|
||||
@@ -31,12 +31,7 @@ interface DownloadDao {
|
||||
WHERE packageName=:packageName
|
||||
"""
|
||||
)
|
||||
suspend fun updateProgress(
|
||||
packageName: String,
|
||||
progress: Int,
|
||||
speed: Long,
|
||||
timeRemaining: Long
|
||||
)
|
||||
suspend fun updateProgress(packageName: String, progress: Int, speed: Long, timeRemaining: Long)
|
||||
|
||||
@Query("SELECT * FROM download")
|
||||
fun downloads(): Flow<List<Download>>
|
||||
|
||||
@@ -14,12 +14,10 @@ data class SharedLib(
|
||||
var fileList: List<PlayFile>
|
||||
) : Parcelable {
|
||||
companion object {
|
||||
fun fromApp(app: App): SharedLib {
|
||||
return SharedLib(
|
||||
app.packageName,
|
||||
app.versionCode,
|
||||
app.fileList.filterNot { it.url.isBlank() }
|
||||
)
|
||||
}
|
||||
fun fromApp(app: App): SharedLib = SharedLib(
|
||||
app.packageName,
|
||||
app.versionCode,
|
||||
app.fileList.filterNot { it.url.isBlank() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -21,23 +21,19 @@ data class Favourite(
|
||||
) : Parcelable {
|
||||
|
||||
companion object {
|
||||
fun fromApp(app: App, mode: Mode): Favourite {
|
||||
return Favourite(
|
||||
packageName = app.packageName,
|
||||
displayName = app.displayName,
|
||||
iconURL = app.iconArtwork.url,
|
||||
added = System.currentTimeMillis(),
|
||||
mode = mode
|
||||
)
|
||||
}
|
||||
fun fromApp(app: App, mode: Mode): Favourite = Favourite(
|
||||
packageName = app.packageName,
|
||||
displayName = app.displayName,
|
||||
iconURL = app.iconArtwork.url,
|
||||
added = System.currentTimeMillis(),
|
||||
mode = mode
|
||||
)
|
||||
|
||||
fun Favourite.toApp(): App {
|
||||
return App(
|
||||
packageName = packageName,
|
||||
displayName = displayName,
|
||||
iconArtwork = Artwork(url = iconURL)
|
||||
)
|
||||
}
|
||||
fun Favourite.toApp(): App = App(
|
||||
packageName = packageName,
|
||||
displayName = displayName,
|
||||
iconArtwork = Artwork(url = iconURL)
|
||||
)
|
||||
}
|
||||
|
||||
enum class Mode {
|
||||
|
||||
@@ -6,5 +6,5 @@ import kotlinx.serialization.Serializable
|
||||
@Serializable
|
||||
data class ImportExport(
|
||||
val favourites: List<Favourite>,
|
||||
val auroraStoreVersion: Int = BuildConfig.VERSION_CODE,
|
||||
val auroraStoreVersion: Int = BuildConfig.VERSION_CODE
|
||||
)
|
||||
|
||||
@@ -21,7 +21,5 @@ data class ExternalApk(
|
||||
var fileList: List<PlayFile>
|
||||
) : Parcelable {
|
||||
|
||||
fun isInstalled(context: Context): Boolean {
|
||||
return PackageUtil.isInstalled(context, packageName)
|
||||
}
|
||||
}
|
||||
fun isInstalled(context: Context): Boolean = PackageUtil.isInstalled(context, packageName)
|
||||
}
|
||||
|
||||
@@ -33,40 +33,34 @@ data class Update(
|
||||
) : Parcelable {
|
||||
|
||||
companion object {
|
||||
fun fromApp(context: Context, app: App): Update {
|
||||
return Update(
|
||||
app.packageName,
|
||||
app.versionCode,
|
||||
app.versionName,
|
||||
app.displayName,
|
||||
app.iconArtwork.url,
|
||||
app.changes,
|
||||
app.id,
|
||||
app.developerName,
|
||||
app.size,
|
||||
app.updatedOn,
|
||||
app.certificateSetList.any {
|
||||
it.certificateSet in CertUtil.getEncodedCertificateHashes(
|
||||
context, app.packageName
|
||||
)
|
||||
},
|
||||
app.offerType,
|
||||
app.fileList.filterNot { it.url.isBlank() },
|
||||
app.dependencies.dependentLibraries.map { SharedLib.fromApp(it) },
|
||||
app.targetSdk
|
||||
)
|
||||
}
|
||||
fun fromApp(context: Context, app: App): Update = Update(
|
||||
app.packageName,
|
||||
app.versionCode,
|
||||
app.versionName,
|
||||
app.displayName,
|
||||
app.iconArtwork.url,
|
||||
app.changes,
|
||||
app.id,
|
||||
app.developerName,
|
||||
app.size,
|
||||
app.updatedOn,
|
||||
app.certificateSetList.any {
|
||||
it.certificateSet in CertUtil.getEncodedCertificateHashes(
|
||||
context,
|
||||
app.packageName
|
||||
)
|
||||
},
|
||||
app.offerType,
|
||||
app.fileList.filterNot { it.url.isBlank() },
|
||||
app.dependencies.dependentLibraries.map { SharedLib.fromApp(it) },
|
||||
app.targetSdk
|
||||
)
|
||||
}
|
||||
|
||||
fun isSelfUpdate(context: Context): Boolean {
|
||||
return packageName == context.packageName
|
||||
}
|
||||
fun isSelfUpdate(context: Context): Boolean = packageName == context.packageName
|
||||
|
||||
fun isInstalled(context: Context): Boolean {
|
||||
return PackageUtil.isInstalled(context, packageName)
|
||||
}
|
||||
fun isInstalled(context: Context): Boolean = PackageUtil.isInstalled(context, packageName)
|
||||
|
||||
fun isUpToDate(context: Context): Boolean {
|
||||
return PackageUtil.isInstalled(context, packageName, versionCode)
|
||||
}
|
||||
fun isUpToDate(context: Context): Boolean =
|
||||
PackageUtil.isInstalled(context, packageName, versionCode)
|
||||
}
|
||||
|
||||
@@ -100,8 +100,8 @@ open class AuthWorker @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun fetchAuthToken(email: String, oldToken: String? = null): String {
|
||||
return suspendCoroutine { continuation ->
|
||||
private suspend fun fetchAuthToken(email: String, oldToken: String? = null): String =
|
||||
suspendCoroutine { continuation ->
|
||||
fetchAuthToken(email, oldToken) { future ->
|
||||
try {
|
||||
val bundle = future.result
|
||||
@@ -110,14 +110,15 @@ open class AuthWorker @AssistedInject constructor(
|
||||
if (token != null) {
|
||||
continuation.resume(token)
|
||||
} else {
|
||||
continuation.resumeWithException(IllegalStateException("Auth token is null"))
|
||||
continuation.resumeWithException(
|
||||
IllegalStateException("Auth token is null")
|
||||
)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
continuation.resumeWithException(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun fetchAuthToken(
|
||||
email: String,
|
||||
@@ -155,8 +156,8 @@ open class AuthWorker @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
private fun verifyAndSaveAuth(authData: AuthData, accountType: AccountType): AuthData? {
|
||||
return if (authData.authToken.isNotEmpty() && authData.deviceConfigToken.isNotEmpty()) {
|
||||
private fun verifyAndSaveAuth(authData: AuthData, accountType: AccountType): AuthData? =
|
||||
if (authData.authToken.isNotEmpty() && authData.deviceConfigToken.isNotEmpty()) {
|
||||
authProvider.saveAuthData(authData)
|
||||
AccountProvider.login(
|
||||
context,
|
||||
@@ -171,5 +172,4 @@ open class AuthWorker @AssistedInject constructor(
|
||||
AccountProvider.logout(context)
|
||||
null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,19 +58,23 @@ class CacheWorker @AssistedInject constructor(
|
||||
override suspend fun doWork(): Result {
|
||||
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")
|
||||
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
|
||||
if (download.listFiles().isNullOrEmpty()) {
|
||||
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()) {
|
||||
// Purge empty non-accessible directory
|
||||
Log.i(TAG, "Removing empty directory for ${download.name}, ${versionCode.name}")
|
||||
|
||||
@@ -48,9 +48,6 @@ import com.aurora.store.util.PackageUtil
|
||||
import com.aurora.store.util.PathUtil
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.NonCancellable
|
||||
import kotlinx.coroutines.withContext
|
||||
import java.io.File
|
||||
import java.io.FileOutputStream
|
||||
import java.net.SocketException
|
||||
@@ -60,6 +57,9 @@ import java.security.DigestInputStream
|
||||
import java.security.MessageDigest
|
||||
import kotlin.coroutines.cancellation.CancellationException
|
||||
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.
|
||||
@@ -206,10 +206,13 @@ class DownloadWorker @AssistedInject constructor(
|
||||
private suspend fun onSuccess(): Result {
|
||||
return withContext(NonCancellable) {
|
||||
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)
|
||||
else
|
||||
} else {
|
||||
appInstaller.getPreferredInstaller().install(download)
|
||||
}
|
||||
Result.success()
|
||||
} catch (exception: Exception) {
|
||||
Log.e(TAG, "Failed to install ${download.packageName}", exception)
|
||||
@@ -425,7 +428,10 @@ class DownloadWorker @AssistedInject constructor(
|
||||
}
|
||||
|
||||
val notification = NotificationUtil.getDownloadNotification(
|
||||
context, download, icon, exception?.message
|
||||
context,
|
||||
download,
|
||||
icon,
|
||||
exception?.message
|
||||
)
|
||||
notificationManager.notify(
|
||||
if (isProgress) NOTIFICATION_ID else download.packageName.hashCode(),
|
||||
@@ -453,7 +459,10 @@ class DownloadWorker @AssistedInject constructor(
|
||||
file.inputStream().use { fis ->
|
||||
DigestInputStream(fis, messageDigest).use { dis ->
|
||||
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 */
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,6 +104,7 @@ class ExportWorker @AssistedInject constructor(
|
||||
|
||||
notificationManager = context.getSystemService<NotificationManager>()!!
|
||||
|
||||
@Suppress("ktlint:standard:mixed-condition-operators")
|
||||
if (packageName.isNullOrEmpty() || isDownload && versionCode == -1L) {
|
||||
Log.e(TAG, "Input data is corrupt, bailing out!")
|
||||
notifyStatus(displayName ?: String(), uri, false)
|
||||
@@ -126,12 +127,10 @@ class ExportWorker @AssistedInject constructor(
|
||||
return Result.success()
|
||||
}
|
||||
|
||||
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||
return ForegroundInfo(
|
||||
NOTIFICATION_ID_FGS,
|
||||
NotificationUtil.getExportNotification(context)
|
||||
)
|
||||
}
|
||||
override suspend fun getForegroundInfo(): ForegroundInfo = ForegroundInfo(
|
||||
NOTIFICATION_ID_FGS,
|
||||
NotificationUtil.getExportNotification(context)
|
||||
)
|
||||
|
||||
private fun notifyStatus(packageName: String, uri: Uri, success: Boolean = true) {
|
||||
notificationManager.notify(
|
||||
@@ -157,12 +156,10 @@ class ExportWorker @AssistedInject constructor(
|
||||
bundleAllAPKs(fileList.filterNotNull(), uri)
|
||||
}
|
||||
|
||||
private fun copyDownloadedApp(packageName: String, versionCode: Long, uri: Uri) {
|
||||
return bundleAllAPKs(
|
||||
PathUtil.getAppDownloadDir(context, packageName, versionCode).listFiles()!!.toList(),
|
||||
uri
|
||||
)
|
||||
}
|
||||
private fun copyDownloadedApp(packageName: String, versionCode: Long, uri: Uri) = bundleAllAPKs(
|
||||
PathUtil.getAppDownloadDir(context, packageName, versionCode).listFiles()!!.toList(),
|
||||
uri
|
||||
)
|
||||
|
||||
/**
|
||||
* Bundles all the given APKs to a zip file
|
||||
|
||||
@@ -32,10 +32,10 @@ import com.aurora.store.util.Preferences
|
||||
import com.aurora.store.util.Preferences.PREFERENCE_UPDATES_AUTO
|
||||
import dagger.assisted.Assisted
|
||||
import dagger.assisted.AssistedInject
|
||||
import java.util.Locale
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.serialization.json.Json
|
||||
import java.util.Locale
|
||||
|
||||
/**
|
||||
* 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 canSelfUpdate = !CertUtil.isFDroidApp(context, BuildConfig.APPLICATION_ID) &&
|
||||
!CertUtil.isAppGalleryApp(context, BuildConfig.APPLICATION_ID) &&
|
||||
BuildType.CURRENT != BuildType.DEBUG
|
||||
!CertUtil.isAppGalleryApp(context, BuildConfig.APPLICATION_ID) &&
|
||||
BuildType.CURRENT != BuildType.DEBUG
|
||||
|
||||
private val isAuroraOnlyFilterEnabled: Boolean
|
||||
get() = Preferences.getBoolean(context, Preferences.PREFERENCE_FILTER_AURORA_ONLY, false)
|
||||
@@ -77,14 +77,16 @@ class UpdateWorker @AssistedInject constructor(
|
||||
super.doWork()
|
||||
|
||||
Log.i(TAG, "Checking for app updates")
|
||||
val updateMode = UpdateMode.entries[inputData.getInt(
|
||||
UpdateHelper.UPDATE_MODE,
|
||||
Preferences.getInteger(
|
||||
context,
|
||||
PREFERENCE_UPDATES_AUTO,
|
||||
UpdateMode.CHECK_AND_INSTALL.ordinal
|
||||
val updateMode = UpdateMode.entries[
|
||||
inputData.getInt(
|
||||
UpdateHelper.UPDATE_MODE,
|
||||
Preferences.getInteger(
|
||||
context,
|
||||
PREFERENCE_UPDATES_AUTO,
|
||||
UpdateMode.CHECK_AND_INSTALL.ordinal
|
||||
)
|
||||
)
|
||||
)]
|
||||
]
|
||||
|
||||
if (updateMode == UpdateMode.DISABLED || !AccountProvider.isLoggedIn(context)) {
|
||||
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
|
||||
if (updateMode == UpdateMode.CHECK_AND_NOTIFY || !context.isIgnoringBatteryOptimizations()) {
|
||||
if (updateMode == UpdateMode.CHECK_AND_NOTIFY ||
|
||||
!context.isIgnoringBatteryOptimizations()
|
||||
) {
|
||||
Log.i(TAG, "Found ${updates.size} updates, notifying!")
|
||||
notifyUpdates(updates)
|
||||
return Result.success()
|
||||
@@ -123,7 +127,10 @@ class UpdateWorker @AssistedInject constructor(
|
||||
|
||||
// Notify about apps that cannot be auto-updated
|
||||
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)
|
||||
}
|
||||
|
||||
@@ -137,12 +144,10 @@ class UpdateWorker @AssistedInject constructor(
|
||||
}
|
||||
}
|
||||
|
||||
override suspend fun getForegroundInfo(): ForegroundInfo {
|
||||
return ForegroundInfo(
|
||||
notificationID,
|
||||
NotificationUtil.getUpdateNotification(context)
|
||||
)
|
||||
}
|
||||
override suspend fun getForegroundInfo(): ForegroundInfo = ForegroundInfo(
|
||||
notificationID,
|
||||
NotificationUtil.getUpdateNotification(context)
|
||||
)
|
||||
|
||||
/**
|
||||
* Checks and returns updates for all possible apps
|
||||
@@ -157,12 +162,18 @@ class UpdateWorker @AssistedInject constructor(
|
||||
val filteredPackages = if (isAuroraOnlyFilterEnabled) {
|
||||
packages.filter { CertUtil.isAuroraStoreApp(context, it.packageName) }
|
||||
} 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 { PackageUtil.isUpdatable(context, it.packageName, it.versionCode.toLong()) }
|
||||
.filter { PackageUtil.isUpdatable(context, it.packageName, it.versionCode) }
|
||||
.toMutableList()
|
||||
|
||||
if (canSelfUpdate) getSelfUpdate()?.let { updates.add(it) }
|
||||
@@ -179,7 +190,9 @@ class UpdateWorker @AssistedInject constructor(
|
||||
return withContext(Dispatchers.IO) {
|
||||
val updateUrl = when (BuildType.CURRENT) {
|
||||
BuildType.RELEASE -> Constants.UPDATE_URL_STABLE
|
||||
|
||||
BuildType.NIGHTLY -> Constants.UPDATE_URL_NIGHTLY
|
||||
|
||||
else -> {
|
||||
Log.i(TAG, "Self-updates are not available for this build!")
|
||||
return@withContext null
|
||||
@@ -193,6 +206,7 @@ class UpdateWorker @AssistedInject constructor(
|
||||
val isUpdate = when (BuildType.CURRENT) {
|
||||
BuildType.NIGHTLY,
|
||||
BuildType.RELEASE -> selfUpdate.versionCode > BuildConfig.VERSION_CODE
|
||||
|
||||
else -> false
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user