DownloadWorker: Download required shared libs for apps

Signed-off-by: Aayush Gupta <aayushgupta219@gmail.com>
This commit is contained in:
Aayush Gupta
2023-12-20 22:16:56 +05:30
parent 231d39867b
commit 97b145e2e5
6 changed files with 100 additions and 19 deletions

View File

@@ -2,10 +2,13 @@ package com.aurora.store.data.room
import androidx.room.Database import androidx.room.Database
import androidx.room.RoomDatabase import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import com.aurora.store.data.room.download.Download import com.aurora.store.data.room.download.Download
import com.aurora.store.data.room.download.DownloadDao import com.aurora.store.data.room.download.DownloadDao
import com.aurora.store.data.room.download.DownloadConverter
@Database(entities = [Download::class], version = 1, exportSchema = false) @Database(entities = [Download::class], version = 1, exportSchema = false)
@TypeConverters(DownloadConverter::class)
abstract class AuroraDatabase : RoomDatabase() { abstract class AuroraDatabase : RoomDatabase() {
abstract fun downloadDao(): DownloadDao abstract fun downloadDao(): DownloadDao
} }

View File

@@ -23,7 +23,8 @@ data class Download(
var speed: Long, var speed: Long,
var timeRemaining: Long, var timeRemaining: Long,
var totalFiles: Int, var totalFiles: Int,
var downloadedFiles: Int var downloadedFiles: Int,
val sharedLibs: List<SharedLib>
) : Parcelable { ) : Parcelable {
val isFinished get() = status in DownloadStatus.finished val isFinished get() = status in DownloadStatus.finished
@@ -43,7 +44,8 @@ data class Download(
0L, 0L,
0L, 0L,
0, 0,
0 0,
app.dependencies.dependentLibraries.map { SharedLib.fromApp(it) }
) )
} }
} }

View File

@@ -0,0 +1,19 @@
package com.aurora.store.data.room.download
import androidx.room.TypeConverter
import com.google.gson.Gson
import com.google.gson.reflect.TypeToken
object DownloadConverter {
@TypeConverter
fun toSharedLibList(string: String): List<SharedLib> {
val listType = object : TypeToken<List<SharedLib>>() {}.type
return Gson().fromJson(string, listType)
}
@TypeConverter
fun fromSharedLibList(list: List<SharedLib>): String {
return Gson().toJson(list)
}
}

View File

@@ -0,0 +1,17 @@
package com.aurora.store.data.room.download
import android.os.Parcelable
import com.aurora.gplayapi.data.models.App
import kotlinx.parcelize.Parcelize
@Parcelize
data class SharedLib(
val packageName: String,
val versionCode: Int
) : Parcelable {
companion object {
fun fromApp(app: App): SharedLib {
return SharedLib(app.packageName, app.versionCode)
}
}
}

View File

