mirror of
https://github.com/revanced/revanced-manager.git
synced 2025-04-30 14:04:26 +02:00
UI refactoring/functionality WIP
This commit is contained in:
parent
770e35bfe4
commit
3f497a93d0
@ -10,7 +10,6 @@ import androidx.core.splashscreen.SplashScreen.Companion.installSplashScreen
|
|||||||
import androidx.core.view.WindowCompat
|
import androidx.core.view.WindowCompat
|
||||||
import app.revanced.manager.ui.destination.Destination
|
import app.revanced.manager.ui.destination.Destination
|
||||||
import app.revanced.manager.ui.destination.SettingsDestination
|
import app.revanced.manager.ui.destination.SettingsDestination
|
||||||
import app.revanced.manager.ui.model.SelectedApp
|
|
||||||
import app.revanced.manager.ui.screen.AppSelectorScreen
|
import app.revanced.manager.ui.screen.AppSelectorScreen
|
||||||
import app.revanced.manager.ui.screen.DashboardScreen
|
import app.revanced.manager.ui.screen.DashboardScreen
|
||||||
import app.revanced.manager.ui.screen.InstalledAppInfoScreen
|
import app.revanced.manager.ui.screen.InstalledAppInfoScreen
|
||||||
@ -99,14 +98,7 @@ class MainActivity : ComponentActivity() {
|
|||||||
is Destination.AppSelector -> AppSelectorScreen(
|
is Destination.AppSelector -> AppSelectorScreen(
|
||||||
// onAppClick = { navController.navigate(Destination.VersionSelector(it)) },
|
// onAppClick = { navController.navigate(Destination.VersionSelector(it)) },
|
||||||
// TODO: complete this feature
|
// TODO: complete this feature
|
||||||
onAppClick = { packageName, version ->
|
onSelect = {
|
||||||
navController.navigate(
|
|
||||||
Destination.SelectedApplicationInfo(
|
|
||||||
SelectedApp.Search(packageName, version)
|
|
||||||
)
|
|
||||||
)
|
|
||||||
},
|
|
||||||
onStorageClick = {
|
|
||||||
navController.navigate(
|
navController.navigate(
|
||||||
Destination.SelectedApplicationInfo(
|
Destination.SelectedApplicationInfo(
|
||||||
it
|
it
|
||||||
|
@ -17,7 +17,7 @@ class DownloadedAppRepository(app: Application, db: AppDatabase) {
|
|||||||
|
|
||||||
fun getAll() = dao.getAllApps().distinctUntilChanged()
|
fun getAll() = dao.getAllApps().distinctUntilChanged()
|
||||||
|
|
||||||
fun getApkFileForApp(app: DownloadedApp): File = getApkFileForDir(dir.resolve(app.directory))
|
private fun getApkFileForApp(app: DownloadedApp): File = getApkFileForDir(dir.resolve(app.directory))
|
||||||
private fun getApkFileForDir(directory: File) = directory.listFiles()!!.first()
|
private fun getApkFileForDir(directory: File) = directory.listFiles()!!.first()
|
||||||
|
|
||||||
suspend fun download(
|
suspend fun download(
|
||||||
|
@ -10,7 +10,6 @@ import androidx.compose.material.icons.filled.Storage
|
|||||||
import androidx.compose.material.icons.outlined.Search
|
import androidx.compose.material.icons.outlined.Search
|
||||||
import androidx.compose.material3.*
|
import androidx.compose.material3.*
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.SideEffect
|
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableStateOf
|
import androidx.compose.runtime.mutableStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
@ -33,19 +32,19 @@ import app.revanced.manager.ui.component.SearchView
|
|||||||
import app.revanced.manager.ui.model.SelectedApp
|
import app.revanced.manager.ui.model.SelectedApp
|
||||||
import app.revanced.manager.ui.viewmodel.AppSelectorViewModel
|
import app.revanced.manager.ui.viewmodel.AppSelectorViewModel
|
||||||
import app.revanced.manager.util.APK_MIMETYPE
|
import app.revanced.manager.util.APK_MIMETYPE
|
||||||
|
import app.revanced.manager.util.EventEffect
|
||||||
import app.revanced.manager.util.transparentListItemColors
|
import app.revanced.manager.util.transparentListItemColors
|
||||||
import org.koin.androidx.compose.koinViewModel
|
import org.koin.androidx.compose.koinViewModel
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
fun AppSelectorScreen(
|
fun AppSelectorScreen(
|
||||||
onAppClick: (packageName: String, version: String?) -> Unit,
|
onSelect: (SelectedApp) -> Unit,
|
||||||
onStorageClick: (SelectedApp.Local) -> Unit,
|
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
vm: AppSelectorViewModel = koinViewModel()
|
vm: AppSelectorViewModel = koinViewModel()
|
||||||
) {
|
) {
|
||||||
SideEffect {
|
EventEffect(flow = vm.appSelectionFlow) {
|
||||||
vm.onStorageClick = onStorageClick
|
onSelect(it)
|
||||||
}
|
}
|
||||||
|
|
||||||
val pickApkLauncher =
|
val pickApkLauncher =
|
||||||
@ -90,7 +89,7 @@ fun AppSelectorScreen(
|
|||||||
) { app ->
|
) { app ->
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
onAppClick(
|
vm.selectApp(
|
||||||
app.packageName,
|
app.packageName,
|
||||||
suggestedVersions[app.packageName]
|
suggestedVersions[app.packageName]
|
||||||
)
|
)
|
||||||
@ -190,7 +189,7 @@ fun AppSelectorScreen(
|
|||||||
) { app ->
|
) { app ->
|
||||||
ListItem(
|
ListItem(
|
||||||
modifier = Modifier.clickable {
|
modifier = Modifier.clickable {
|
||||||
onAppClick(
|
vm.selectApp(
|
||||||
app.packageName,
|
app.packageName,
|
||||||
suggestedVersions[app.packageName]
|
suggestedVersions[app.packageName]
|
||||||
)
|
)
|
||||||
|
@ -2,6 +2,7 @@ package app.revanced.manager.ui.screen
|
|||||||
|
|
||||||
import androidx.activity.compose.BackHandler
|
import androidx.activity.compose.BackHandler
|
||||||
import androidx.activity.compose.rememberLauncherForActivityResult
|
import androidx.activity.compose.rememberLauncherForActivityResult
|
||||||
|
import androidx.activity.result.contract.ActivityResultContracts
|
||||||
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
|
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
|
||||||
import androidx.compose.animation.AnimatedVisibility
|
import androidx.compose.animation.AnimatedVisibility
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
@ -49,7 +50,7 @@ import app.revanced.manager.ui.model.State
|
|||||||
import app.revanced.manager.ui.model.StepCategory
|
import app.revanced.manager.ui.model.StepCategory
|
||||||
import app.revanced.manager.ui.viewmodel.PatcherViewModel
|
import app.revanced.manager.ui.viewmodel.PatcherViewModel
|
||||||
import app.revanced.manager.util.APK_MIMETYPE
|
import app.revanced.manager.util.APK_MIMETYPE
|
||||||
import app.revanced.manager.util.IntentContract
|
import app.revanced.manager.util.EventEffect
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3Api::class)
|
@OptIn(ExperimentalMaterial3Api::class)
|
||||||
@Composable
|
@Composable
|
||||||
@ -95,11 +96,12 @@ fun PatcherScreen(
|
|||||||
onConfirm = vm::install
|
onConfirm = vm::install
|
||||||
)
|
)
|
||||||
|
|
||||||
val activityLauncher = rememberLauncherForActivityResult(contract = IntentContract) {
|
val activityLauncher = rememberLauncherForActivityResult(
|
||||||
vm.handleActivityResult(it)
|
contract = ActivityResultContracts.StartActivityForResult(),
|
||||||
}
|
onResult = vm::handleActivityResult
|
||||||
SideEffect {
|
)
|
||||||
vm.launchActivity = activityLauncher::launch
|
EventEffect(flow = vm.launchActivityFlow) { intent ->
|
||||||
|
activityLauncher.launch(intent)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (vm.activeInteractionRequest)
|
if (vm.activeInteractionRequest)
|
||||||
|
@ -14,7 +14,11 @@ import app.revanced.manager.ui.model.SelectedApp
|
|||||||
import app.revanced.manager.util.PM
|
import app.revanced.manager.util.PM
|
||||||
import app.revanced.manager.util.toast
|
import app.revanced.manager.util.toast
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
|
import kotlinx.coroutines.flow.MutableSharedFlow
|
||||||
|
import kotlinx.coroutines.flow.asSharedFlow
|
||||||
import kotlinx.coroutines.flow.flowOn
|
import kotlinx.coroutines.flow.flowOn
|
||||||
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -30,13 +34,19 @@ class AppSelectorViewModel(
|
|||||||
}
|
}
|
||||||
val appList = pm.appList
|
val appList = pm.appList
|
||||||
|
|
||||||
var onStorageClick: (SelectedApp.Local) -> Unit = {}
|
private val appSelectionChannel = Channel<SelectedApp>()
|
||||||
|
val appSelectionFlow = appSelectionChannel.receiveAsFlow()
|
||||||
|
|
||||||
val suggestedAppVersions = patchBundleRepository.suggestedVersions.flowOn(Dispatchers.Default)
|
val suggestedAppVersions = patchBundleRepository.suggestedVersions.flowOn(Dispatchers.Default)
|
||||||
|
|
||||||
var nonSuggestedVersionDialogSubject by mutableStateOf<SelectedApp.Local?>(null)
|
var nonSuggestedVersionDialogSubject by mutableStateOf<SelectedApp.Local?>(null)
|
||||||
private set
|
private set
|
||||||
|
|
||||||
|
private suspend fun selectApp(app: SelectedApp) = appSelectionChannel.send(app)
|
||||||
|
fun selectApp(packageName: String, version: String?) = viewModelScope.launch {
|
||||||
|
selectApp(SelectedApp.Search(packageName, version))
|
||||||
|
}
|
||||||
|
|
||||||
fun loadLabel(app: PackageInfo?) = with(pm) { app?.label() ?: "Not installed" }
|
fun loadLabel(app: PackageInfo?) = with(pm) { app?.label() ?: "Not installed" }
|
||||||
|
|
||||||
fun dismissNonSuggestedVersionDialog() {
|
fun dismissNonSuggestedVersionDialog() {
|
||||||
@ -54,7 +64,7 @@ class AppSelectorViewModel(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (patchBundleRepository.isVersionAllowed(selectedApp.packageName, selectedApp.version)) {
|
if (patchBundleRepository.isVersionAllowed(selectedApp.packageName, selectedApp.version)) {
|
||||||
onStorageClick(selectedApp)
|
selectApp(selectedApp)
|
||||||
} else {
|
} else {
|
||||||
nonSuggestedVersionDialogSubject = selectedApp
|
nonSuggestedVersionDialogSubject = selectedApp
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import android.content.IntentFilter
|
|||||||
import android.content.pm.PackageInstaller
|
import android.content.pm.PackageInstaller
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
|
import androidx.activity.result.ActivityResult
|
||||||
import androidx.compose.runtime.Stable
|
import androidx.compose.runtime.Stable
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
@ -39,7 +40,6 @@ import app.revanced.manager.ui.model.SelectedApp
|
|||||||
import app.revanced.manager.ui.model.State
|
import app.revanced.manager.ui.model.State
|
||||||
import app.revanced.manager.ui.model.Step
|
import app.revanced.manager.ui.model.Step
|
||||||
import app.revanced.manager.ui.model.StepCategory
|
import app.revanced.manager.ui.model.StepCategory
|
||||||
import app.revanced.manager.util.IntentContract
|
|
||||||
import app.revanced.manager.util.PM
|
import app.revanced.manager.util.PM
|
||||||
import app.revanced.manager.util.simpleMessage
|
import app.revanced.manager.util.simpleMessage
|
||||||
import app.revanced.manager.util.tag
|
import app.revanced.manager.util.tag
|
||||||
@ -49,8 +49,10 @@ import kotlinx.coroutines.CompletableDeferred
|
|||||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.GlobalScope
|
import kotlinx.coroutines.GlobalScope
|
||||||
|
import kotlinx.coroutines.channels.Channel
|
||||||
import kotlinx.coroutines.flow.MutableStateFlow
|
import kotlinx.coroutines.flow.MutableStateFlow
|
||||||
import kotlinx.coroutines.flow.StateFlow
|
import kotlinx.coroutines.flow.StateFlow
|
||||||
|
import kotlinx.coroutines.flow.receiveAsFlow
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import kotlinx.coroutines.time.withTimeout
|
import kotlinx.coroutines.time.withTimeout
|
||||||
import kotlinx.coroutines.withContext
|
import kotlinx.coroutines.withContext
|
||||||
@ -83,8 +85,9 @@ class PatcherViewModel(
|
|||||||
null
|
null
|
||||||
)
|
)
|
||||||
val activeInteractionRequest by derivedStateOf { currentInteractionRequest != null }
|
val activeInteractionRequest by derivedStateOf { currentInteractionRequest != null }
|
||||||
private var launchedActivity: CompletableDeferred<IntentContract.Result>? = null
|
private var launchedActivity: CompletableDeferred<ActivityResult>? = null
|
||||||
var launchActivity: (Intent) -> Unit = {}
|
private val launchActivityChannel = Channel<Intent>()
|
||||||
|
val launchActivityFlow = launchActivityChannel.receiveAsFlow()
|
||||||
|
|
||||||
private val tempDir = fs.tempDir.resolve("installer").also {
|
private val tempDir = fs.tempDir.resolve("installer").also {
|
||||||
it.deleteRecursively()
|
it.deleteRecursively()
|
||||||
@ -249,17 +252,17 @@ class PatcherViewModel(
|
|||||||
withContext(Dispatchers.Main) {
|
withContext(Dispatchers.Main) {
|
||||||
if (launchedActivity != null) throw Exception("An activity has already been launched.")
|
if (launchedActivity != null) throw Exception("An activity has already been launched.")
|
||||||
try {
|
try {
|
||||||
val job = CompletableDeferred<IntentContract.Result>()
|
val job = CompletableDeferred<ActivityResult>()
|
||||||
launchActivity(intent)
|
launchActivityChannel.send(intent)
|
||||||
|
|
||||||
launchedActivity = job
|
launchedActivity = job
|
||||||
val result = job.await()
|
val result = job.await()
|
||||||
when (result.code) {
|
when (result.resultCode) {
|
||||||
Activity.RESULT_OK -> result.intent
|
Activity.RESULT_OK -> result.data
|
||||||
Activity.RESULT_CANCELED -> throw UserInteractionException.Activity.Cancelled()
|
Activity.RESULT_CANCELED -> throw UserInteractionException.Activity.Cancelled()
|
||||||
else -> throw UserInteractionException.Activity.NotCompleted(
|
else -> throw UserInteractionException.Activity.NotCompleted(
|
||||||
result.code,
|
result.resultCode,
|
||||||
result.intent
|
result.data
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@ -269,7 +272,7 @@ class PatcherViewModel(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fun handleActivityResult(result: IntentContract.Result) {
|
fun handleActivityResult(result: ActivityResult) {
|
||||||
launchedActivity?.complete(result)
|
launchedActivity?.complete(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,12 +0,0 @@
|
|||||||
package app.revanced.manager.util
|
|
||||||
|
|
||||||
import android.content.Context
|
|
||||||
import android.content.Intent
|
|
||||||
import androidx.activity.result.contract.ActivityResultContract
|
|
||||||
|
|
||||||
object IntentContract : ActivityResultContract<Intent, IntentContract.Result>() {
|
|
||||||
override fun createIntent(context: Context, input: Intent) = input
|
|
||||||
override fun parseResult(resultCode: Int, intent: Intent?) = Result(resultCode, intent)
|
|
||||||
|
|
||||||
class Result(val code: Int, val intent: Intent?)
|
|
||||||
}
|
|
@ -15,18 +15,20 @@ import androidx.compose.foundation.ScrollState
|
|||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.material3.ListItemColors
|
import androidx.compose.material3.ListItemColors
|
||||||
import androidx.compose.material3.ListItemDefaults
|
import androidx.compose.material3.ListItemDefaults
|
||||||
import androidx.compose.material3.LocalContentColor
|
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
import androidx.compose.runtime.State
|
import androidx.compose.runtime.State
|
||||||
import androidx.compose.runtime.derivedStateOf
|
import androidx.compose.runtime.derivedStateOf
|
||||||
import androidx.compose.runtime.getValue
|
import androidx.compose.runtime.getValue
|
||||||
import androidx.compose.runtime.mutableIntStateOf
|
import androidx.compose.runtime.mutableIntStateOf
|
||||||
import androidx.compose.runtime.remember
|
import androidx.compose.runtime.remember
|
||||||
|
import androidx.compose.runtime.rememberUpdatedState
|
||||||
import androidx.compose.runtime.setValue
|
import androidx.compose.runtime.setValue
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.core.net.toUri
|
import androidx.core.net.toUri
|
||||||
import androidx.lifecycle.Lifecycle
|
import androidx.lifecycle.Lifecycle
|
||||||
import androidx.lifecycle.LifecycleOwner
|
import androidx.lifecycle.LifecycleOwner
|
||||||
|
import androidx.lifecycle.compose.LocalLifecycleOwner
|
||||||
import androidx.lifecycle.lifecycleScope
|
import androidx.lifecycle.lifecycleScope
|
||||||
import androidx.lifecycle.repeatOnLifecycle
|
import androidx.lifecycle.repeatOnLifecycle
|
||||||
import app.revanced.manager.R
|
import app.revanced.manager.R
|
||||||
@ -201,6 +203,20 @@ val transparentListItemColors
|
|||||||
?: ListItemDefaults.colors(containerColor = Color.Transparent)
|
?: ListItemDefaults.colors(containerColor = Color.Transparent)
|
||||||
.also { transparentListItemColorsCached = it }
|
.also { transparentListItemColorsCached = it }
|
||||||
|
|
||||||
|
@Composable
|
||||||
|
fun <T> EventEffect(flow: Flow<T>, vararg keys: Any?, state: Lifecycle.State = Lifecycle.State.STARTED, block: suspend (T) -> Unit) {
|
||||||
|
val lifecycleOwner = LocalLifecycleOwner.current
|
||||||
|
val currentBlock by rememberUpdatedState(block)
|
||||||
|
|
||||||
|
LaunchedEffect(flow, state, *keys) {
|
||||||
|
lifecycleOwner.repeatOnLifecycle(state) {
|
||||||
|
flow.collect {
|
||||||
|
currentBlock(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
const val isScrollingUpSensitivity = 10
|
const val isScrollingUpSensitivity = 10
|
||||||
|
|
||||||
@Composable
|
@Composable
|
||||||
|
Loading…
x
Reference in New Issue
Block a user