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:
Aayush Gupta
2025-05-13 15:14:54 +08:00
parent a2a9dbfdfe
commit e39bc3b8a5
36 changed files with 182 additions and 194 deletions

View File

@@ -26,7 +26,7 @@ sealed class BusEvent : Event() {
lateinit var error: String
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() {

View File

@@ -107,7 +107,7 @@ class DownloadHelper @Inject constructor(
* @param packageName Name of the package of the app
* @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)")
downloadDao.delete(packageName)
PathUtil.getAppDownloadDir(context, packageName, versionCode)

View File

@@ -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
for (file in getFiles(packageName, versionCode, sharedLibPkgName))

View File

@@ -183,7 +183,7 @@ class SessionInstaller @Inject constructor(
private fun stageInstall(
packageName: String,
versionCode: Int,
versionCode: Long,
sharedLibPkgName: String = ""
): Int? {
val resolvedPackageName = sharedLibPkgName.ifBlank { packageName }

View File

@@ -129,7 +129,7 @@ class ShizukuInstaller @Inject constructor(
private fun install(
packageName: String,
versionCode: Int,
versionCode: Long,
sharedLibPkgName: String = "",
displayName: String = ""
) {

View File

@@ -103,7 +103,7 @@ abstract class InstallerBase(private val context: Context) : IInstaller {
fun getFiles(
packageName: String,
versionCode: Int,
versionCode: Long,
sharedLibPackageName: String = ""
): List<File> {
val downloadDir = if (sharedLibPackageName.isNotBlank()) {

View File

@@ -1,13 +1,9 @@
package com.aurora.store.data.model
import android.content.Context
import android.content.pm.PackageInfo
import android.graphics.Bitmap
import android.os.Parcelable
import androidx.core.content.pm.PackageInfoCompat
import com.aurora.gplayapi.data.models.App
import com.aurora.store.data.room.update.Update
import com.aurora.store.util.PackageUtil
import kotlinx.parcelize.IgnoredOnParcel
import kotlinx.parcelize.Parcelize
@@ -15,7 +11,7 @@ import kotlinx.parcelize.Parcelize
data class MinimalApp(
val packageName: String,
val versionName: String,
val versionCode: Int,
val versionCode: Long,
val displayName: String,
@IgnoredOnParcel
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 {
return MinimalApp(
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)
)
}
}
}

View File

@@ -23,7 +23,7 @@ import android.content.Context
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.Artwork
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.R
import com.aurora.store.util.CertUtil
@@ -31,7 +31,7 @@ import com.google.gson.annotations.SerializedName
data class SelfUpdate(
@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("fdroid_build") var fdroidBuild: String = String(),
@SerializedName("updated_on") var updatedOn: String = String(),
@@ -63,7 +63,7 @@ data class SelfUpdate(
developerName = "Rahul Kumar Patel",
iconArtwork = Artwork(url = "$BASE_URL/$icon"),
fileList = mutableListOf(
File(
PlayFile(
name = "${context.packageName}.apk",
url = downloadURL,
size = selfUpdate.size

View File

@@ -3,6 +3,6 @@ package com.aurora.store.data.model
data class SessionInfo(
val sessionId: Int,
val packageName: String,
val versionCode: Int,
val versionCode: Long,
val displayName: String = String()
)

View File

@@ -52,7 +52,7 @@ class InstallerStatusReceiver : BroadcastReceiver() {
if (context != null && intent?.action == ACTION_INSTALL_STATUS) {
val packageName = intent.getStringExtra(EXTRA_PACKAGE_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 extra = intent.getStringExtra(PackageInstaller.EXTRA_STATUS_MESSAGE)

View File

@@ -4,7 +4,7 @@ import android.os.Parcelable
import androidx.room.Entity
import androidx.room.PrimaryKey
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.room.update.Update
import kotlinx.parcelize.Parcelize
@@ -14,7 +14,7 @@ import java.util.Date
@Entity(tableName = "download")
data class Download(
@PrimaryKey val packageName: String,
val versionCode: Int,
val versionCode: Long,
val offerType: Int,
val isInstalled: Boolean,
val displayName: String,
@@ -27,7 +27,7 @@ data class Download(
var timeRemaining: Long,
var totalFiles: Int,
var downloadedFiles: Int,
var fileList: List<File>,
var fileList: List<PlayFile>,
val sharedLibs: List<SharedLib>,
val targetSdk: Int = 1,
val downloadedAt: Long = 0

View File

@@ -2,7 +2,7 @@ package com.aurora.store.data.room.download
import androidx.room.ProvidedTypeConverter
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.reflect.TypeToken
import javax.inject.Inject
@@ -24,13 +24,13 @@ class DownloadConverter @Inject constructor(private val gson: Gson) {
}
@TypeConverter
fun toGPlayFileList(string: String): List<File> {
val listType = object : TypeToken<List<File>>() {}.type
fun toGPlayFileList(string: String): List<PlayFile> {
val listType = object : TypeToken<List<PlayFile>>() {}.type
return gson.fromJson(string, listType)
}
@TypeConverter
fun fromGPlayFileList(list: List<File>): String {
fun fromGPlayFileList(list: List<PlayFile>): String {
return gson.toJson(list)
}
}

View File

@@ -4,7 +4,7 @@ import androidx.room.Dao
import androidx.room.Insert
import androidx.room.OnConflictStrategy
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 kotlinx.coroutines.flow.Flow
@@ -18,7 +18,7 @@ interface DownloadDao {
suspend fun updateStatus(packageName: String, downloadStatus: DownloadStatus)
@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")
suspend fun updateSharedLibs(packageName: String, sharedLibs: List<SharedLib>)

View File

@@ -2,14 +2,14 @@ package com.aurora.store.data.room.download
import android.os.Parcelable
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
@Parcelize
data class SharedLib(
val packageName: String,
val versionCode: Int,
var fileList: List<File>
val versionCode: Long,
var fileList: List<PlayFile>
) : Parcelable {
companion object {
fun fromApp(app: App): SharedLib {

View File

@@ -5,7 +5,7 @@ import android.os.Parcelable
import androidx.room.Entity
import androidx.room.PrimaryKey
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.util.CertUtil
import com.aurora.store.util.PackageUtil
@@ -16,7 +16,7 @@ import kotlinx.parcelize.Parcelize
data class Update(
@PrimaryKey
val packageName: String,
val versionCode: Int,
val versionCode: Long,
val versionName: String,
val displayName: String,
val iconURL: String,
@@ -27,7 +27,7 @@ data class Update(
val updatedOn: String,
val hasValidCert: Boolean,
val offerType: Int,
var fileList: List<File>,
var fileList: List<PlayFile>,
val sharedLibs: List<SharedLib>,
val targetSdk: Int = 1
) : Parcelable {

View File

@@ -24,6 +24,7 @@ import com.aurora.extensions.isPAndAbove
import com.aurora.extensions.isQAndAbove
import com.aurora.extensions.isSAndAbove
import com.aurora.extensions.requiresObbDir
import com.aurora.gplayapi.data.models.PlayFile
import com.aurora.gplayapi.helpers.PurchaseHelper
import com.aurora.gplayapi.network.IHttpClient
import com.aurora.store.AuroraApp
@@ -55,7 +56,6 @@ import java.security.DigestInputStream
import java.security.MessageDigest
import kotlin.coroutines.cancellation.CancellationException
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.
@@ -127,7 +127,7 @@ class DownloadWorker @AssistedInject constructor(
PathUtil.getObbDownloadDir(download.packageName).mkdirs()
}
val files = mutableListOf<GPlayFile>()
val files = mutableListOf<PlayFile>()
// Check if shared libs are present, if yes, handle them first
if (download.sharedLibs.isNotEmpty()) {
@@ -245,7 +245,7 @@ class DownloadWorker @AssistedInject constructor(
* @param offerType Offer type of the app (free/paid)
* @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 {
// Android 9.0+ supports key rotation, so purchase with latest certificate's hash
return if (isPAndAbove && download.isInstalled) {
@@ -267,10 +267,10 @@ class DownloadWorker @AssistedInject constructor(
/**
* Downloads the file from the given request.
* 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.
*/
private suspend fun downloadFile(packageName: String, gFile: GPlayFile): Boolean {
private suspend fun downloadFile(packageName: String, gFile: PlayFile): Boolean {
return withContext(Dispatchers.IO) {
Log.i(TAG, "Downloading $packageName @ ${gFile.name}")
val file = PathUtil.getLocalFile(context, gFile, download)
@@ -414,11 +414,11 @@ class DownloadWorker @AssistedInject constructor(
}
/**
* Verifies integrity of a downloaded [GPlayFile].
* @param gFile [GPlayFile] to verify
* Verifies integrity of a downloaded [PlayFile].
* @param gFile [PlayFile] to verify
*/
@OptIn(ExperimentalStdlibApi::class)
private suspend fun verifyFile(gFile: GPlayFile): Boolean {
private suspend fun verifyFile(gFile: PlayFile): Boolean {
val file = PathUtil.getLocalFile(context, gFile, download)
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)
if (apkFile.exists()) {
apkFile.delete()

View File

@@ -77,7 +77,7 @@ class ExportWorker @AssistedInject constructor(
.putString(URI, uri.toString())
.putString(DISPLAY_NAME, download.displayName)
.putString(PACKAGE_NAME, download.packageName)
.putInt(VERSION_CODE, download.versionCode)
.putLong(VERSION_CODE, download.versionCode)
.build()
val oneTimeWorkRequest = OneTimeWorkRequestBuilder<ExportWorker>()
@@ -99,11 +99,11 @@ class ExportWorker @AssistedInject constructor(
val uri = inputData.getString(URI)!!.toUri()
val packageName = inputData.getString(PACKAGE_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>()!!
if (packageName.isNullOrEmpty() || isDownload && versionCode == -1) {
if (packageName.isNullOrEmpty() || isDownload && versionCode == -1L) {
Log.e(TAG, "Input data is corrupt, bailing out!")
notifyStatus(displayName ?: String(), uri, false)
return Result.failure()
@@ -156,7 +156,7 @@ class ExportWorker @AssistedInject constructor(
bundleAllAPKs(fileList.filterNotNull(), uri)
}
private fun copyDownloadedApp(packageName: String, versionCode: Int, uri: Uri) {
private fun copyDownloadedApp(packageName: String, versionCode: Long, uri: Uri) {
return bundleAllAPKs(
PathUtil.getAppDownloadDir(context, packageName, versionCode).listFiles()!!.toList(),
uri

View File

@@ -51,8 +51,8 @@ object PackageUtil {
private const val TAG = "PackageUtil"
const val PACKAGE_NAME_GMS = "com.google.android.gms"
private const val VERSION_CODE_MICRO_G = 240913402
private const val VERSION_CODE_MICRO_G_HUAWEI = 240913007
private const val VERSION_CODE_MICRO_G: Long = 240913402
private const val VERSION_CODE_MICRO_G_HUAWEI: Long = 240913007
fun getAllValidPackages(context: Context): List<PackageInfo> {
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 {
val packageInfo = getPackageInfo(context, packageName)
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) {
val sharedLibraries = getAllSharedLibraries(context)
if (isPAndAbove) {
sharedLibraries.any {
it.name == packageName && it.longVersion == versionCode.toLong()
it.name == packageName && it.longVersion == versionCode
}
} else {
sharedLibraries.any {
@Suppress("DEPRECATION")
it.name == packageName && it.version == versionCode
it.name == packageName && it.version == versionCode.toInt()
}
}
} else {

View File

@@ -21,10 +21,10 @@ package com.aurora.store.util
import android.content.Context
import android.os.Environment
import com.aurora.gplayapi.data.models.PlayFile
import com.aurora.store.data.room.download.Download
import java.io.File
import java.util.UUID
import com.aurora.gplayapi.data.models.File as GPlayFile
object PathUtil {
@@ -47,14 +47,14 @@ object PathUtil {
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())
}
fun getLibDownloadDir(
context: Context,
packageName: String,
versionCode: Int,
versionCode: Long,
sharedLibPackageName: String
): 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 gFile [GPlayFile] to download
* @param playFile [PlayFile] to download
* @param download An instance of [Download]
*/
fun getLocalFile(context: Context, gFile: GPlayFile, download: Download): File {
val sharedLib = download.sharedLibs.find { it.fileList.contains(gFile) }
return when (gFile.type) {
GPlayFile.FileType.BASE, GPlayFile.FileType.SPLIT -> {
fun getLocalFile(context: Context, playFile: PlayFile, download: Download): File {
val sharedLib = download.sharedLibs.find { it.fileList.contains(playFile) }
return when (playFile.type) {
PlayFile.Type.BASE, PlayFile.Type.SPLIT -> {
val downloadDir = if (sharedLib != null) {
getLibDownloadDir(
context,
@@ -83,16 +83,16 @@ object PathUtil {
} else {
getAppDownloadDir(context, download.packageName, download.versionCode)
}
return File(downloadDir, gFile.name)
return File(downloadDir, playFile.name)
}
GPlayFile.FileType.OBB, GPlayFile.FileType.PATCH -> {
File(getObbDownloadDir(download.packageName), gFile.name)
PlayFile.Type.OBB, PlayFile.Type.PATCH -> {
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(
getAppDownloadDir(
context,

View File

@@ -23,7 +23,7 @@ import android.content.Context
import android.util.AttributeSet
import com.airbnb.epoxy.ModelProp
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.util.CommonUtil
import com.aurora.store.view.epoxy.views.BaseModel
@@ -40,7 +40,7 @@ class FileView @JvmOverloads constructor(
) : BaseView<ViewFileBinding>(context, attrs, defStyleAttr) {
@ModelProp
fun file(file: File) {
fun file(file: PlayFile) {
binding.line1.text = file.name
binding.line2.text = CommonUtil.addSiPrefix(file.size)
}

View File

@@ -27,8 +27,6 @@ import androidx.navigation.fragment.navArgs
import com.aurora.gplayapi.data.models.App
import com.aurora.gplayapi.data.models.StreamBundle
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.Loading.getDataAs
import com.aurora.store.databinding.FragmentGenericWithToolbarBinding
@@ -44,14 +42,11 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
private val args: CategoryBrowseFragmentArgs by navArgs()
private val viewModel: CategoryStreamViewModel by activityViewModels()
private lateinit var category: StreamContract.Category
private var streamBundle: StreamBundle? = StreamBundle()
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
category = CategoryUtil.getCategoryFromUrl(args.browseUrl)
val genericCarouselController = CategoryCarouselController(this)
// Toolbar
@@ -64,11 +59,11 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
binding.recycler.setController(genericCarouselController)
binding.recycler.addOnScrollListener(object : EndlessRecyclerOnScrollListener() {
override fun onLoadMore(currentPage: Int) {
viewModel.observe(category)
viewModel.observe(args.browseUrl)
}
})
viewModel.getStreamBundle(category)
viewModel.getStreamBundle(args.browseUrl)
viewModel.liveData.observe(viewLifecycleOwner) {
when (it) {
is ViewState.Loading -> {
@@ -77,7 +72,7 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
is ViewState.Success<*> -> {
val stash = it.getDataAs<Map<String, StreamBundle>>()
streamBundle = stash[category.value]
streamBundle = stash[args.browseUrl]
genericCarouselController.setData(streamBundle)
}
@@ -92,7 +87,7 @@ class CategoryBrowseFragment : BaseFragment<FragmentGenericWithToolbarBinding>()
}
override fun onClusterScrolled(streamCluster: StreamCluster) {
viewModel.observeCluster(category, streamCluster)
viewModel.observeCluster(args.browseUrl, streamCluster)
}
override fun onAppClick(app: App) {

View File

@@ -136,8 +136,8 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
val requestedApp = app.copy(
versionCode = event.versionCode,
dependencies = app.dependencies.copy(
dependentLibraries = app.dependencies.dependentLibraries.onEach { lib ->
lib.versionCode = event.versionCode
dependentLibraries = app.dependencies.dependentLibraries.map { lib ->
lib.copy(versionCode = event.versionCode)
}
)
)
@@ -173,11 +173,10 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
super.onViewCreated(view, savedInstanceState)
app = args.app ?: App(args.packageName)
app.apply {
// Check whether app is installed or not
app = args.app ?: App(
packageName = args.packageName,
isInstalled = PackageUtil.isInstalled(requireContext(), app.packageName)
}
)
// Show the basic app details, while the rest of the data is being fetched
updateAppHeader(app, false)
@@ -204,7 +203,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
updateExtraDetails(app)
updateCompatibilityInfo()
if (app.versionCode == 0) {
if (app.versionCode == 0L) {
warnAppUnavailable(app)
}
@@ -607,7 +606,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
}
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
binding.layoutDetailsApp.btnPrimaryAction.isEnabled = true
@@ -634,7 +633,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
binding.layoutDetailsApp.btnPrimaryAction.apply {
text = getString(R.string.action_update)
setOnClickListener {
if (app.versionCode == 0) {
if (app.versionCode == 0L) {
toast(R.string.toast_app_unavailable)
setText(R.string.status_unavailable)
} else {
@@ -692,7 +691,7 @@ class AppDetailsFragment : BaseFragment<FragmentDetailsBinding>() {
if (viewModel.authProvider.isAnonymous && !app.isFree) {
toast(R.string.toast_purchase_blocked)
return@setOnClickListener
} else if (app.versionCode == 0) {
} else if (app.versionCode == 0L) {
toast(R.string.toast_app_unavailable)
return@setOnClickListener
}

View File

@@ -95,7 +95,7 @@ class ManualDownloadSheet : BaseDialogSheet<SheetManualDownloadBinding>() {
if (customVersionString.isEmpty())
binding.versionCodeInp.error = "Enter version code"
else {
viewModel.purchase(args.app, customVersionString.toInt())
viewModel.purchase(args.app, customVersionString.toLong())
}
}

View File

@@ -67,10 +67,10 @@ class ExpandedStreamBrowseViewModel @Inject constructor(
streamCluster.clusterNextPageUrl
)
streamCluster.apply {
clusterAppList.addAll(newCluster.clusterAppList)
streamCluster = streamCluster.copy(
clusterAppList = streamCluster.clusterAppList + newCluster.clusterAppList,
clusterNextPageUrl = newCluster.clusterNextPageUrl
}
)
liveData.postValue(streamCluster)

View File

@@ -56,10 +56,10 @@ class StreamBrowseViewModel @Inject constructor(
streamCluster.clusterNextPageUrl
)
streamCluster.apply {
clusterAppList.addAll(nextCluster.clusterAppList)
clusterNextPageUrl = nextCluster.clusterNextPageUrl
}
streamCluster = streamCluster.copy(
clusterNextPageUrl = nextCluster.clusterNextPageUrl,
clusterAppList = streamCluster.clusterAppList + nextCluster.clusterAppList
)
liveData.postValue(streamCluster)
} else {

View File

@@ -96,9 +96,9 @@ class AppDetailsViewModel @Inject constructor(
checkFavourite(packageName)
val app: App = appStash.getOrPut(packageName) {
appDetailsHelper.getAppByPackageName(packageName).apply {
appDetailsHelper.getAppByPackageName(packageName).copy(
isInstalled = PackageUtil.isInstalled(context, packageName)
}
)
}
_app.emit(app)

View File

@@ -62,10 +62,11 @@ class DetailsClusterViewModel @Inject constructor(
if (!bundle.hasCluster() || bundle.hasNext()) {
val newBundle = appDetailsHelper.getDetailsStream(streamUrl)
bundle.apply {
streamClusters.putAll(newBundle.streamClusters)
val mergedBundle = bundle.copy(
streamClusters = bundle.streamClusters + newBundle.streamClusters,
streamNextPageUrl = newBundle.streamNextPageUrl
}
)
stash[streamUrl] = mergedBundle
liveData.postValue(ViewState.Success(stash))
}
@@ -87,7 +88,6 @@ class DetailsClusterViewModel @Inject constructor(
liveData.postValue(ViewState.Success(stash))
} else {
Log.i(TAG, "End of cluster")
streamCluster.clusterNextPageUrl = String()
}
} catch (e: Exception) {
liveData.postValue(ViewState.Error(e.message))
@@ -101,9 +101,18 @@ class DetailsClusterViewModel @Inject constructor(
clusterID: Int,
newCluster: StreamCluster
) {
targetBundle(url).streamClusters[clusterID]?.apply {
clusterAppList.addAll(newCluster.clusterAppList)
clusterNextPageUrl = newCluster.clusterNextPageUrl
val bundle = targetBundle(url)
bundle.streamClusters[clusterID]?.let { oldCluster ->
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))
}
}

View File

@@ -76,7 +76,6 @@ class DevProfileViewModel @Inject constructor(
liveData.postValue(ViewState.Success(devStream))
} else {
Log.i(TAG, "End of cluster")
streamCluster.clusterNextPageUrl = String()
}
} catch (e: Exception) {
liveData.postValue(ViewState.Error(e.message))
@@ -86,9 +85,17 @@ class DevProfileViewModel @Inject constructor(
}
private fun updateCluster(newCluster: StreamCluster) {
streamBundle.streamClusters[newCluster.id]?.apply {
clusterAppList.addAll(newCluster.clusterAppList)
clusterNextPageUrl = newCluster.clusterNextPageUrl
streamBundle.streamClusters[newCluster.id]?.let { oldCluster ->
val mergedCluster = oldCluster.copy(
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)
}
}
}

View File

@@ -76,10 +76,11 @@ class StreamViewModel @Inject constructor(
}
//Update old bundle
bundle.apply {
streamClusters.putAll(newBundle.streamClusters)
val mergedBundle = bundle.copy(
streamClusters = bundle.streamClusters + newBundle.streamClusters,
streamNextPageUrl = newBundle.streamNextPageUrl
}
)
stash[category] = mergedBundle
//Post updated to UI
liveData.postValue(ViewState.Success(stash))
@@ -98,13 +99,13 @@ class StreamViewModel @Inject constructor(
supervisorScope {
try {
if (streamCluster.hasNext()) {
val newCluster =
streamContract.nextStreamCluster(streamCluster.clusterNextPageUrl)
val newCluster = streamContract.nextStreamCluster(
streamCluster.clusterNextPageUrl
)
updateCluster(category, streamCluster.id, newCluster)
liveData.postValue(ViewState.Success(stash))
} else {
Log.i(TAG, "End of cluster")
streamCluster.clusterNextPageUrl = String()
}
} catch (e: Exception) {
liveData.postValue(ViewState.Error(e.message))
@@ -118,9 +119,18 @@ class StreamViewModel @Inject constructor(
clusterID: Int,
newCluster: StreamCluster
) {
targetBundle(category).streamClusters[clusterID]?.apply {
clusterAppList.addAll(newCluster.clusterAppList)
clusterNextPageUrl = newCluster.clusterNextPageUrl
val bundle = targetBundle(category)
bundle.streamClusters[clusterID]?.let { oldCluster ->
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))
}
}

View File

@@ -57,11 +57,10 @@ class ReviewViewModel @Inject constructor(
supervisorScope {
try {
val nextReviewCluster = reviewsHelper.next(nextReviewPageUrl)
reviewsCluster.copy(
nextPageUrl = nextReviewCluster.nextPageUrl
).apply {
reviewList.addAll(nextReviewCluster.reviewList)
}
reviewsCluster = reviewsCluster.copy(
nextPageUrl = nextReviewCluster.nextPageUrl,
reviewList = nextReviewCluster.reviewList + nextReviewCluster.reviewList
)
liveData.postValue(reviewsCluster)
} catch (_: Exception) {

View File

@@ -23,7 +23,6 @@ import android.util.Log
import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import com.aurora.extensions.flushAndAdd
import com.aurora.gplayapi.data.models.SearchBundle
import com.aurora.gplayapi.helpers.SearchHelper
import com.aurora.gplayapi.helpers.contracts.SearchContract
@@ -54,10 +53,6 @@ class SearchResultViewModel @Inject constructor(
get() = if (authProvider.isAnonymous) webSearchHelper else searchHelper
fun observeSearchResults(query: String) {
//Clear old results
searchBundle.subBundles.clear()
searchBundle.appList.clear()
//Fetch new results
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
try {
@@ -75,17 +70,17 @@ class SearchResultViewModel @Inject constructor(
}
@Synchronized
fun next(nextSubBundleSet: MutableSet<SearchBundle.SubBundle>) {
fun next(nextSubBundleSet: Set<SearchBundle.SubBundle>) {
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
try {
if (nextSubBundleSet.isNotEmpty()) {
val newSearchBundle = helper.next(nextSubBundleSet)
val newSearchBundle = helper.next(nextSubBundleSet.toMutableSet())
if (newSearchBundle.appList.isNotEmpty()) {
searchBundle.apply {
subBundles.flushAndAdd(newSearchBundle.subBundles)
appList.addAll(newSearchBundle.appList)
}
searchBundle = searchBundle.copy(
subBundles = newSearchBundle.subBundles,
appList = searchBundle.appList + newSearchBundle.appList
)
liveData.postValue(searchBundle)
}

View File

@@ -24,7 +24,7 @@ class ManualDownloadViewModel @Inject constructor(
private val _purchaseStatus = MutableSharedFlow<Boolean>()
val purchaseStatus = _purchaseStatus.asSharedFlow()
fun purchase(app: App, customVersion: Int) {
fun purchase(app: App, customVersion: Long) {
viewModelScope.launch(Dispatchers.IO) {
try {
val files = purchaseHelper.purchase(app.packageName, customVersion, app.offerType)

View File

@@ -49,38 +49,38 @@ class CategoryStreamViewModel @Inject constructor(
private val categoryStreamContract: CategoryStreamContract
get() = webCategoryStreamHelper
fun getStreamBundle(category: StreamContract.Category) {
fun getStreamBundle(browseUrl: String) {
liveData.postValue(ViewState.Loading)
observe(category)
observe(browseUrl)
}
fun observe(category: StreamContract.Category) {
fun observe(browseUrl: String) {
liveData.postValue(ViewState.Loading)
viewModelScope.launch(Dispatchers.IO) {
supervisorScope {
val bundle = targetBundle(category)
val bundle = targetBundle(browseUrl)
if (bundle.streamClusters.isNotEmpty()) {
liveData.postValue(ViewState.Success(stash))
}
try {
if (!bundle.hasCluster() || bundle.hasNext()) {
//Fetch new stream bundle
val newBundle = if (bundle.streamClusters.isEmpty()) {
categoryStreamContract.fetch(category.value)
categoryStreamContract.fetch(browseUrl)
} else {
categoryStreamContract.nextStreamBundle(
category,
StreamContract.Category.NONE,
bundle.streamNextPageUrl
)
}
//Update old bundle
bundle.apply {
streamClusters.putAll(newBundle.streamClusters)
val mergedBundle = bundle.copy(
streamClusters = bundle.streamClusters + newBundle.streamClusters,
streamNextPageUrl = newBundle.streamNextPageUrl
}
)
stash[browseUrl] = mergedBundle
//Post updated to UI
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) {
supervisorScope {
try {
if (streamCluster.hasNext()) {
val newCluster =
categoryStreamContract.nextStreamCluster(streamCluster.clusterNextPageUrl)
updateCluster(category, streamCluster.id, newCluster)
val newCluster = categoryStreamContract.nextStreamCluster(
streamCluster.clusterNextPageUrl
)
updateCluster(browseUrl, streamCluster.id, newCluster)
liveData.postValue(ViewState.Success(stash))
} else {
Log.i(TAG, "End of cluster")
streamCluster.clusterNextPageUrl = String()
}
} catch (e: Exception) {
liveData.postValue(ViewState.Error(e.message))
@@ -114,22 +114,24 @@ class CategoryStreamViewModel @Inject constructor(
}
}
private fun updateCluster(
category: StreamContract.Category,
clusterID: Int,
newCluster: StreamCluster
) {
targetBundle(category).streamClusters[clusterID]?.apply {
clusterAppList.addAll(newCluster.clusterAppList)
clusterNextPageUrl = newCluster.clusterNextPageUrl
private fun updateCluster(browseUrl: String, clusterID: Int, newCluster: StreamCluster) {
val bundle = targetBundle(browseUrl)
bundle.streamClusters[clusterID]?.let { oldCluster ->
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(browseUrl, bundle.copy(streamClusters = newStreamClusters))
}
}
private fun targetBundle(category: StreamContract.Category): StreamBundle {
val streamBundle = stash.getOrPut(category.value) {
StreamBundle()
}
private fun targetBundle(browseUrl: String): StreamBundle {
val streamBundle = stash.getOrPut(browseUrl) { StreamBundle() }
return streamBundle
}
}

View File

@@ -85,10 +85,13 @@ class TopChartViewModel @Inject constructor(
chart: TopChartsContract.Chart,
newCluster: StreamCluster
) {
targetCluster(type, chart).apply {
clusterAppList.addAll(newCluster.clusterAppList)
clusterNextPageUrl = newCluster.clusterNextPageUrl
}
val streamCluster = targetCluster(type, chart)
val mergedCluster = streamCluster.copy(
clusterNextPageUrl = newCluster.clusterNextPageUrl,
clusterAppList = streamCluster.clusterAppList + newCluster.clusterAppList
)
stash[type] = mutableMapOf(chart to mergedCluster)
}
private fun targetCluster(