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 app.revanced.manager.ui.destination.Destination
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.DashboardScreen
import app.revanced.manager.ui.screen.InstalledAppInfoScreen
@ -99,14 +98,7 @@ class MainActivity : ComponentActivity() {
is Destination.AppSelector -> AppSelectorScreen(
// onAppClick = { navController.navigate(Destination.VersionSelector(it)) },
// TODO: complete this feature
onAppClick = { packageName, version ->
navController.navigate(
Destination.SelectedApplicationInfo(
SelectedApp.Search(packageName, version)
)
)
},
onStorageClick = {
onSelect = {
navController.navigate(
Destination.SelectedApplicationInfo(
it

View file

@ -17,7 +17,7 @@ class DownloadedAppRepository(app: Application, db: AppDatabase) {
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()
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.material3.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.SideEffect
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
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.viewmodel.AppSelectorViewModel
import app.revanced.manager.util.APK_MIMETYPE
import app.revanced.manager.util.EventEffect
import app.revanced.manager.util.transparentListItemColors
import org.koin.androidx.compose.koinViewModel
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun AppSelectorScreen(
onAppClick: (packageName: String, version: String?) -> Unit,
onStorageClick: (SelectedApp.Local) -> Unit,
onSelect: (SelectedApp) -> Unit,
onBackClick: () -> Unit,
vm: AppSelectorViewModel = koinViewModel()
) {
SideEffect {
vm.onStorageClick = onStorageClick
EventEffect(flow = vm.appSelectionFlow) {
onSelect(it)
}
val pickApkLauncher =
@ -90,7 +89,7 @@ fun AppSelectorScreen(
) { app ->
ListItem(
modifier = Modifier.clickable {
onAppClick(
vm.selectApp(
app.packageName,
suggestedVersions[app.packageName]
)
@ -190,7 +189,7 @@ fun AppSelectorScreen(
) { app ->
ListItem(
modifier = Modifier.clickable {
onAppClick(
vm.selectApp(
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.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
import androidx.compose.animation.AnimatedVisibility
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.viewmodel.PatcherViewModel
import app.revanced.manager.util.APK_MIMETYPE
import app.revanced.manager.util.IntentContract
import app.revanced.manager.util.EventEffect
@OptIn(ExperimentalMaterial3Api::class)
@Composable
@ -95,11 +96,12 @@ fun PatcherScreen(
onConfirm = vm::install
)
val activityLauncher = rememberLauncherForActivityResult(contract = IntentContract) {
vm.handleActivityResult(it)
}
SideEffect {
vm.launchActivity = activityLauncher::launch
val activityLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.StartActivityForResult(),
onResult = vm::handleActivityResult
)
EventEffect(flow = vm.launchActivityFlow) { intent ->
activityLauncher.launch(intent)
}
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.toast
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.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
import java.io.File
@ -30,13 +34,19 @@ class AppSelectorViewModel(
}
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)
var nonSuggestedVersionDialogSubject by mutableStateOf<SelectedApp.Local?>(null)
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 dismissNonSuggestedVersionDialog() {
@ -54,7 +64,7 @@ class AppSelectorViewModel(
}
if (patchBundleRepository.isVersionAllowed(selectedApp.packageName, selectedApp.version)) {
onStorageClick(selectedApp)
selectApp(selectedApp)
} else {
nonSuggestedVersionDialogSubject = selectedApp
}

View file

@ -9,6 +9,7 @@ import android.content.IntentFilter
import android.content.pm.PackageInstaller
import android.net.Uri
import android.util.Log
import androidx.activity.result.ActivityResult
import androidx.compose.runtime.Stable
import androidx.compose.runtime.derivedStateOf
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.Step
import app.revanced.manager.ui.model.StepCategory
import app.revanced.manager.util.IntentContract
import app.revanced.manager.util.PM
import app.revanced.manager.util.simpleMessage
import app.revanced.manager.util.tag
@ -49,8 +49,10 @@ import kotlinx.coroutines.CompletableDeferred
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.MutableStateFlow
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.launch
import kotlinx.coroutines.time.withTimeout
import kotlinx.coroutines.withContext
@ -83,8 +85,9 @@ class PatcherViewModel(
null
)
val activeInteractionRequest by derivedStateOf { currentInteractionRequest != null }
private var launchedActivity: CompletableDeferred<IntentContract.Result>? = null
var launchActivity: (Intent) -> Unit = {}
private var launchedActivity: CompletableDeferred<ActivityResult>? = null
private val launchActivityChannel = Channel<Intent>()
val launchActivityFlow = launchActivityChannel.receiveAsFlow()
private val tempDir = fs.tempDir.resolve("installer").also {
it.deleteRecursively()
@ -249,17 +252,17 @@ class PatcherViewModel(
withContext(Dispatchers.Main) {
if (launchedActivity != null) throw Exception("An activity has already been launched.")
try {
val job = CompletableDeferred<IntentContract.Result>()
launchActivity(intent)
val job = CompletableDeferred<ActivityResult>()
launchActivityChannel.send(intent)
launchedActivity = job
val result = job.await()
when (result.code) {
Activity.RESULT_OK -> result.intent
when (result.resultCode) {
Activity.RESULT_OK -> result.data
Activity.RESULT_CANCELED -> throw UserInteractionException.Activity.Cancelled()
else -> throw UserInteractionException.Activity.NotCompleted(
result.code,
result.intent
result.resultCode,
result.data
)
}
} finally {
@ -269,7 +272,7 @@ class PatcherViewModel(
})
}
fun handleActivityResult(result: IntentContract.Result) {
fun handleActivityResult(result: ActivityResult) {
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.material3.ListItemColors
import androidx.compose.material3.ListItemDefaults
import androidx.compose.material3.LocalContentColor
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.State
import androidx.compose.runtime.derivedStateOf
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableIntStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberUpdatedState
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.Color
import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.compose.LocalLifecycleOwner
import androidx.lifecycle.lifecycleScope
import androidx.lifecycle.repeatOnLifecycle
import app.revanced.manager.R
@ -201,6 +203,20 @@ val transparentListItemColors
?: ListItemDefaults.colors(containerColor = Color.Transparent)
.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
@Composable