libs: Bump gplyapi to 3.5.0
* Map versionCode as Long * Handle all cases of immutable object modification Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
@@ -19,18 +19,8 @@
|
|||||||
|
|
||||||
package com.aurora.extensions
|
package com.aurora.extensions
|
||||||
|
|
||||||
import com.aurora.gplayapi.data.models.File
|
import com.aurora.gplayapi.data.models.PlayFile
|
||||||
|
|
||||||
fun <T> MutableList<T>.flushAndAdd(list: List<T>) {
|
fun List<PlayFile>.requiresObbDir(): Boolean {
|
||||||
clear()
|
return this.any { it.type == PlayFile.Type.OBB }
|
||||||
addAll(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun <T> MutableSet<T>.flushAndAdd(list: Set<T>) {
|
|
||||||
clear()
|
|
||||||
addAll(list)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun List<File>.requiresObbDir(): Boolean {
|
|
||||||
return this.any { it.type == File.FileType.OBB || it.type == File.FileType.PATCH }
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -26,7 +26,7 @@ sealed class BusEvent : Event() {
|
|||||||
lateinit var error: String
|
lateinit var error: String
|
||||||
|
|
||||||
data class Blacklisted(val packageName: String) : BusEvent()
|
data class Blacklisted(val packageName: String) : BusEvent()
|
||||||
data class ManualDownload(val packageName: String, val versionCode: Int) : BusEvent()
|
data class ManualDownload(val packageName: String, val versionCode: Long) : BusEvent()
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed class AuthEvent : Event() {
|
sealed class AuthEvent : Event() {
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ class DownloadHelper @Inject constructor(
|
|||||||
* @param packageName Name of the package of the app
|
* @param packageName Name of the package of the app
|
||||||
* @param versionCode Version of the package
|
* @param versionCode Version of the package
|
||||||
*/
|
*/
|
||||||
suspend fun clearDownload(packageName: String, versionCode: Int) {
|
suspend fun clearDownload(packageName: String, versionCode: Long) {
|
||||||
Log.i(TAG, "Clearing downloads for $packageName ($versionCode)")
|
Log.i(TAG, "Clearing downloads for $packageName ($versionCode)")
|
||||||
downloadDao.delete(packageName)
|
downloadDao.delete(packageName)
|
||||||
PathUtil.getAppDownloadDir(context, packageName, versionCode)
|
PathUtil.getAppDownloadDir(context, packageName, versionCode)
|
||||||
|
|||||||
@@ -81,7 +81,7 @@ class RootInstaller @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun xInstall(packageName: String, versionCode: Int, 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))
|
||||||
|
|||||||
@@ -183,7 +183,7 @@ class SessionInstaller @Inject constructor(
|
|||||||
|
|
||||||
private fun stageInstall(
|
private fun stageInstall(
|
||||||
packageName: String,
|
packageName: String,
|
||||||
versionCode: Int,
|
versionCode: Long,
|
||||||
sharedLibPkgName: String = ""
|
sharedLibPkgName: String = ""
|
||||||
): Int? {
|
): Int? {
|
||||||
val resolvedPackageName = sharedLibPkgName.ifBlank { packageName }
|
val resolvedPackageName = sharedLibPkgName.ifBlank { packageName }
|
||||||
|
|||||||
@@ -129,7 +129,7 @@ class ShizukuInstaller @Inject constructor(
|
|||||||
|
|
||||||
private fun install(
|
private fun install(
|
||||||
packageName: String,
|
packageName: String,
|
||||||
versionCode: Int,
|
versionCode: Long,
|
||||||
sharedLibPkgName: String = "",
|
sharedLibPkgName: String = "",
|
||||||
displayName: String = ""
|
displayName: String = ""
|
||||||
) {
|
) {
|
||||||
|
|||||||
@@ -103,7 +103,7 @@ abstract class InstallerBase(private val context: Context) : IInstaller {
|
|||||||
|
|
||||||
fun getFiles(
|
fun getFiles(
|
||||||
packageName: String,
|
packageName: String,
|
||||||
versionCode: Int,
|
versionCode: Long,
|
||||||
sharedLibPackageName: String = ""
|
sharedLibPackageName: String = ""
|
||||||
): List<File> {
|
): List<File> {
|
||||||
val downloadDir = if (sharedLibPackageName.isNotBlank()) {
|
val downloadDir = if (sharedLibPackageName.isNotBlank()) {
|
||||||
|
|||||||
@@ -1,13 +1,9 @@
|
|||||||
package com.aurora.store.data.model
|
package com.aurora.store.data.model
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.pm.PackageInfo
|
|
||||||
import android.graphics.Bitmap
|
import android.graphics.Bitmap
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import androidx.core.content.pm.PackageInfoCompat
|
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.store.data.room.update.Update
|
import com.aurora.store.data.room.update.Update
|
||||||
import com.aurora.store.util.PackageUtil
|
|
||||||
import kotlinx.parcelize.IgnoredOnParcel
|
import kotlinx.parcelize.IgnoredOnParcel
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@@ -15,7 +11,7 @@ import kotlinx.parcelize.Parcelize
|
|||||||
data class MinimalApp(
|
data class MinimalApp(
|
||||||
val packageName: String,
|
val packageName: String,
|
||||||
val versionName: String,
|
val versionName: String,
|
||||||
val versionCode: Int,
|
val versionCode: Long,
|
||||||
val displayName: String,
|
val displayName: String,
|
||||||
@IgnoredOnParcel
|
@IgnoredOnParcel
|
||||||
val icon: Bitmap? = null
|
val icon: Bitmap? = null
|
||||||
@@ -32,14 +28,6 @@ data class MinimalApp(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun toApp(minimalApp: MinimalApp): App {
|
|
||||||
return App(minimalApp.packageName).apply {
|
|
||||||
versionName = minimalApp.versionName ?: ""
|
|
||||||
versionCode = minimalApp.versionCode
|
|
||||||
displayName = minimalApp.displayName
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun fromUpdate(update: Update): MinimalApp {
|
fun fromUpdate(update: Update): MinimalApp {
|
||||||
return MinimalApp(
|
return MinimalApp(
|
||||||
update.packageName,
|
update.packageName,
|
||||||
@@ -49,14 +37,5 @@ data class MinimalApp(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromPackageInfo(context: Context, packageInfo: PackageInfo): MinimalApp {
|
|
||||||
return MinimalApp(
|
|
||||||
packageInfo.packageName,
|
|
||||||
packageInfo.versionName ?: "",
|
|
||||||
PackageInfoCompat.getLongVersionCode(packageInfo).toInt(),
|
|
||||||
packageInfo.applicationInfo!!.loadLabel(context.packageManager).toString(),
|
|
||||||
PackageUtil.getIconForPackage(context, packageInfo.packageName)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import android.content.Context
|
|||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.Artwork
|
import com.aurora.gplayapi.data.models.Artwork
|
||||||
import com.aurora.gplayapi.data.models.EncodedCertificateSet
|
import com.aurora.gplayapi.data.models.EncodedCertificateSet
|
||||||
import com.aurora.gplayapi.data.models.File
|
import com.aurora.gplayapi.data.models.PlayFile
|
||||||
import com.aurora.store.BuildConfig
|
import com.aurora.store.BuildConfig
|
||||||
import com.aurora.store.R
|
import com.aurora.store.R
|
||||||
import com.aurora.store.util.CertUtil
|
import com.aurora.store.util.CertUtil
|
||||||
@@ -31,7 +31,7 @@ import com.google.gson.annotations.SerializedName
|
|||||||
|
|
||||||
data class SelfUpdate(
|
data class SelfUpdate(
|
||||||
@SerializedName("version_name") var versionName: String = String(),
|
@SerializedName("version_name") var versionName: String = String(),
|
||||||
@SerializedName("version_code") var versionCode: Int = 0,
|
@SerializedName("version_code") var versionCode: Long = 0,
|
||||||
@SerializedName("aurora_build") var auroraBuild: String = String(),
|
@SerializedName("aurora_build") var auroraBuild: String = String(),
|
||||||
@SerializedName("fdroid_build") var fdroidBuild: String = String(),
|
@SerializedName("fdroid_build") var fdroidBuild: String = String(),
|
||||||
@SerializedName("updated_on") var updatedOn: String = String(),
|
@SerializedName("updated_on") var updatedOn: String = String(),
|
||||||
@@ -63,7 +63,7 @@ data class SelfUpdate(
|
|||||||
developerName = "Rahul Kumar Patel",
|
developerName = "Rahul Kumar Patel",
|
||||||
iconArtwork = Artwork(url = "$BASE_URL/$icon"),
|
iconArtwork = Artwork(url = "$BASE_URL/$icon"),
|
||||||
fileList = mutableListOf(
|
fileList = mutableListOf(
|
||||||
File(
|
PlayFile(
|
||||||
name = "${context.packageName}.apk",
|
name = "${context.packageName}.apk",
|
||||||
url = downloadURL,
|
url = downloadURL,
|
||||||
size = selfUpdate.size
|
size = selfUpdate.size
|
||||||
|
|||||||
@@ -3,6 +3,6 @@ package com.aurora.store.data.model
|
|||||||
data class SessionInfo(
|
data class SessionInfo(
|
||||||
val sessionId: Int,
|
val sessionId: Int,
|
||||||
val packageName: String,
|
val packageName: String,
|
||||||
val versionCode: Int,
|
val versionCode: Long,
|
||||||
val displayName: String = String()
|
val displayName: String = String()
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -52,7 +52,7 @@ class InstallerStatusReceiver : BroadcastReceiver() {
|
|||||||
if (context != null && intent?.action == ACTION_INSTALL_STATUS) {
|
if (context != null && intent?.action == ACTION_INSTALL_STATUS) {
|
||||||
val packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME)!!
|
val packageName = intent.getStringExtra(EXTRA_PACKAGE_NAME)!!
|
||||||
val displayName = intent.getStringExtra(EXTRA_DISPLAY_NAME)!!
|
val displayName = intent.getStringExtra(EXTRA_DISPLAY_NAME)!!
|
||||||
val versionCode = intent.getIntExtra(EXTRA_VERSION_CODE, -1)
|
val versionCode = intent.getLongExtra(EXTRA_VERSION_CODE, -1)
|
||||||
|
|
||||||
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)
|
val status = intent.getIntExtra(PackageInstaller.EXTRA_STATUS, -1)
|
||||||
val extra = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
|
val extra = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import android.os.Parcelable
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.File
|
import com.aurora.gplayapi.data.models.PlayFile
|
||||||
import com.aurora.store.data.model.DownloadStatus
|
import com.aurora.store.data.model.DownloadStatus
|
||||||
import com.aurora.store.data.room.update.Update
|
import com.aurora.store.data.room.update.Update
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
@@ -14,7 +14,7 @@ import java.util.Date
|
|||||||
@Entity(tableName = "download")
|
@Entity(tableName = "download")
|
||||||
data class Download(
|
data class Download(
|
||||||
@PrimaryKey val packageName: String,
|
@PrimaryKey val packageName: String,
|
||||||
val versionCode: Int,
|
val versionCode: Long,
|
||||||
val offerType: Int,
|
val offerType: Int,
|
||||||
val isInstalled: Boolean,
|
val isInstalled: Boolean,
|
||||||
val displayName: String,
|
val displayName: String,
|
||||||
@@ -27,7 +27,7 @@ data class Download(
|
|||||||
var timeRemaining: Long,
|
var timeRemaining: Long,
|
||||||
var totalFiles: Int,
|
var totalFiles: Int,
|
||||||
var downloadedFiles: Int,
|
var downloadedFiles: Int,
|
||||||
var fileList: List<File>,
|
var fileList: List<PlayFile>,
|
||||||
val sharedLibs: List<SharedLib>,
|
val sharedLibs: List<SharedLib>,
|
||||||
val targetSdk: Int = 1,
|
val targetSdk: Int = 1,
|
||||||
val downloadedAt: Long = 0
|
val downloadedAt: Long = 0
|
||||||
|
|||||||
@@ -2,7 +2,7 @@ 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.File
|
import com.aurora.gplayapi.data.models.PlayFile
|
||||||
import com.google.gson.Gson
|
import com.google.gson.Gson
|
||||||
import com.google.gson.reflect.TypeToken
|
import com.google.gson.reflect.TypeToken
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
@@ -24,13 +24,13 @@ class DownloadConverter @Inject constructor(private val gson: Gson) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun toGPlayFileList(string: String): List<File> {
|
fun toGPlayFileList(string: String): List<PlayFile> {
|
||||||
val listType = object : TypeToken<List<File>>() {}.type
|
val listType = object : TypeToken<List<PlayFile>>() {}.type
|
||||||
return gson.fromJson(string, listType)
|
return gson.fromJson(string, listType)
|
||||||
}
|
}
|
||||||
|
|
||||||
@TypeConverter
|
@TypeConverter
|
||||||
fun fromGPlayFileList(list: List<File>): String {
|
fun fromGPlayFileList(list: List<PlayFile>): String {
|
||||||
return gson.toJson(list)
|
return gson.toJson(list)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ import androidx.room.Dao
|
|||||||
import androidx.room.Insert
|
import androidx.room.Insert
|
||||||
import androidx.room.OnConflictStrategy
|
import androidx.room.OnConflictStrategy
|
||||||
import androidx.room.Query
|
import androidx.room.Query
|
||||||
import com.aurora.gplayapi.data.models.File
|
import com.aurora.gplayapi.data.models.PlayFile
|
||||||
import com.aurora.store.data.model.DownloadStatus
|
import com.aurora.store.data.model.DownloadStatus
|
||||||
import kotlinx.coroutines.flow.Flow
|
import kotlinx.coroutines.flow.Flow
|
||||||
|
|
||||||
@@ -18,7 +18,7 @@ interface DownloadDao {
|
|||||||
suspend fun updateStatus(packageName: String, downloadStatus: DownloadStatus)
|
suspend fun updateStatus(packageName: String, downloadStatus: DownloadStatus)
|
||||||
|
|
||||||
@Query("UPDATE download SET fileList=:fileList WHERE packageName=:packageName")
|
@Query("UPDATE download SET fileList=:fileList WHERE packageName=:packageName")
|
||||||
suspend fun updateFiles(packageName: String, fileList: List<File>)
|
suspend fun updateFiles(packageName: String, fileList: List<PlayFile>)
|
||||||
|
|
||||||
@Query("UPDATE download SET sharedLibs=:sharedLibs WHERE packageName=:packageName")
|
@Query("UPDATE download SET sharedLibs=:sharedLibs WHERE packageName=:packageName")
|
||||||
suspend fun updateSharedLibs(packageName: String, sharedLibs: List<SharedLib>)
|
suspend fun updateSharedLibs(packageName: String, sharedLibs: List<SharedLib>)
|
||||||
|
|||||||
@@ -2,14 +2,14 @@ package com.aurora.store.data.room.download
|
|||||||
|
|
||||||
import android.os.Parcelable
|
import android.os.Parcelable
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.File
|
import com.aurora.gplayapi.data.models.PlayFile
|
||||||
import kotlinx.parcelize.Parcelize
|
import kotlinx.parcelize.Parcelize
|
||||||
|
|
||||||
@Parcelize
|
@Parcelize
|
||||||
data class SharedLib(
|
data class SharedLib(
|
||||||
val packageName: String,
|
val packageName: String,
|
||||||
val versionCode: Int,
|
val versionCode: Long,
|
||||||
var fileList: List<File>
|
var fileList: List<PlayFile>
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
companion object {
|
companion object {
|
||||||
fun fromApp(app: App): SharedLib {
|
fun fromApp(app: App): SharedLib {
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import android.os.Parcelable
|
|||||||
import androidx.room.Entity
|
import androidx.room.Entity
|
||||||
import androidx.room.PrimaryKey
|
import androidx.room.PrimaryKey
|
||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.File
|
import com.aurora.gplayapi.data.models.PlayFile
|
||||||
import com.aurora.store.data.room.download.SharedLib
|
import com.aurora.store.data.room.download.SharedLib
|
||||||
import com.aurora.store.util.CertUtil
|
import com.aurora.store.util.CertUtil
|
||||||
import com.aurora.store.util.PackageUtil
|
import com.aurora.store.util.PackageUtil
|
||||||
@@ -16,7 +16,7 @@ import kotlinx.parcelize.Parcelize
|
|||||||
data class Update(
|
data class Update(
|
||||||
@PrimaryKey
|
@PrimaryKey
|
||||||
val packageName: String,
|
val packageName: String,
|
||||||
val versionCode: Int,
|
val versionCode: Long,
|
||||||
val versionName: String,
|
val versionName: String,
|
||||||
val displayName: String,
|
val displayName: String,
|
||||||
val iconURL: String,
|
val iconURL: String,
|
||||||
@@ -27,7 +27,7 @@ data class Update(
|
|||||||
val updatedOn: String,
|
val updatedOn: String,
|
||||||
val hasValidCert: Boolean,
|
val hasValidCert: Boolean,
|
||||||
val offerType: Int,
|
val offerType: Int,
|
||||||
var fileList: List<File>,
|
var fileList: List<PlayFile>,
|
||||||
val sharedLibs: List<SharedLib>,
|
val sharedLibs: List<SharedLib>,
|
||||||
val targetSdk: Int = 1
|
val targetSdk: Int = 1
|
||||||
) : Parcelable {
|
) : Parcelable {
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ import com.aurora.extensions.isPAndAbove
|
|||||||
import com.aurora.extensions.isQAndAbove
|
import com.aurora.extensions.isQAndAbove
|
||||||
import com.aurora.extensions.isSAndAbove
|
import com.aurora.extensions.isSAndAbove
|
||||||
import com.aurora.extensions.requiresObbDir
|
import com.aurora.extensions.requiresObbDir
|
||||||
|
import com.aurora.gplayapi.data.models.PlayFile
|
||||||
import com.aurora.gplayapi.helpers.PurchaseHelper
|
import com.aurora.gplayapi.helpers.PurchaseHelper
|
||||||
import com.aurora.gplayapi.network.IHttpClient
|
import com.aurora.gplayapi.network.IHttpClient
|
||||||
import com.aurora.store.AuroraApp
|
import com.aurora.store.AuroraApp
|
||||||
@@ -55,7 +56,6 @@ 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 com.aurora.gplayapi.data.models.File as GPlayFile
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 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.
|
||||||
@@ -127,7 +127,7 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
PathUtil.getObbDownloadDir(download.packageName).mkdirs()
|
PathUtil.getObbDownloadDir(download.packageName).mkdirs()
|
||||||
}
|
}
|
||||||
|
|
||||||
val files = mutableListOf<GPlayFile>()
|
val files = mutableListOf<PlayFile>()
|
||||||
|
|
||||||
// Check if shared libs are present, if yes, handle them first
|
// Check if shared libs are present, if yes, handle them first
|
||||||
if (download.sharedLibs.isNotEmpty()) {
|
if (download.sharedLibs.isNotEmpty()) {
|
||||||
@@ -245,7 +245,7 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
* @param offerType Offer type of the app (free/paid)
|
* @param offerType Offer type of the app (free/paid)
|
||||||
* @return A list of purchased files
|
* @return A list of purchased files
|
||||||
*/
|
*/
|
||||||
private fun purchase(packageName: String, versionCode: Int, offerType: Int): List<GPlayFile> {
|
private fun purchase(packageName: String, versionCode: Long, offerType: Int): List<PlayFile> {
|
||||||
try {
|
try {
|
||||||
// Android 9.0+ supports key rotation, so purchase with latest certificate's hash
|
// Android 9.0+ supports key rotation, so purchase with latest certificate's hash
|
||||||
return if (isPAndAbove && download.isInstalled) {
|
return if (isPAndAbove && download.isInstalled) {
|
||||||
@@ -267,10 +267,10 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
/**
|
/**
|
||||||
* Downloads the file from the given request.
|
* Downloads the file from the given request.
|
||||||
* Failed downloads aren't removed and persists as long as [CacheWorker] doesn't cleans them.
|
* Failed downloads aren't removed and persists as long as [CacheWorker] doesn't cleans them.
|
||||||
* @param gFile A [GPlayFile] to download
|
* @param gFile A [PlayFile] to download
|
||||||
* @return A [Boolean] indicating whether the file was downloaded or not.
|
* @return A [Boolean] indicating whether the file was downloaded or not.
|
||||||
*/
|
*/
|
||||||
private suspend fun downloadFile(packageName: String, gFile: GPlayFile): Boolean {
|
private suspend fun downloadFile(packageName: String, gFile: PlayFile): Boolean {
|
||||||
return withContext(Dispatchers.IO) {
|
return withContext(Dispatchers.IO) {
|
||||||
Log.i(TAG, "Downloading $packageName @ ${gFile.name}")
|
Log.i(TAG, "Downloading $packageName @ ${gFile.name}")
|
||||||
val file = PathUtil.getLocalFile(context, gFile, download)
|
val file = PathUtil.getLocalFile(context, gFile, download)
|
||||||
@@ -414,11 +414,11 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Verifies integrity of a downloaded [GPlayFile].
|
* Verifies integrity of a downloaded [PlayFile].
|
||||||
* @param gFile [GPlayFile] to verify
|
* @param gFile [PlayFile] to verify
|
||||||
*/
|
*/
|
||||||
@OptIn(ExperimentalStdlibApi::class)
|
@OptIn(ExperimentalStdlibApi::class)
|
||||||
private suspend fun verifyFile(gFile: GPlayFile): Boolean {
|
private suspend fun verifyFile(gFile: PlayFile): Boolean {
|
||||||
val file = PathUtil.getLocalFile(context, gFile, download)
|
val file = PathUtil.getLocalFile(context, gFile, download)
|
||||||
Log.i(TAG, "Verifying $file")
|
Log.i(TAG, "Verifying $file")
|
||||||
|
|
||||||
@@ -446,7 +446,7 @@ class DownloadWorker @AssistedInject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun deleteFile(file: GPlayFile) {
|
private fun deleteFile(file: PlayFile) {
|
||||||
val apkFile = PathUtil.getLocalFile(context, file, download)
|
val apkFile = PathUtil.getLocalFile(context, file, download)
|
||||||
if (apkFile.exists()) {
|
if (apkFile.exists()) {
|
||||||
apkFile.delete()
|
apkFile.delete()
|
||||||
|
|||||||
@@ -77,7 +77,7 @@ class ExportWorker @AssistedInject constructor(
|
|||||||
.putString(URI, uri.toString())
|
.putString(URI, uri.toString())
|
||||||
.putString(DISPLAY_NAME, download.displayName)
|
.putString(DISPLAY_NAME, download.displayName)
|
||||||
.putString(PACKAGE_NAME, download.packageName)
|
.putString(PACKAGE_NAME, download.packageName)
|
||||||
.putInt(VERSION_CODE, download.versionCode)
|
.putLong(VERSION_CODE, download.versionCode)
|
||||||
.build()
|
.build()
|
||||||
|
|
||||||
val oneTimeWorkRequest = OneTimeWorkRequestBuilder<ExportWorker>()
|
val oneTimeWorkRequest = OneTimeWorkRequestBuilder<ExportWorker>()
|
||||||
@@ -99,11 +99,11 @@ class ExportWorker @AssistedInject constructor(
|
|||||||
val uri = inputData.getString(URI)!!.toUri()
|
val uri = inputData.getString(URI)!!.toUri()
|
||||||
val packageName = inputData.getString(PACKAGE_NAME)
|
val packageName = inputData.getString(PACKAGE_NAME)
|
||||||
val displayName = inputData.getString(DISPLAY_NAME)
|
val displayName = inputData.getString(DISPLAY_NAME)
|
||||||
val versionCode = inputData.getInt(VERSION_CODE, -1)
|
val versionCode = inputData.getLong(VERSION_CODE, -1)
|
||||||
|
|
||||||
notificationManager = context.getSystemService<NotificationManager>()!!
|
notificationManager = context.getSystemService<NotificationManager>()!!
|
||||||
|
|
||||||
if (packageName.isNullOrEmpty() || isDownload && versionCode == -1) {
|
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)
|
||||||
return Result.failure()
|
return Result.failure()
|
||||||
@@ -156,7 +156,7 @@ class ExportWorker @AssistedInject constructor(
|
|||||||
bundleAllAPKs(fileList.filterNotNull(), uri)
|
bundleAllAPKs(fileList.filterNotNull(), uri)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun copyDownloadedApp(packageName: String, versionCode: Int, uri: Uri) {
|
private fun copyDownloadedApp(packageName: String, versionCode: Long, uri: Uri) {
|
||||||
return bundleAllAPKs(
|
return bundleAllAPKs(
|
||||||
PathUtil.getAppDownloadDir(context, packageName, versionCode).listFiles()!!.toList(),
|
PathUtil.getAppDownloadDir(context, packageName, versionCode).listFiles()!!.toList(),
|
||||||
uri
|
uri
|
||||||
|
|||||||
@@ -51,8 +51,8 @@ object PackageUtil {
|
|||||||
private const val TAG = "PackageUtil"
|
private const val TAG = "PackageUtil"
|
||||||
|
|
||||||
const val PACKAGE_NAME_GMS = "com.google.android.gms"
|
const val PACKAGE_NAME_GMS = "com.google.android.gms"
|
||||||
private const val VERSION_CODE_MICRO_G = 240913402
|
private const val VERSION_CODE_MICRO_G: Long = 240913402
|
||||||
private const val VERSION_CODE_MICRO_G_HUAWEI = 240913007
|
private const val VERSION_CODE_MICRO_G_HUAWEI: Long = 240913007
|
||||||
|
|
||||||
fun getAllValidPackages(context: Context): List<PackageInfo> {
|
fun getAllValidPackages(context: Context): List<PackageInfo> {
|
||||||
return context.packageManager.getInstalledPackages(PackageManager.GET_META_DATA)
|
return context.packageManager.getInstalledPackages(PackageManager.GET_META_DATA)
|
||||||
@@ -85,7 +85,7 @@ object PackageUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isInstalled(context: Context, packageName: String, versionCode: Int): Boolean {
|
fun isInstalled(context: Context, packageName: String, versionCode: Long): Boolean {
|
||||||
return try {
|
return try {
|
||||||
val packageInfo = getPackageInfo(context, packageName)
|
val packageInfo = getPackageInfo(context, packageName)
|
||||||
return PackageInfoCompat.getLongVersionCode(packageInfo) >= versionCode.toLong()
|
return PackageInfoCompat.getLongVersionCode(packageInfo) >= versionCode.toLong()
|
||||||
@@ -110,17 +110,17 @@ object PackageUtil {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun isSharedLibraryInstalled(context: Context, packageName: String, versionCode: Int): Boolean {
|
fun isSharedLibraryInstalled(context: Context, packageName: String, versionCode: Long): Boolean {
|
||||||
return if (isOAndAbove) {
|
return if (isOAndAbove) {
|
||||||
val sharedLibraries = getAllSharedLibraries(context)
|
val sharedLibraries = getAllSharedLibraries(context)
|
||||||
if (isPAndAbove) {
|
if (isPAndAbove) {
|
||||||
sharedLibraries.any {
|
sharedLibraries.any {
|
||||||
it.name == packageName && it.longVersion == versionCode.toLong()
|
it.name == packageName && it.longVersion == versionCode
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
sharedLibraries.any {
|
sharedLibraries.any {
|
||||||
@Suppress("DEPRECATION")
|
@Suppress("DEPRECATION")
|
||||||
it.name == packageName && it.version == versionCode
|
it.name == packageName && it.version == versionCode.toInt()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -21,10 +21,10 @@ package com.aurora.store.util
|
|||||||
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.os.Environment
|
import android.os.Environment
|
||||||
|
import com.aurora.gplayapi.data.models.PlayFile
|
||||||
import com.aurora.store.data.room.download.Download
|
import com.aurora.store.data.room.download.Download
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
import com.aurora.gplayapi.data.models.File as GPlayFile
|
|
||||||
|
|
||||||
object PathUtil {
|
object PathUtil {
|
||||||
|
|
||||||
@@ -47,14 +47,14 @@ object PathUtil {
|
|||||||
return File(getDownloadDirectory(context), packageName)
|
return File(getDownloadDirectory(context), packageName)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getAppDownloadDir(context: Context, packageName: String, versionCode: Int): File {
|
fun getAppDownloadDir(context: Context, packageName: String, versionCode: Long): File {
|
||||||
return File(getPackageDirectory(context, packageName), versionCode.toString())
|
return File(getPackageDirectory(context, packageName), versionCode.toString())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getLibDownloadDir(
|
fun getLibDownloadDir(
|
||||||
context: Context,
|
context: Context,
|
||||||
packageName: String,
|
packageName: String,
|
||||||
versionCode: Int,
|
versionCode: Long,
|
||||||
sharedLibPackageName: String
|
sharedLibPackageName: String
|
||||||
): File {
|
): File {
|
||||||
return File(
|
return File(
|
||||||
@@ -64,15 +64,15 @@ object PathUtil {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns an instance of java's [File] class for the given [GPlayFile]
|
* Returns an instance of java's [File] class for the given [PlayFile]
|
||||||
* @param context [Context]
|
* @param context [Context]
|
||||||
* @param gFile [GPlayFile] to download
|
* @param playFile [PlayFile] to download
|
||||||
* @param download An instance of [Download]
|
* @param download An instance of [Download]
|
||||||
*/
|
*/
|
||||||
fun getLocalFile(context: Context, gFile: GPlayFile, download: Download): File {
|
fun getLocalFile(context: Context, playFile: PlayFile, download: Download): File {
|
||||||
val sharedLib = download.sharedLibs.find { it.fileList.contains(gFile) }
|
val sharedLib = download.sharedLibs.find { it.fileList.contains(playFile) }
|
||||||
return when (gFile.type) {
|
return when (playFile.type) {
|
||||||
GPlayFile.FileType.BASE, GPlayFile.FileType.SPLIT -> {
|
PlayFile.Type.BASE, PlayFile.Type.SPLIT -> {
|
||||||
val downloadDir = if (sharedLib != null) {
|
val downloadDir = if (sharedLib != null) {
|
||||||
getLibDownloadDir(
|
getLibDownloadDir(
|
||||||
context,
|
context,
|
||||||
@@ -83,16 +83,16 @@ object PathUtil {
|
|||||||
} else {
|
} else {
|
||||||
getAppDownloadDir(context, download.packageName, download.versionCode)
|
getAppDownloadDir(context, download.packageName, download.versionCode)
|
||||||
}
|
}
|
||||||
return File(downloadDir, gFile.name)
|
return File(downloadDir, playFile.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
GPlayFile.FileType.OBB, GPlayFile.FileType.PATCH -> {
|
PlayFile.Type.OBB, PlayFile.Type.PATCH -> {
|
||||||
File(getObbDownloadDir(download.packageName), gFile.name)
|
File(getObbDownloadDir(download.packageName), playFile.name)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun getZipFile(context: Context, packageName: String, versionCode: Int): File {
|
fun getZipFile(context: Context, packageName: String, versionCode: Long): File {
|
||||||
return File(
|
return File(
|
||||||
getAppDownloadDir(
|
getAppDownloadDir(
|
||||||
context,
|
context,
|
||||||
|
|||||||
@@ -23,7 +23,7 @@ import android.content.Context
|
|||||||
import android.util.AttributeSet
|
import android.util.AttributeSet
|
||||||
import com.airbnb.epoxy.ModelProp
|
import com.airbnb.epoxy.ModelProp
|
||||||
import com.airbnb.epoxy.ModelView
|
import com.airbnb.epoxy.ModelView
|
||||||
import com.aurora.gplayapi.data.models.File
|
import com.aurora.gplayapi.data.models.PlayFile
|
||||||
import com.aurora.store.databinding.ViewFileBinding
|
import com.aurora.store.databinding.ViewFileBinding
|
||||||
import com.aurora.store.util.CommonUtil
|
import com.aurora.store.util.CommonUtil
|
||||||
import com.aurora.store.view.epoxy.views.BaseModel
|
import com.aurora.store.view.epoxy.views.BaseModel
|
||||||
@@ -40,7 +40,7 @@ class FileView @JvmOverloads constructor(
|
|||||||
) : BaseView<ViewFileBinding>(context, attrs, defStyleAttr) {
|
) : BaseView<ViewFileBinding>(context, attrs, defStyleAttr) {
|
||||||
|
|
||||||
@ModelProp
|
@ModelProp
|
||||||
fun file(file: File) {
|
fun file(file: PlayFile) {
|
||||||
binding.line1.text = file.name
|
binding.line1.text = file.name
|
||||||
binding.line2.text = CommonUtil.addSiPrefix(file.size)
|
binding.line2.text = CommonUtil.addSiPrefix(file.size)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -27,8 +27,6 @@ import androidx.navigation.fragment.navArgs
|
|||||||
import com.aurora.gplayapi.data.models.App
|
import com.aurora.gplayapi.data.models.App
|
||||||
import com.aurora.gplayapi.data.models.StreamBundle
|
import com.aurora.gplayapi.data.models.StreamBundle
|
||||||
import com.aurora.gplayapi.data.models.StreamCluster
|
import com.aurora.gplayapi.data.models.StreamCluster
|
||||||
import com.aurora.gplayapi.helpers.contracts.StreamContract
|
|
||||||
import com.aurora.gplayapi.utils.CategoryUtil
|
|
||||||
import com.aurora.store.data.model.ViewState
|
import com.aurora.store.data.model.ViewState
|
||||||
import com.aurora.store.data.model.ViewState.Loading.getDataAs
|
import com.aurora.store.data.model.ViewState.Loading.getDataAs
|
||||||
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
|
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
|
||||||
@@ -44,14 +42,11 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
|
|||||||
private val args: CategoryBrowseFragmentArgs by navArgs()
|
private val args: CategoryBrowseFragmentArgs by navArgs()
|
||||||
private val viewModel: CategoryStreamViewModel by activityViewModels()
|
private val viewModel: CategoryStreamViewModel by activityViewModels()
|
||||||
|
|
||||||
private lateinit var category: StreamContract.Category
|
|
||||||
private var streamBundle: StreamBundle? = StreamBundle()
|
private var streamBundle: StreamBundle? = StreamBundle()
|
||||||
|
|
||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
category = CategoryUtil.getCategoryFromUrl(args.browseUrl)
|
|
||||||
|
|
||||||
val genericCarouselController = CategoryCarouselController(this)
|
val genericCarouselController = CategoryCarouselController(this)
|
||||||
|
|
||||||
// Toolbar
|
// Toolbar
|
||||||
@@ -64,11 +59,11 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
|
|||||||
binding.recycler.setController(genericCarouselController)
|
binding.recycler.setController(genericCarouselController)
|
||||||
binding.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
|
binding.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
|
||||||
override fun onLoadMore(currentPage: Int) {
|
override fun onLoadMore(currentPage: Int) {
|
||||||
viewModel.observe(category)
|
viewModel.observe(args.browseUrl)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
viewModel.getStreamBundle(category)
|
viewModel.getStreamBundle(args.browseUrl)
|
||||||
viewModel.liveData.observe(viewLifecycleOwner) {
|
viewModel.liveData.observe(viewLifecycleOwner) {
|
||||||
when (it) {
|
when (it) {
|
||||||
is ViewState.Loading -> {
|
is ViewState.Loading -> {
|
||||||
@@ -77,7 +72,7 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
|
|||||||
|
|
||||||
is ViewState.Success<*> -> {
|
is ViewState.Success<*> -> {
|
||||||
val stash = it.getDataAs<Map<String, StreamBundle>>()
|
val stash = it.getDataAs<Map<String, StreamBundle>>()
|
||||||
streamBundle = stash[category.value]
|
streamBundle = stash[args.browseUrl]
|
||||||
|
|
||||||
genericCarouselController.setData(streamBundle)
|
genericCarouselController.setData(streamBundle)
|
||||||
}
|
}
|
||||||
@@ -92,7 +87,7 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
override fun onClusterScrolled(streamCluster: StreamCluster) {
|
||||||
viewModel.observeCluster(category, streamCluster)
|
viewModel.observeCluster(args.browseUrl, streamCluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onAppClick(app: App) {
|
override fun onAppClick(app: App) {
|
||||||
|
|||||||
@@ -136,8 +136,8 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
val requestedApp = app.copy(
|
val requestedApp = app.copy(
|
||||||
versionCode = event.versionCode,
|
versionCode = event.versionCode,
|
||||||
dependencies = app.dependencies.copy(
|
dependencies = app.dependencies.copy(
|
||||||
dependentLibraries = app.dependencies.dependentLibraries.onEach { lib ->
|
dependentLibraries = app.dependencies.dependentLibraries.map { lib ->
|
||||||
lib.versionCode = event.versionCode
|
lib.copy(versionCode = event.versionCode)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
@@ -173,11 +173,10 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||||
super.onViewCreated(view, savedInstanceState)
|
super.onViewCreated(view, savedInstanceState)
|
||||||
|
|
||||||
app = args.app ?: App(args.packageName)
|
app = args.app ?: App(
|
||||||
app.apply {
|
packageName = args.packageName,
|
||||||
// Check whether app is installed or not
|
|
||||||
isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
|
isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
|
||||||
}
|
)
|
||||||
|
|
||||||
// Show the basic app details, while the rest of the data is being fetched
|
// Show the basic app details, while the rest of the data is being fetched
|
||||||
updateAppHeader(app, false)
|
updateAppHeader(app, false)
|
||||||
@@ -204,7 +203,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
updateExtraDetails(app)
|
updateExtraDetails(app)
|
||||||
updateCompatibilityInfo()
|
updateCompatibilityInfo()
|
||||||
|
|
||||||
if (app.versionCode == 0) {
|
if (app.versionCode == 0L) {
|
||||||
warnAppUnavailable(app)
|
warnAppUnavailable(app)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -607,7 +606,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun checkAndSetupInstall() {
|
private fun checkAndSetupInstall() {
|
||||||
app.isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
|
app = app.copy(isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName))
|
||||||
|
|
||||||
// Setup primary and secondary action buttons
|
// Setup primary and secondary action buttons
|
||||||
binding.layoutDetailsApp.btnPrimaryAction.isEnabled = true
|
binding.layoutDetailsApp.btnPrimaryAction.isEnabled = true
|
||||||
@@ -634,7 +633,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
binding.layoutDetailsApp.btnPrimaryAction.apply {
|
binding.layoutDetailsApp.btnPrimaryAction.apply {
|
||||||
text = getString(R.string.action_update)
|
text = getString(R.string.action_update)
|
||||||
setOnClickListener {
|
setOnClickListener {
|
||||||
if (app.versionCode == 0) {
|
if (app.versionCode == 0L) {
|
||||||
toast(R.string.toast_app_unavailable)
|
toast(R.string.toast_app_unavailable)
|
||||||
setText(R.string.status_unavailable)
|
setText(R.string.status_unavailable)
|
||||||
} else {
|
} else {
|
||||||
@@ -692,7 +691,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
|
|||||||
if (viewModel.authProvider.isAnonymous && !app.isFree) {
|
if (viewModel.authProvider.isAnonymous && !app.isFree) {
|
||||||
toast(R.string.toast_purchase_blocked)
|
toast(R.string.toast_purchase_blocked)
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
} else if (app.versionCode == 0) {
|
} else if (app.versionCode == 0L) {
|
||||||
toast(R.string.toast_app_unavailable)
|
toast(R.string.toast_app_unavailable)
|
||||||
return@setOnClickListener
|
return@setOnClickListener
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -95,7 +95,7 @@ class ManualDownloadSheet : BaseDialogSheet<SheetManualDownloadBinding>() {
|
|||||||
if (customVersionString.isEmpty())
|
if (customVersionString.isEmpty())
|
||||||
binding.versionCodeInp.error = "Enter version code"
|
binding.versionCodeInp.error = "Enter version code"
|
||||||
else {
|
else {
|
||||||
viewModel.purchase(args.app, customVersionString.toInt())
|
viewModel.purchase(args.app, customVersionString.toLong())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -67,10 +67,10 @@ class ExpandedStreamBrowseViewModel @Inject constructor(
|
|||||||
streamCluster.clusterNextPageUrl
|
streamCluster.clusterNextPageUrl
|
||||||
)
|
)
|
||||||
|
|
||||||
streamCluster.apply {
|
streamCluster = streamCluster.copy(
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
clusterAppList = streamCluster.clusterAppList + newCluster.clusterAppList,
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
||||||
}
|
)
|
||||||
|
|
||||||
liveData.postValue(streamCluster)
|
liveData.postValue(streamCluster)
|
||||||
|
|
||||||
|
|||||||
@@ -56,10 +56,10 @@ class StreamBrowseViewModel @Inject constructor(
|
|||||||
streamCluster.clusterNextPageUrl
|
streamCluster.clusterNextPageUrl
|
||||||
)
|
)
|
||||||
|
|
||||||
streamCluster.apply {
|
streamCluster = streamCluster.copy(
|
||||||
clusterAppList.addAll(nextCluster.clusterAppList)
|
clusterNextPageUrl = nextCluster.clusterNextPageUrl,
|
||||||
clusterNextPageUrl = nextCluster.clusterNextPageUrl
|
clusterAppList = streamCluster.clusterAppList + nextCluster.clusterAppList
|
||||||
}
|
)
|
||||||
|
|
||||||
liveData.postValue(streamCluster)
|
liveData.postValue(streamCluster)
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -96,9 +96,9 @@ class AppDetailsViewModel @Inject constructor(
|
|||||||
checkFavourite(packageName)
|
checkFavourite(packageName)
|
||||||
|
|
||||||
val app: App = appStash.getOrPut(packageName) {
|
val app: App = appStash.getOrPut(packageName) {
|
||||||
appDetailsHelper.getAppByPackageName(packageName).apply {
|
appDetailsHelper.getAppByPackageName(packageName).copy(
|
||||||
isInstalled = PackageUtil.isInstalled(context, packageName)
|
isInstalled = PackageUtil.isInstalled(context, packageName)
|
||||||
}
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
_app.emit(app)
|
_app.emit(app)
|
||||||
|
|||||||
@@ -62,10 +62,11 @@ class DetailsClusterViewModel @Inject constructor(
|
|||||||
if (!bundle.hasCluster() || bundle.hasNext()) {
|
if (!bundle.hasCluster() || bundle.hasNext()) {
|
||||||
val newBundle = appDetailsHelper.getDetailsStream(streamUrl)
|
val newBundle = appDetailsHelper.getDetailsStream(streamUrl)
|
||||||
|
|
||||||
bundle.apply {
|
val mergedBundle = bundle.copy(
|
||||||
streamClusters.putAll(newBundle.streamClusters)
|
streamClusters = bundle.streamClusters + newBundle.streamClusters,
|
||||||
streamNextPageUrl = newBundle.streamNextPageUrl
|
streamNextPageUrl = newBundle.streamNextPageUrl
|
||||||
}
|
)
|
||||||
|
stash[streamUrl] = mergedBundle
|
||||||
|
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
}
|
}
|
||||||
@@ -87,7 +88,6 @@ class DetailsClusterViewModel @Inject constructor(
|
|||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "End of cluster")
|
Log.i(TAG, "End of cluster")
|
||||||
streamCluster.clusterNextPageUrl = String()
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
liveData.postValue(ViewState.Error(e.message))
|
liveData.postValue(ViewState.Error(e.message))
|
||||||
@@ -101,9 +101,18 @@ class DetailsClusterViewModel @Inject constructor(
|
|||||||
clusterID: Int,
|
clusterID: Int,
|
||||||
newCluster: StreamCluster
|
newCluster: StreamCluster
|
||||||
) {
|
) {
|
||||||
targetBundle(url).streamClusters[clusterID]?.apply {
|
val bundle = targetBundle(url)
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
bundle.streamClusters[clusterID]?.let { oldCluster ->
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
val mergedCluster = oldCluster.copy(
|
||||||
|
clusterNextPageUrl = newCluster.clusterNextPageUrl,
|
||||||
|
clusterAppList = oldCluster.clusterAppList + newCluster.clusterAppList
|
||||||
|
)
|
||||||
|
val newStreamClusters = bundle.streamClusters.toMutableMap().also {
|
||||||
|
it.remove(clusterID)
|
||||||
|
it[clusterID] = mergedCluster
|
||||||
|
}
|
||||||
|
|
||||||
|
stash.put(url, bundle.copy(streamClusters = newStreamClusters))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -76,7 +76,6 @@ class DevProfileViewModel @Inject constructor(
|
|||||||
liveData.postValue(ViewState.Success(devStream))
|
liveData.postValue(ViewState.Success(devStream))
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "End of cluster")
|
Log.i(TAG, "End of cluster")
|
||||||
streamCluster.clusterNextPageUrl = String()
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
liveData.postValue(ViewState.Error(e.message))
|
liveData.postValue(ViewState.Error(e.message))
|
||||||
@@ -86,9 +85,17 @@ class DevProfileViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCluster(newCluster: StreamCluster) {
|
private fun updateCluster(newCluster: StreamCluster) {
|
||||||
streamBundle.streamClusters[newCluster.id]?.apply {
|
streamBundle.streamClusters[newCluster.id]?.let { oldCluster ->
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
val mergedCluster = oldCluster.copy(
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
clusterNextPageUrl = newCluster.clusterNextPageUrl,
|
||||||
|
clusterAppList = oldCluster.clusterAppList + newCluster.clusterAppList
|
||||||
|
)
|
||||||
|
val newStreamClusters = streamBundle.streamClusters.toMutableMap().also {
|
||||||
|
it.remove(newCluster.id)
|
||||||
|
it[newCluster.id] = mergedCluster
|
||||||
|
}
|
||||||
|
|
||||||
|
streamBundle = streamBundle.copy(streamClusters = newStreamClusters)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -76,10 +76,11 @@ class StreamViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
//Update old bundle
|
//Update old bundle
|
||||||
bundle.apply {
|
val mergedBundle = bundle.copy(
|
||||||
streamClusters.putAll(newBundle.streamClusters)
|
streamClusters = bundle.streamClusters + newBundle.streamClusters,
|
||||||
streamNextPageUrl = newBundle.streamNextPageUrl
|
streamNextPageUrl = newBundle.streamNextPageUrl
|
||||||
}
|
)
|
||||||
|
stash[category] = mergedBundle
|
||||||
|
|
||||||
//Post updated to UI
|
//Post updated to UI
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
@@ -98,13 +99,13 @@ class StreamViewModel @Inject constructor(
|
|||||||
supervisorScope {
|
supervisorScope {
|
||||||
try {
|
try {
|
||||||
if (streamCluster.hasNext()) {
|
if (streamCluster.hasNext()) {
|
||||||
val newCluster =
|
val newCluster = streamContract.nextStreamCluster(
|
||||||
streamContract.nextStreamCluster(streamCluster.clusterNextPageUrl)
|
streamCluster.clusterNextPageUrl
|
||||||
|
)
|
||||||
updateCluster(category, streamCluster.id, newCluster)
|
updateCluster(category, streamCluster.id, newCluster)
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "End of cluster")
|
Log.i(TAG, "End of cluster")
|
||||||
streamCluster.clusterNextPageUrl = String()
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
liveData.postValue(ViewState.Error(e.message))
|
liveData.postValue(ViewState.Error(e.message))
|
||||||
@@ -118,9 +119,18 @@ class StreamViewModel @Inject constructor(
|
|||||||
clusterID: Int,
|
clusterID: Int,
|
||||||
newCluster: StreamCluster
|
newCluster: StreamCluster
|
||||||
) {
|
) {
|
||||||
targetBundle(category).streamClusters[clusterID]?.apply {
|
val bundle = targetBundle(category)
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
bundle.streamClusters[clusterID]?.let { oldCluster ->
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
val mergedCluster = oldCluster.copy(
|
||||||
|
clusterNextPageUrl = newCluster.clusterNextPageUrl,
|
||||||
|
clusterAppList = oldCluster.clusterAppList + newCluster.clusterAppList
|
||||||
|
)
|
||||||
|
val newStreamClusters = bundle.streamClusters.toMutableMap().also {
|
||||||
|
it.remove(clusterID)
|
||||||
|
it[clusterID] = mergedCluster
|
||||||
|
}
|
||||||
|
|
||||||
|
stash.put(category, bundle.copy(streamClusters = newStreamClusters))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -57,11 +57,10 @@ class ReviewViewModel @Inject constructor(
|
|||||||
supervisorScope {
|
supervisorScope {
|
||||||
try {
|
try {
|
||||||
val nextReviewCluster = reviewsHelper.next(nextReviewPageUrl)
|
val nextReviewCluster = reviewsHelper.next(nextReviewPageUrl)
|
||||||
reviewsCluster.copy(
|
reviewsCluster = reviewsCluster.copy(
|
||||||
nextPageUrl = nextReviewCluster.nextPageUrl
|
nextPageUrl = nextReviewCluster.nextPageUrl,
|
||||||
).apply {
|
reviewList = nextReviewCluster.reviewList + nextReviewCluster.reviewList
|
||||||
reviewList.addAll(nextReviewCluster.reviewList)
|
)
|
||||||
}
|
|
||||||
|
|
||||||
liveData.postValue(reviewsCluster)
|
liveData.postValue(reviewsCluster)
|
||||||
} catch (_: Exception) {
|
} catch (_: Exception) {
|
||||||
|
|||||||
@@ -23,7 +23,6 @@ import android.util.Log
|
|||||||
import androidx.lifecycle.MutableLiveData
|
import androidx.lifecycle.MutableLiveData
|
||||||
import androidx.lifecycle.ViewModel
|
import androidx.lifecycle.ViewModel
|
||||||
import androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.aurora.extensions.flushAndAdd
|
|
||||||
import com.aurora.gplayapi.data.models.SearchBundle
|
import com.aurora.gplayapi.data.models.SearchBundle
|
||||||
import com.aurora.gplayapi.helpers.SearchHelper
|
import com.aurora.gplayapi.helpers.SearchHelper
|
||||||
import com.aurora.gplayapi.helpers.contracts.SearchContract
|
import com.aurora.gplayapi.helpers.contracts.SearchContract
|
||||||
@@ -54,10 +53,6 @@ class SearchResultViewModel @Inject constructor(
|
|||||||
get() = if (authProvider.isAnonymous) webSearchHelper else searchHelper
|
get() = if (authProvider.isAnonymous) webSearchHelper else searchHelper
|
||||||
|
|
||||||
fun observeSearchResults(query: String) {
|
fun observeSearchResults(query: String) {
|
||||||
//Clear old results
|
|
||||||
searchBundle.subBundles.clear()
|
|
||||||
searchBundle.appList.clear()
|
|
||||||
//Fetch new results
|
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
try {
|
try {
|
||||||
@@ -75,17 +70,17 @@ class SearchResultViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Synchronized
|
@Synchronized
|
||||||
fun next(nextSubBundleSet: MutableSet<SearchBundle.SubBundle>) {
|
fun next(nextSubBundleSet: Set<SearchBundle.SubBundle>) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
try {
|
try {
|
||||||
if (nextSubBundleSet.isNotEmpty()) {
|
if (nextSubBundleSet.isNotEmpty()) {
|
||||||
val newSearchBundle = helper.next(nextSubBundleSet)
|
val newSearchBundle = helper.next(nextSubBundleSet.toMutableSet())
|
||||||
if (newSearchBundle.appList.isNotEmpty()) {
|
if (newSearchBundle.appList.isNotEmpty()) {
|
||||||
searchBundle.apply {
|
searchBundle = searchBundle.copy(
|
||||||
subBundles.flushAndAdd(newSearchBundle.subBundles)
|
subBundles = newSearchBundle.subBundles,
|
||||||
appList.addAll(newSearchBundle.appList)
|
appList = searchBundle.appList + newSearchBundle.appList
|
||||||
}
|
)
|
||||||
|
|
||||||
liveData.postValue(searchBundle)
|
liveData.postValue(searchBundle)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -24,7 +24,7 @@ class ManualDownloadViewModel @Inject constructor(
|
|||||||
private val _purchaseStatus = MutableSharedFlow<Boolean>()
|
private val _purchaseStatus = MutableSharedFlow<Boolean>()
|
||||||
val purchaseStatus = _purchaseStatus.asSharedFlow()
|
val purchaseStatus = _purchaseStatus.asSharedFlow()
|
||||||
|
|
||||||
fun purchase(app: App, customVersion: Int) {
|
fun purchase(app: App, customVersion: Long) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
try {
|
try {
|
||||||
val files = purchaseHelper.purchase(app.packageName, customVersion, app.offerType)
|
val files = purchaseHelper.purchase(app.packageName, customVersion, app.offerType)
|
||||||
|
|||||||
@@ -49,38 +49,38 @@ class CategoryStreamViewModel @Inject constructor(
|
|||||||
private val categoryStreamContract: CategoryStreamContract
|
private val categoryStreamContract: CategoryStreamContract
|
||||||
get() = webCategoryStreamHelper
|
get() = webCategoryStreamHelper
|
||||||
|
|
||||||
fun getStreamBundle(category: StreamContract.Category) {
|
fun getStreamBundle(browseUrl: String) {
|
||||||
liveData.postValue(ViewState.Loading)
|
liveData.postValue(ViewState.Loading)
|
||||||
observe(category)
|
observe(browseUrl)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observe(category: StreamContract.Category) {
|
fun observe(browseUrl: String) {
|
||||||
liveData.postValue(ViewState.Loading)
|
liveData.postValue(ViewState.Loading)
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
val bundle = targetBundle(category)
|
val bundle = targetBundle(browseUrl)
|
||||||
if (bundle.streamClusters.isNotEmpty()) {
|
if (bundle.streamClusters.isNotEmpty()) {
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
}
|
}
|
||||||
|
|
||||||
try {
|
try {
|
||||||
if (!bundle.hasCluster() || bundle.hasNext()) {
|
if (!bundle.hasCluster() || bundle.hasNext()) {
|
||||||
|
|
||||||
//Fetch new stream bundle
|
//Fetch new stream bundle
|
||||||
val newBundle = if (bundle.streamClusters.isEmpty()) {
|
val newBundle = if (bundle.streamClusters.isEmpty()) {
|
||||||
categoryStreamContract.fetch(category.value)
|
categoryStreamContract.fetch(browseUrl)
|
||||||
} else {
|
} else {
|
||||||
categoryStreamContract.nextStreamBundle(
|
categoryStreamContract.nextStreamBundle(
|
||||||
category,
|
StreamContract.Category.NONE,
|
||||||
bundle.streamNextPageUrl
|
bundle.streamNextPageUrl
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
//Update old bundle
|
//Update old bundle
|
||||||
bundle.apply {
|
val mergedBundle = bundle.copy(
|
||||||
streamClusters.putAll(newBundle.streamClusters)
|
streamClusters = bundle.streamClusters + newBundle.streamClusters,
|
||||||
streamNextPageUrl = newBundle.streamNextPageUrl
|
streamNextPageUrl = newBundle.streamNextPageUrl
|
||||||
}
|
)
|
||||||
|
stash[browseUrl] = mergedBundle
|
||||||
|
|
||||||
//Post updated to UI
|
//Post updated to UI
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
@@ -94,18 +94,18 @@ class CategoryStreamViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun observeCluster(category: StreamContract.Category, streamCluster: StreamCluster) {
|
fun observeCluster(browseUrl: String, streamCluster: StreamCluster) {
|
||||||
viewModelScope.launch(Dispatchers.IO) {
|
viewModelScope.launch(Dispatchers.IO) {
|
||||||
supervisorScope {
|
supervisorScope {
|
||||||
try {
|
try {
|
||||||
if (streamCluster.hasNext()) {
|
if (streamCluster.hasNext()) {
|
||||||
val newCluster =
|
val newCluster = categoryStreamContract.nextStreamCluster(
|
||||||
categoryStreamContract.nextStreamCluster(streamCluster.clusterNextPageUrl)
|
streamCluster.clusterNextPageUrl
|
||||||
updateCluster(category, streamCluster.id, newCluster)
|
)
|
||||||
|
updateCluster(browseUrl, streamCluster.id, newCluster)
|
||||||
liveData.postValue(ViewState.Success(stash))
|
liveData.postValue(ViewState.Success(stash))
|
||||||
} else {
|
} else {
|
||||||
Log.i(TAG, "End of cluster")
|
Log.i(TAG, "End of cluster")
|
||||||
streamCluster.clusterNextPageUrl = String()
|
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
liveData.postValue(ViewState.Error(e.message))
|
liveData.postValue(ViewState.Error(e.message))
|
||||||
@@ -114,22 +114,24 @@ class CategoryStreamViewModel @Inject constructor(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun updateCluster(
|
private fun updateCluster(browseUrl: String, clusterID: Int, newCluster: StreamCluster) {
|
||||||
category: StreamContract.Category,
|
val bundle = targetBundle(browseUrl)
|
||||||
clusterID: Int,
|
bundle.streamClusters[clusterID]?.let { oldCluster ->
|
||||||
newCluster: StreamCluster
|
val mergedCluster = oldCluster.copy(
|
||||||
) {
|
clusterNextPageUrl = newCluster.clusterNextPageUrl,
|
||||||
targetBundle(category).streamClusters[clusterID]?.apply {
|
clusterAppList = oldCluster.clusterAppList + newCluster.clusterAppList
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
)
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
val newStreamClusters = bundle.streamClusters.toMutableMap().also {
|
||||||
|
it.remove(clusterID)
|
||||||
|
it[clusterID] = mergedCluster
|
||||||
|
}
|
||||||
|
|
||||||
|
stash.put(browseUrl, bundle.copy(streamClusters = newStreamClusters))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun targetBundle(category: StreamContract.Category): StreamBundle {
|
private fun targetBundle(browseUrl: String): StreamBundle {
|
||||||
val streamBundle = stash.getOrPut(category.value) {
|
val streamBundle = stash.getOrPut(browseUrl) { StreamBundle() }
|
||||||
StreamBundle()
|
|
||||||
}
|
|
||||||
|
|
||||||
return streamBundle
|
return streamBundle
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -85,10 +85,13 @@ class TopChartViewModel @Inject constructor(
|
|||||||
chart: TopChartsContract.Chart,
|
chart: TopChartsContract.Chart,
|
||||||
newCluster: StreamCluster
|
newCluster: StreamCluster
|
||||||
) {
|
) {
|
||||||
targetCluster(type, chart).apply {
|
val streamCluster = targetCluster(type, chart)
|
||||||
clusterAppList.addAll(newCluster.clusterAppList)
|
val mergedCluster = streamCluster.copy(
|
||||||
clusterNextPageUrl = newCluster.clusterNextPageUrl
|
clusterNextPageUrl = newCluster.clusterNextPageUrl,
|
||||||
}
|
clusterAppList = streamCluster.clusterAppList + newCluster.clusterAppList
|
||||||
|
)
|
||||||
|
|
||||||
|
stash[type] = mutableMapOf(chart to mergedCluster)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun targetCluster(
|
private fun targetCluster(
|
||||||
|
|||||||
@@ -9,7 +9,7 @@ composeBom = "2025.05.00"
|
|||||||
core = "1.16.0"
|
core = "1.16.0"
|
||||||
epoxy = "5.1.4"
|
epoxy = "5.1.4"
|
||||||
espresso = "3.6.1"
|
espresso = "3.6.1"
|
||||||
gplayapi = "3.4.6"
|
gplayapi = "3.5.0"
|
||||||
gson = "2.13.1"
|
gson = "2.13.1"
|
||||||
hiddenapibypass = "6.1"
|
hiddenapibypass = "6.1"
|
||||||
hilt = "2.56.2"
|
hilt = "2.56.2"
|
||||||
|
|||||||
Reference in New Issue
Block a user