@@ -35,7 +35,6 @@ import kotlinx.coroutines.withContext
import java.io.File import java.io.File
import java.net.URL import java.net.URL
import kotlinx.coroutines.NonCancellable import kotlinx.coroutines.NonCancellable
import kotlin.io.path.ExperimentalPathApi
import kotlin.properties.Delegates import kotlin.properties.Delegates
import com.aurora.gplayapi.data.models.File as GPlayFile import com.aurora.gplayapi.data.models.File as GPlayFile
@@ -89,16 +88,33 @@ class DownloadWorker @AssistedInject constructor(
return Result.failure() return Result.failure()
} }
// Download and verify all files exists // Create dirs & generate download request for files and shared libs (if any)
totalBytes = files.sumOf { it.size }
PathUtil.getAppDownloadDir(appContext, download.packageName, download.versionCode).mkdirs() PathUtil.getAppDownloadDir(appContext, download.packageName, download.versionCode).mkdirs()
if (files.any { it.type == GPlayFile.FileType.OBB || it.type == GPlayFile.FileType.PATCH }) { if (files.any { it.type == GPlayFile.FileType.OBB || it.type == GPlayFile.FileType.PATCH }) {
PathUtil.getObbDownloadDir(download.packageName).mkdirs() PathUtil.getObbDownloadDir(download.packageName).mkdirs()
} }
val requestList = getDownloadRequest(files) val requestList = mutableListOf<Request>()
if (download.sharedLibs.isNotEmpty()) {
// Purchase and append shared libs data to existing request
download.sharedLibs.forEach {
PathUtil.getLibDownloadDir(
appContext,
download.packageName,
download.versionCode,
it.packageName
).mkdirs()
val libs = purchaseHelper.purchase(it.packageName, it.versionCode, 0)
requestList.addAll(getDownloadRequest(libs, it.packageName))
}
}
requestList.addAll(getDownloadRequest(files, null))
// Update data for notification
download.totalFiles = requestList.size download.totalFiles = requestList.size
totalBytes = requestList.sumOf { it.size }
// Download and verify all files exists
requestList.forEach { request -> requestList.forEach { request ->
downloading = true downloading = true
runCatching { downloadFile(request); download.downloadedFiles++ } runCatching { downloadFile(request); download.downloadedFiles++ }
@@ -134,7 +150,6 @@ class DownloadWorker @AssistedInject constructor(
return Result.success() return Result.success()
} }
@OptIn(ExperimentalPathApi::class)
private suspend fun onFailure() { private suspend fun onFailure() {
withContext(NonCancellable) { withContext(NonCancellable) {
Log.i(TAG, "Cleaning up!") Log.i(TAG, "Cleaning up!")
@@ -147,13 +162,13 @@ class DownloadWorker @AssistedInject constructor(
} }
} }
private fun getDownloadRequest(files: List<GPlayFile>): List<Request> { private fun getDownloadRequest(files: List<GPlayFile>, libPackageName: String?): List<Request> {
val downloadList = mutableListOf<Request>() val downloadList = mutableListOf<Request>()
files.filter { it.url.isNotBlank() }.forEach { files.filter { it.url.isNotBlank() }.forEach {
val filePath = when (it.type) { val filePath = when (it.type) {
GPlayFile.FileType.BASE, GPlayFile.FileType.SPLIT -> { GPlayFile.FileType.BASE, GPlayFile.FileType.SPLIT -> {
PathUtil.getApkDownloadFile( PathUtil.getApkDownloadFile(
appContext, download.packageName, download.versionCode, it appContext, download.packageName, download.versionCode, it, libPackageName
) )
} }

View File

@@ -32,6 +32,8 @@ fun Context.getInternalBaseDirectory(): String {
object PathUtil { object PathUtil {
private const val libraries = "libraries"
private fun getDownloadDirectory(context: Context): String { private fun getDownloadDirectory(context: Context): String {
return if (context.isExternalStorageEnable()) { return if (context.isExternalStorageEnable()) {
getExternalPath(context) getExternalPath(context)
@@ -47,22 +49,45 @@ object PathUtil {
private fun getVersionDirectory( private fun getVersionDirectory(
context: Context, context: Context,
packageName: String, packageName: String,
versionCode: Int versionCode: Int,
sharedLibPackageName: String? = null
): String { ): String {
return getPackageDirectory(context, packageName) + "/$versionCode" return if (!sharedLibPackageName.isNullOrBlank()) {
getLibDownloadDir(context, packageName, versionCode, sharedLibPackageName).absolutePath
} else {
getPackageDirectory(context, packageName) + "/$versionCode"
}
} }
fun getAppDownloadDir(context: Context, packageName: String, versionCode: Int): File { fun getAppDownloadDir(context: Context, packageName: String, versionCode: Int): File {
return File(getPackageDirectory(context, packageName), versionCode.toString()) return File(getPackageDirectory(context, packageName), versionCode.toString())
} }
fun getLibDownloadDir(
context: Context,
packageName: String,
versionCode: Int,
sharedLibPackageName: String
): File {
return File(
getAppDownloadDir(context, packageName, versionCode).absolutePath,
"$libraries/$sharedLibPackageName"
)
}
fun getApkDownloadFile( fun getApkDownloadFile(
context: Context, context: Context,
packageName: String, packageName: String,
versionCode: Int, versionCode: Int,
file: GPlayFile file: GPlayFile,
sharedLibPackageName: String? = null
): String { ): String {
return getVersionDirectory(context, packageName, versionCode) + "/${file.name}" return getVersionDirectory(
context,
packageName,
versionCode,
sharedLibPackageName
) + "/${file.name}"
} }
fun getApkDownloadFile(context: Context, packageName: String, versionCode: Int): String { fun getApkDownloadFile(context: Context, packageName: String, versionCode: Int): String {
@@ -71,7 +96,7 @@ object PathUtil {
fun getExternalPath(context: Context): String { fun getExternalPath(context: Context): String {
val defaultDir = val defaultDir =
java.io.File("${Environment.getExternalStorageDirectory().absolutePath}/Aurora/Store") File("${Environment.getExternalStorageDirectory().absolutePath}/Aurora/Store")
if (!defaultDir.exists()) if (!defaultDir.exists())
defaultDir.mkdirs() defaultDir.mkdirs()
@@ -112,8 +137,8 @@ object PathUtil {
return "${context.getInternalBaseDirectory()}/SpoofConfigs" return "${context.getInternalBaseDirectory()}/SpoofConfigs"
} }
fun getNewEmptySpoofConfig(context: Context): java.io.File { fun getNewEmptySpoofConfig(context: Context): File {
val file = java.io.File("${getSpoofDirectory(context)}/${UUID.randomUUID()}.properties") val file = File("${getSpoofDirectory(context)}/${UUID.randomUUID()}.properties")
file.parentFile?.mkdirs() file.parentFile?.mkdirs()
file.createNewFile() file.createNewFile()
return file return file
@@ -121,9 +146,9 @@ object PathUtil {
fun canWriteToDirectory(context: Context, directoryPath: String): Boolean { fun canWriteToDirectory(context: Context, directoryPath: String): Boolean {
val directory = if (directoryPath.startsWith("/")) { val directory = if (directoryPath.startsWith("/")) {
java.io.File(directoryPath) File(directoryPath)
} else { } else {
java.io.File(context.getExternalFilesDir(null), directoryPath) File(context.getExternalFilesDir(null), directoryPath)
} }
return if (isRAndAbove()) { return if (isRAndAbove()) {