UI refactoring/functionality WIP

This commit is contained in:
Ax333l 2024-08-06 13:23:27 +02:00
parent 770e35bfe4
commit 3f497a93d0
No known key found for this signature in database
GPG Key ID: D2B4D85271127D23
8 changed files with 58 additions and 48 deletions

View File

@ -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

View File

@ -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(

View File

@ -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]
) )

View File

@ -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)

View File

@ -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
} }

View File

@ -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)
} }

View File

@ -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?)
}

View File

@ -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