mirror of
https://github.com/ReVanced/revanced-manager.git
synced 2024-11-12 18:04:28 +01:00
UI refactoring/functionality WIP
This commit is contained in:
parent
770e35bfe4
commit
3f497a93d0
8 changed files with 58 additions and 48 deletions
|
@ -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
|
||||
|
|
|
@ -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(
|
||||
|
|
|
@ -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]
|
||||
)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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.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
|
||||
|
|
Loading…
Reference in a new issue