refactor(downloaders): improve file system code (#1379)

This commit is contained in:
Ax333l 2023-10-14 17:42:10 +02:00 committed by GitHub
parent 56a4a7043d
commit 8f6d720454
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 55 additions and 50 deletions

View File

@ -2,7 +2,7 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 1, "version": 1,
"identityHash": "5515d164bc8f713201506d42a02d337f", "identityHash": "371c7a84b122a2de8b660b35e6e9ce14",
"entities": [ "entities": [
{ {
"tableName": "patch_bundles", "tableName": "patch_bundles",
@ -160,7 +160,7 @@
}, },
{ {
"tableName": "downloaded_app", "tableName": "downloaded_app",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `version` TEXT NOT NULL, `file` TEXT NOT NULL, PRIMARY KEY(`package_name`, `version`))", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`package_name` TEXT NOT NULL, `version` TEXT NOT NULL, `directory` TEXT NOT NULL, PRIMARY KEY(`package_name`, `version`))",
"fields": [ "fields": [
{ {
"fieldPath": "packageName", "fieldPath": "packageName",
@ -175,8 +175,8 @@
"notNull": true "notNull": true
}, },
{ {
"fieldPath": "file", "fieldPath": "directory",
"columnName": "file", "columnName": "directory",
"affinity": "TEXT", "affinity": "TEXT",
"notNull": true "notNull": true
} }
@ -300,7 +300,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '5515d164bc8f713201506d42a02d337f')" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '371c7a84b122a2de8b660b35e6e9ce14')"
] ]
} }
} }

View File

@ -16,5 +16,5 @@ class Converters {
fun fileFromString(value: String) = File(value) fun fileFromString(value: String) = File(value)
@TypeConverter @TypeConverter
fun fileToString(file: File): String = file.absolutePath fun fileToString(file: File): String = file.path
} }

View File

@ -11,5 +11,5 @@ import java.io.File
data class DownloadedApp( data class DownloadedApp(
@ColumnInfo(name = "package_name") val packageName: String, @ColumnInfo(name = "package_name") val packageName: String,
@ColumnInfo(name = "version") val version: String, @ColumnInfo(name = "version") val version: String,
@ColumnInfo(name = "file") val file: File, @ColumnInfo(name = "directory") val directory: File,
) )

View File

