Skip to content

Commit

Permalink
feat: finish implementing the sources system (ReVanced#70)
Browse files Browse the repository at this point in the history
  • Loading branch information
Axelen123 committed Aug 3, 2023
1 parent 299aaa2 commit 379ce91
Show file tree
Hide file tree
Showing 60 changed files with 1,544 additions and 1,059 deletions.
42 changes: 24 additions & 18 deletions app/schemas/app.revanced.manager.data.room.AppDatabase/1.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "f7e0fef1b937143a8b128e3dbab7c041",
"identityHash": "7142188e25ce489eb233aed8fb76e4cc",
"entities": [
{
"tableName": "sources",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `name` TEXT NOT NULL, `location` TEXT NOT NULL, `version` TEXT NOT NULL, `integrations_version` TEXT NOT NULL, PRIMARY KEY(`uid`))",
"tableName": "patch_bundles",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `name` TEXT NOT NULL, `source` TEXT NOT NULL, `auto_update` INTEGER NOT NULL, `version` TEXT, `integrations_version` TEXT, PRIMARY KEY(`uid`))",
"fields": [
{
"fieldPath": "uid",
Expand All @@ -21,22 +21,28 @@
"notNull": true
},
{
"fieldPath": "location",
"columnName": "location",
"fieldPath": "source",
"columnName": "source",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "autoUpdate",
"columnName": "auto_update",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "versionInfo.patches",
"columnName": "version",
"affinity": "TEXT",
"notNull": true
"notNull": false
},
{
"fieldPath": "versionInfo.integrations",
"columnName": "integrations_version",
"affinity": "TEXT",
"notNull": true
"notNull": false
}
],
"primaryKey": {
Expand All @@ -47,20 +53,20 @@
},
"indices": [
{
"name": "index_sources_name",
"name": "index_patch_bundles_name",
"unique": true,
"columnNames": [
"name"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_sources_name` ON `${TABLE_NAME}` (`name`)"
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_patch_bundles_name` ON `${TABLE_NAME}` (`name`)"
}
],
"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 )",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`uid` INTEGER NOT NULL, `patch_bundle` INTEGER NOT NULL, `package_name` TEXT NOT NULL, PRIMARY KEY(`uid`), FOREIGN KEY(`patch_bundle`) REFERENCES `patch_bundles`(`uid`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "uid",
Expand All @@ -69,8 +75,8 @@
"notNull": true
},
{
"fieldPath": "source",
"columnName": "source",
"fieldPath": "patchBundle",
"columnName": "patch_bundle",
"affinity": "INTEGER",
"notNull": true
},
Expand All @@ -89,23 +95,23 @@
},
"indices": [
{
"name": "index_patch_selections_source_package_name",
"name": "index_patch_selections_patch_bundle_package_name",
"unique": true,
"columnNames": [
"source",
"patch_bundle",
"package_name"
],
"orders": [],
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_patch_selections_source_package_name` ON `${TABLE_NAME}` (`source`, `package_name`)"
"createSql": "CREATE UNIQUE INDEX IF NOT EXISTS `index_patch_selections_patch_bundle_package_name` ON `${TABLE_NAME}` (`patch_bundle`, `package_name`)"
}
],
"foreignKeys": [
{
"table": "sources",
"table": "patch_bundles",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"source"
"patch_bundle"
],
"referencedColumns": [
"uid"
Expand Down Expand Up @@ -189,7 +195,7 @@
"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, 'f7e0fef1b937143a8b128e3dbab7c041')"
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '7142188e25ce489eb233aed8fb76e4cc')"
]
}
}
1 change: 1 addition & 0 deletions app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
<permission android:name="android.permission.QUERY_ALL_PACKAGES"
tools:ignore="ReservedSystemPermission" />

<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission android:name="android.permission.REQUEST_DELETE_PACKAGES" />
Expand Down
18 changes: 10 additions & 8 deletions app/src/main/java/app/revanced/manager/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import androidx.compose.animation.ExperimentalAnimationApi
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.runtime.getValue
import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.ui.component.AutoUpdatesDialog
import app.revanced.manager.ui.destination.Destination
import app.revanced.manager.ui.screen.VersionSelectorScreen
import app.revanced.manager.ui.screen.AppSelectorScreen
Expand All @@ -28,22 +28,19 @@ import dev.olshevski.navigation.reimagined.popUpTo
import dev.olshevski.navigation.reimagined.rememberNavController
import me.zhanghai.android.appiconloader.coil.AppIconFetcher
import me.zhanghai.android.appiconloader.coil.AppIconKeyer
import org.koin.android.ext.android.get
import org.koin.androidx.compose.getViewModel
import org.koin.core.parameter.parametersOf
import kotlin.math.roundToInt
import org.koin.androidx.viewmodel.ext.android.getViewModel as getActivityViewModel

class MainActivity : ComponentActivity() {
private val prefs: PreferencesManager = get()

@ExperimentalAnimationApi
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)

installSplashScreen()
val vm: MainViewModel = getActivityViewModel()

getActivityViewModel<MainViewModel>()
installSplashScreen()

val scale = this.resources.displayMetrics.density
val pixels = (36 * scale).roundToInt()
Expand All @@ -57,8 +54,8 @@ class MainActivity : ComponentActivity() {
)

setContent {
val theme by prefs.theme.getAsState()
val dynamicColor by prefs.dynamicColor.getAsState()
val theme by vm.prefs.theme.getAsState()
val dynamicColor by vm.prefs.dynamicColor.getAsState()

ReVancedManagerTheme(
darkTheme = theme == Theme.SYSTEM && isSystemInDarkTheme() || theme == Theme.DARK,
Expand All @@ -69,6 +66,11 @@ class MainActivity : ComponentActivity() {

NavBackHandler(navController)

val showAutoUpdatesDialog by vm.prefs.showAutoUpdatesDialog.getAsState()
if (showAutoUpdatesDialog) {
AutoUpdatesDialog(vm::applyAutoUpdatePrefs)
}

AnimatedNavHost(
controller = navController
) { destination ->
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/app/revanced/manager/ManagerApplication.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@ package app.revanced.manager
import android.app.Application
import app.revanced.manager.di.*
import app.revanced.manager.domain.manager.PreferencesManager
import app.revanced.manager.domain.repository.PatchBundleRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.MainScope
import kotlinx.coroutines.launch
import org.koin.android.ext.android.inject
Expand All @@ -14,6 +16,7 @@ import org.koin.core.context.startKoin
class ManagerApplication : Application() {
private val scope = MainScope()
private val prefs: PreferencesManager by inject()
private val patchBundleRepository: PatchBundleRepository by inject()
override fun onCreate() {
super.onCreate()

Expand All @@ -36,5 +39,11 @@ class ManagerApplication : Application() {
scope.launch {
prefs.preload()
}
scope.launch(Dispatchers.Default) {
with(patchBundleRepository) {
reload()
updateCheck()
}
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package app.revanced.manager.data.platform

import android.app.Application
import android.net.ConnectivityManager
import android.net.NetworkCapabilities
import androidx.core.content.getSystemService

class NetworkInfo(app: Application) {
private val connectivityManager = app.getSystemService<ConnectivityManager>()!!

private fun getCapabilities() = connectivityManager.activeNetwork?.let { connectivityManager.getNetworkCapabilities(it) }
fun isConnected() = connectivityManager.activeNetwork != null
fun isUnmetered() = getCapabilities()?.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_METERED) ?: true

/**
* Returns true if it is safe to download large files.
*/
fun isSafe() = isConnected() && isUnmetered()
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,14 @@ import app.revanced.manager.data.room.apps.DownloadedApp
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.SourceDao
import app.revanced.manager.data.room.sources.SourceEntity
import app.revanced.manager.data.room.bundles.PatchBundleDao
import app.revanced.manager.data.room.bundles.PatchBundleEntity
import kotlin.random.Random

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

Expand Down
Original file line number Diff line number Diff line change
@@ -1,19 +1,16 @@
package app.revanced.manager.data.room

import androidx.room.TypeConverter
import app.revanced.manager.data.room.sources.SourceLocation
import app.revanced.manager.data.room.bundles.Source
import io.ktor.http.*
import java.io.File

class Converters {
@TypeConverter
fun locationFromString(value: String) = when(value) {
SourceLocation.Local.SENTINEL -> SourceLocation.Local
else -> SourceLocation.Remote(Url(value))
}
fun sourceFromString(value: String) = Source.from(value)

@TypeConverter
fun locationToString(location: SourceLocation) = location.toString()
fun sourceToString(value: Source) = value.toString()

@TypeConverter
fun fileFromString(value: String) = File(value)
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package app.revanced.manager.data.room.bundles

import androidx.room.*
import kotlinx.coroutines.flow.Flow

@Dao
interface PatchBundleDao {
@Query("SELECT * FROM patch_bundles")
suspend fun all(): List<PatchBundleEntity>

@Query("SELECT version, integrations_version, auto_update FROM patch_bundles WHERE uid = :uid")
fun getPropsById(uid: Int): Flow<BundleProperties>

@Query("UPDATE patch_bundles SET version = :patches, integrations_version = :integrations WHERE uid = :uid")
suspend fun updateVersion(uid: Int, patches: String?, integrations: String?)

@Query("UPDATE patch_bundles SET auto_update = :value WHERE uid = :uid")
suspend fun setAutoUpdate(uid: Int, value: Boolean)

@Query("DELETE FROM patch_bundles WHERE uid != 0")
suspend fun purgeCustomBundles()

@Transaction
suspend fun reset() {
purgeCustomBundles()
updateVersion(0, null, null) // Reset the main source
}

@Query("DELETE FROM patch_bundles WHERE uid = :uid")
suspend fun remove(uid: Int)

@Insert
suspend fun add(source: PatchBundleEntity)
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package app.revanced.manager.data.room.bundles

import androidx.room.*
import io.ktor.http.*

sealed class Source {
object Local : Source() {
const val SENTINEL = "local"

override fun toString() = SENTINEL
}

object API : Source() {
const val SENTINEL = "api"

override fun toString() = SENTINEL
}

data class Remote(val url: Url) : Source() {
override fun toString() = url.toString()
}

companion object {
fun from(value: String) = when(value) {
Local.SENTINEL -> Local
API.SENTINEL -> API
else -> Remote(Url(value))
}
}
}

data class VersionInfo(
@ColumnInfo(name = "version") val patches: String? = null,
@ColumnInfo(name = "integrations_version") val integrations: String? = null,
)

@Entity(tableName = "patch_bundles", indices = [Index(value = ["name"], unique = true)])
data class PatchBundleEntity(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "name") val name: String,
@Embedded val versionInfo: VersionInfo,
@ColumnInfo(name = "source") val source: Source,
@ColumnInfo(name = "auto_update") val autoUpdate: Boolean
)

data class BundleProperties(
@Embedded val versionInfo: VersionInfo,
@ColumnInfo(name = "auto_update") val autoUpdate: Boolean
)
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,20 @@ import androidx.room.Entity
import androidx.room.ForeignKey
import androidx.room.Index
import androidx.room.PrimaryKey
import app.revanced.manager.data.room.sources.SourceEntity
import app.revanced.manager.data.room.bundles.PatchBundleEntity

@Entity(
tableName = "patch_selections",
foreignKeys = [ForeignKey(
SourceEntity::class,
PatchBundleEntity::class,
parentColumns = ["uid"],
childColumns = ["source"],
childColumns = ["patch_bundle"],
onDelete = ForeignKey.CASCADE
)],
indices = [Index(value = ["source", "package_name"], unique = true)]
indices = [Index(value = ["patch_bundle", "package_name"], unique = true)]
)
data class PatchSelection(
@PrimaryKey val uid: Int,
@ColumnInfo(name = "source") val source: Int,
@ColumnInfo(name = "patch_bundle") val patchBundle: Int,
@ColumnInfo(name = "package_name") val packageName: String
)

0 comments on commit 379ce91

Please sign in to comment.