Skip to content

Commit

Permalink
feat: save patch selection using room db (ReVanced#38)
Browse files Browse the repository at this point in the history
  • Loading branch information
Axelen123 authored Jun 22, 2023
1 parent 2d9f9ad commit 923ce74
Show file tree
Hide file tree
Showing 24 changed files with 766 additions and 150 deletions.
98 changes: 96 additions & 2 deletions app/schemas/app.revanced.manager.data.room.AppDatabase/1.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "b40f3b048880f3f3c9361f6d1c4aaea5",
"identityHash": "dadad726e82673e2a4c266bf7a7c8af1",
"entities": [
{
"tableName": "sources",
Expand Down Expand Up @@ -57,12 +57,106 @@
}
],
"foreignKeys": []
},
{
"tableName": "patch_selections",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `source` INTEGER NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`uid`), FOREIGN KEY(`source`) REFERENCES `sources`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "uid",
"columnName": "uid",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "packageName",
"columnName": "package_name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"uid"
]
},
"indices": [
{
"name": "index_patch_selections_source_package_name",
"unique": true,
"columnNames": [
"source",
"package_name"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_patch_selections_source_package_name` ON `${TABLE_NAME}` (`source`, `package_name`)"
}
],
"foreignKeys": [
{
"table": "sources",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"source"
],
"referencedColumns": [
"uid"
]
}
]
},
{
"tableName": "selected_patches",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`selection` INTEGER NOT NULL, `patch_name` TEXT NOT NULL, PRIMARY KEY(`selection`, `patch_name`), FOREIGN KEY(`selection`) REFERENCES `patch_selections`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "selection",
"columnName": "selection",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "patchName",
"columnName": "patch_name",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"autoGenerate": false,
"columnNames": [
"selection",
"patch_name"
]
},
"indices": [],
"foreignKeys": [
{
"table": "patch_selections",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"selection"
],
"referencedColumns": [
"uid"
]
}
]
}
],
"views": [],
"setupQueries": [
"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, 'b40f3b048880f3f3c9361f6d1c4aaea5')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'dadad726e82673e2a4c266bf7a7c8af1')"
]
}
}
11 changes: 10 additions & 1 deletion app/src/main/java/app/revanced/manager/data/room/AppDatabase.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,20 @@ package app.revanced.manager.data.room
import androidx.room.Database
import androidx.room.RoomDatabase
import androidx.room.TypeConverters
import app.revanced.manager.data.room.selection.PatchSelection
import app.revanced.manager.data.room.selection.SelectedPatch
import app.revanced.manager.data.room.selection.SelectionDao
import app.revanced.manager.data.room.sources.SourceEntity
import app.revanced.manager.data.room.sources.SourceDao
import kotlin.random.Random

@Database(entities = [SourceEntity::class], version = 1)
@Database(entities = [SourceEntity::class, PatchSelection::class, SelectedPatch::class], version = 1)
@TypeConverters(Converters::class)
abstract class AppDatabase : RoomDatabase() {
abstract fun sourceDao(): SourceDao
abstract fun selectionDao(): SelectionDao

companion object {
fun generateUid() = Random.Default.nextInt()
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package app.revanced.manager.data.room.selection

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import app.revanced.manager.data.room.sources.SourceEntity

@Entity(
tableName = "patch_selections",
foreignKeys = [ForeignKey(
SourceEntity::class,
parentColumns = ["uid"],
childColumns = ["source"],
onDelete = ForeignKey.CASCADE
)],
indices = [Index(value = ["source", "package_name"], unique = true)]
)
data class PatchSelection(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "source") val source: Int,
@ColumnInfo(name = "package_name") val packageName: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package app.revanced.manager.data.room.selection

import androidx.room.ColumnInfo
import androidx.room.Entity
import androidx.room.ForeignKey

@Entity(
tableName = "selected_patches",
primaryKeys = ["selection", "patch_name"],
foreignKeys = [ForeignKey(
PatchSelection::class,
parentColumns = ["uid"],
childColumns = ["selection"],
onDelete = ForeignKey.CASCADE
)]
)
data class SelectedPatch(
@ColumnInfo(name = "selection") val selection: Int,
@ColumnInfo(name = "patch_name") val patchName: String
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package app.revanced.manager.data.room.selection

import androidx.room.Dao
import androidx.room.Insert
import androidx.room.MapInfo
import androidx.room.Query
import androidx.room.Transaction

@Dao
abstract class SelectionDao {
@Transaction
@MapInfo(keyColumn = "source", valueColumn = "patch_name")
@Query(
"SELECT source, patch_name FROM patch_selections" +
" LEFT JOIN selected_patches ON uid = selected_patches.selection" +
" WHERE package_name = :packageName"
)
abstract suspend fun getSelectedPatches(packageName: String): Map<Int, List<String>>

@Transaction
@MapInfo(keyColumn = "package_name", valueColumn = "patch_name")
@Query(
"SELECT package_name, patch_name FROM patch_selections" +
" LEFT JOIN selected_patches ON uid = selected_patches.selection" +
" WHERE source = :sourceUid"
)
abstract suspend fun exportSelection(sourceUid: Int): Map<String, List<String>>

@Query("SELECT uid FROM patch_selections WHERE source = :sourceUid AND package_name = :packageName")
abstract suspend fun getSelectionId(sourceUid: Int, packageName: String): Int?

@Insert
abstract suspend fun createSelection(selection: PatchSelection)

@Query("DELETE FROM patch_selections WHERE source = :uid")
abstract suspend fun clearForSource(uid: Int)

@Query("DELETE FROM patch_selections")
abstract suspend fun reset()

@Insert
protected abstract suspend fun selectPatches(patches: List<SelectedPatch>)

@Query("DELETE FROM selected_patches WHERE selection = :selectionId")
protected abstract suspend fun clearSelection(selectionId: Int)

@Transaction
open suspend fun updateSelections(selections: Map<Int, Set<String>>) =
selections.map { (selectionUid, patches) ->
clearSelection(selectionUid)
selectPatches(patches.map { SelectedPatch(selectionUid, it) })
}
}
2 changes: 2 additions & 0 deletions app/src/main/java/app/revanced/manager/di/RepositoryModule.kt
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package app.revanced.manager.di

import app.revanced.manager.domain.repository.PatchSelectionRepository
import app.revanced.manager.domain.repository.ReVancedRepository
import app.revanced.manager.network.api.ManagerAPI
import app.revanced.manager.domain.repository.SourcePersistenceRepository
Expand All @@ -11,5 +12,6 @@ val repositoryModule = module {
singleOf(::ReVancedRepository)
singleOf(::ManagerAPI)
singleOf(::SourcePersistenceRepository)
singleOf(::PatchSelectionRepository)
singleOf(::SourceRepository)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package app.revanced.manager.domain.repository

import app.revanced.manager.data.room.AppDatabase
import app.revanced.manager.data.room.AppDatabase.Companion.generateUid
import app.revanced.manager.data.room.selection.PatchSelection
import app.revanced.manager.domain.sources.Source

class PatchSelectionRepository(db: AppDatabase) {
private val dao = db.selectionDao()

private suspend fun getOrCreateSelection(sourceUid: Int, packageName: String) =
dao.getSelectionId(sourceUid, packageName) ?: PatchSelection(
uid = generateUid(),
source = sourceUid,
packageName = packageName
).also { dao.createSelection(it) }.uid

suspend fun getSelection(packageName: String): Map<Int, Set<String>> =
dao.getSelectedPatches(packageName).mapValues { it.value.toSet() }

suspend fun updateSelection(packageName: String, selection: Map<Int, Set<String>>) =
dao.updateSelections(selection.mapKeys { (sourceUid, _) ->
getOrCreateSelection(
sourceUid,
packageName
)
})

suspend fun reset() = dao.reset()

suspend fun export(source: Source): SerializedSelection = dao.exportSelection(source.uid)

suspend fun import(source: Source, selection: SerializedSelection) {
dao.clearForSource(source.uid)
dao.updateSelections(selection.entries.associate { (packageName, patches) ->
getOrCreateSelection(source.uid, packageName) to patches.toSet()
})
}
}

/**
* A [Map] of package name -> selected patches.
*/
typealias SerializedSelection = Map<String, List<String>>
Original file line number Diff line number Diff line change
@@ -1,19 +1,17 @@
package app.revanced.manager.domain.repository

import app.revanced.manager.data.room.AppDatabase
import app.revanced.manager.data.room.AppDatabase.Companion.generateUid
import app.revanced.manager.data.room.sources.SourceEntity
import app.revanced.manager.data.room.sources.SourceLocation
import app.revanced.manager.data.room.sources.VersionInfo
import app.revanced.manager.util.apiURL
import kotlin.random.Random
import io.ktor.http.*

class SourcePersistenceRepository(db: AppDatabase) {
private val dao = db.sourceDao()

private companion object {
fun generateUid() = Random.Default.nextInt()

val defaultSource = SourceEntity(
uid = generateUid(),
name = "Official",
Expand Down
Loading

0 comments on commit 923ce74

Please sign in to comment.