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.RoomDatabase
import androidx.room.TypeConverters
import com.aurora.store.data.room.download.Download
import com.aurora.store.data.room.download.DownloadDao
import com.aurora.store.data.room.download.DownloadConverter
@Database(entities = [Download::class], version = 1, exportSchema = false)
@TypeConverters(DownloadConverter::class)
abstract class AuroraDatabase : RoomDatabase() {
abstract fun downloadDao(): DownloadDao
}

View File

@@ -23,7 +23,8 @@ data class Download(
var speed: Long,
var timeRemaining: Long,
var totalFiles: Int,
var downloadedFiles: Int
var downloadedFiles: Int,
val sharedLibs: List<SharedLib>
) : Parcelable {
val isFinished get() = status in DownloadStatus.finished
@@ -43,7 +44,8 @@ data class Download(
0L,
0L,
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.net.URL
import kotlinx.coroutines.NonCancellable
import kotlin.io.path.ExperimentalPathApi
import kotlin.properties.Delegates
import com.aurora.gplayapi.data.models.File as GPlayFile
@@ -89,16 +88,33 @@ class DownloadWorker @AssistedInject constructor(
return Result.failure()
}
// Download and verify all files exists
totalBytes = files.sumOf { it.size }
// Create dirs & generate download request for files and shared libs (if any)
PathUtil.getAppDownloadDir(appContext, download.packageName, download.versionCode).mkdirs()
if (files.any { it.type == GPlayFile.FileType.OBB || it.type == GPlayFile.FileType.PATCH }) {
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
totalBytes = requestList.sumOf { it.size }
// Download and verify all files exists
requestList.forEach { request ->
downloading = true
runCatching { downloadFile(request); download.downloadedFiles++ }
@@ -134,7 +150,6 @@ class DownloadWorker @AssistedInject constructor(
return Result.success()
}
@OptIn(ExperimentalPathApi::class)
private suspend fun onFailure() {
withContext(NonCancellable) {
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>()
files.filter { it.url.isNotBlank() }.forEach {
val filePath = when (it.type) {
GPlayFile.FileType.BASE, GPlayFile.FileType.SPLIT -> {
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 {
private const val libraries = "libraries"
private fun getDownloadDirectory(context: Context): String {
return if (context.isExternalStorageEnable()) {
getExternalPath(context)
@@ -47,22 +49,45 @@ object PathUtil {
private fun getVersionDirectory(
context: Context,
packageName: String,
versionCode: Int
versionCode: Int,
sharedLibPackageName: String? = null
): 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 {
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(
context: Context,
packageName: String,
versionCode: Int,
file: GPlayFile
file: GPlayFile,
sharedLibPackageName: String? = null
): 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 {
@@ -71,7 +96,7 @@ object PathUtil {
fun getExternalPath(context: Context): String {
val defaultDir =
java.io.File("${Environment.getExternalStorageDirectory().absolutePath}/Aurora/Store")
File("${Environment.getExternalStorageDirectory().absolutePath}/Aurora/Store")
if (!defaultDir.exists())
defaultDir.mkdirs()
@@ -112,8 +137,8 @@ object PathUtil {
return "${context.getInternalBaseDirectory()}/SpoofConfigs"
}
fun getNewEmptySpoofConfig(context: Context): java.io.File {
val file = java.io.File("${getSpoofDirectory(context)}/${UUID.randomUUID()}.properties")
fun getNewEmptySpoofConfig(context: Context): File {
val file = File("${getSpoofDirectory(context)}/${UUID.randomUUID()}.properties")
file.parentFile?.mkdirs()
file.createNewFile()
return file
@@ -121,9 +146,9 @@ object PathUtil {
fun canWriteToDirectory(context: Context, directoryPath: String): Boolean {
val directory = if (directoryPath.startsWith("/")) {
java.io.File(directoryPath)
File(directoryPath)
} else {
java.io.File(context.getExternalFilesDir(null), directoryPath)
File(context.getExternalFilesDir(null), directoryPath)
}
return if (isRAndAbove()) {