@ -1,34 +1,61 @@
package app.revanced.manager.domain.repository package app.revanced.manager.domain.repository
import android.app.Application
import android.content.Context
import app.revanced.manager.data.room.AppDatabase import app.revanced.manager.data.room.AppDatabase
import app.revanced.manager.data.room.AppDatabase.Companion.generateUid
import app.revanced.manager.data.room.apps.downloaded.DownloadedApp import app.revanced.manager.data.room.apps.downloaded.DownloadedApp
import app.revanced.manager.network.downloader.AppDownloader
import kotlinx.coroutines.flow.distinctUntilChanged import kotlinx.coroutines.flow.distinctUntilChanged
import java.io.File import java.io.File
class DownloadedAppRepository( class DownloadedAppRepository(
app: Application,
db: AppDatabase db: AppDatabase
) { ) {
private val dir = app.getDir("downloaded-apps", Context.MODE_PRIVATE)
private val dao = db.downloadedAppDao() private val dao = db.downloadedAppDao()
fun getAll() = dao.getAllApps().distinctUntilChanged() fun getAll() = dao.getAllApps().distinctUntilChanged()
suspend fun get(packageName: String, version: String) = dao.get(packageName, version) fun getApkFileForApp(app: DownloadedApp): File = getApkFileForDir(dir.resolve(app.directory))
private fun getApkFileForDir(directory: File) = directory.listFiles()!!.first()
suspend fun add( suspend fun download(
packageName: String, app: AppDownloader.App,
version: String, preferSplits: Boolean,
file: File onDownload: suspend (downloadProgress: Pair<Float, Float>?) -> Unit = {},
) = dao.insert( ): File {
DownloadedApp( this.get(app.packageName, app.version)?.let { downloaded ->
packageName = packageName, return getApkFileForApp(downloaded)
version = version, }
file = file
) // Converted integers cannot contain / or .. unlike the package name or version, so they are safer to use here.
) val relativePath = File(generateUid().toString())
val savePath = dir.resolve(relativePath).also { it.mkdirs() }
try {
app.download(savePath, preferSplits, onDownload)
dao.insert(DownloadedApp(
packageName = app.packageName,
version = app.version,
directory = relativePath,
))
} catch (e: Exception) {
savePath.deleteRecursively()
throw e
}
// Return the Apk file.
return getApkFileForDir(savePath)
}
suspend fun get(packageName: String, version: String) = dao.get(packageName, version)
suspend fun delete(downloadedApps: Collection<DownloadedApp>) { suspend fun delete(downloadedApps: Collection<DownloadedApp>) {
downloadedApps.forEach { downloadedApps.forEach {
it.file.deleteRecursively() dir.resolve(it.directory).deleteRecursively()
} }
dao.delete(downloadedApps) dao.delete(downloadedApps)

View File

@ -171,7 +171,7 @@ class APKMirror : AppDownloader, KoinComponent {
saveDirectory: File, saveDirectory: File,
preferSplit: Boolean, preferSplit: Boolean,
onDownload: suspend (downloadProgress: Pair<Float, Float>?) -> Unit onDownload: suspend (downloadProgress: Pair<Float, Float>?) -> Unit
): File { ) {
val variants = httpClient.getHtml { url(apkMirror + downloadLink) } val variants = httpClient.getHtml { url(apkMirror + downloadLink) }
.div { .div {
withClass = "variants-table" withClass = "variants-table"
@ -246,18 +246,10 @@ class APKMirror : AppDownloader, KoinComponent {
} }
} }
val saveLocation = if (variant.apkType == APKType.BUNDLE) val targetFile = saveDirectory.resolve("base.apk")
saveDirectory.resolve(version).also { it.mkdirs() }
else
saveDirectory.resolve("$version.apk")
try { try {
val downloadLocation = if (variant.apkType == APKType.BUNDLE) httpClient.download(targetFile) {
saveLocation.resolve("temp.zip")
else
saveLocation
httpClient.download(downloadLocation) {
url(apkMirror + downloadLink) url(apkMirror + downloadLink)
onDownload { bytesSentTotal, contentLength -> onDownload { bytesSentTotal, contentLength ->
onDownload(bytesSentTotal.div(100000).toFloat().div(10) to contentLength.div(100000).toFloat().div(10)) onDownload(bytesSentTotal.div(100000).toFloat().div(10) to contentLength.div(100000).toFloat().div(10))
@ -267,16 +259,11 @@ class APKMirror : AppDownloader, KoinComponent {
if (variant.apkType == APKType.BUNDLE) { if (variant.apkType == APKType.BUNDLE) {
// TODO: Extract temp.zip // TODO: Extract temp.zip
downloadLocation.delete() targetFile.delete()
} }
} catch (e: Exception) {
saveLocation.deleteRecursively()
throw e
} finally { } finally {
onDownload(null) onDownload(null)
} }
return saveLocation
} }
} }

View File

@ -22,7 +22,6 @@ interface AppDownloader {
saveDirectory: File, saveDirectory: File,
preferSplit: Boolean, preferSplit: Boolean,
onDownload: suspend (downloadProgress: Pair<Float, Float>?) -> Unit = {} onDownload: suspend (downloadProgress: Pair<Float, Float>?) -> Unit = {}
): File )
} }
} }

View File

@ -190,19 +190,11 @@ class PatcherWorker(
val inputFile = when (val selectedApp = args.input) { val inputFile = when (val selectedApp = args.input) {
is SelectedApp.Download -> { is SelectedApp.Download -> {
val savePath = applicationContext.filesDir.resolve("downloaded-apps") downloadedAppRepository.download(
.resolve(args.input.packageName).also { it.mkdirs() } selectedApp.app,
selectedApp.app.download(
savePath,
prefs.preferSplits.get(), prefs.preferSplits.get(),
onDownload = { downloadProgress.emit(it) } onDownload = { downloadProgress.emit(it) }
).also { ).also {
downloadedAppRepository.add(
args.input.packageName,
args.input.version,
it
)
args.setInputFile(it) args.setInputFile(it)
updateProgress() // Downloading updateProgress() // Downloading
} }

View File

@ -68,7 +68,7 @@ class VersionSelectorViewModel(
} }
val downloadedVersions = downloadedAppRepository.getAll().map { downloadedApps -> val downloadedVersions = downloadedAppRepository.getAll().map { downloadedApps ->
downloadedApps.filter { it.packageName == packageName }.map { SelectedApp.Local(it.packageName, it.version, it.file, false) } downloadedApps.filter { it.packageName == packageName }.map { SelectedApp.Local(it.packageName, it.version, downloadedAppRepository.getApkFileForApp(it), false) }
} }
init { init {