Skip to content

Commit

Permalink
[optimize|build] Optimize the MVI event listening method; update depe…
Browse files Browse the repository at this point in the history
…ndencies
  • Loading branch information
SkyD666 committed Aug 25, 2024
1 parent 5595d3c commit 665c341
Show file tree
Hide file tree
Showing 21 changed files with 190 additions and 271 deletions.
10 changes: 5 additions & 5 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ android {
minSdk = 24
targetSdk = 34
versionCode = 67
versionName = "2.3-alpha03"
versionName = "2.3-alpha04"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
vectorDrawables {
Expand Down Expand Up @@ -157,8 +157,8 @@ dependencies {
implementation("androidx.lifecycle:lifecycle-runtime-ktx:2.8.4")
implementation("androidx.activity:activity-compose:1.9.1")
implementation("androidx.palette:palette-ktx:1.0.0")
implementation("com.google.dagger:hilt-android:2.51.1")
ksp("com.google.dagger:hilt-android-compiler:2.51.1")
implementation("com.google.dagger:hilt-android:2.52")
ksp("com.google.dagger:hilt-android-compiler:2.52")
implementation("androidx.hilt:hilt-navigation-compose:1.2.0")
implementation("androidx.navigation:navigation-compose:2.7.7")
implementation("androidx.security:security-crypto:1.1.0-alpha06")
Expand All @@ -185,8 +185,8 @@ dependencies {
implementation("com.google.mlkit:text-recognition-chinese:$mlkitRecognitionVersion")
implementation("com.google.mlkit:text-recognition-japanese:$mlkitRecognitionVersion")
implementation("com.google.mlkit:text-recognition-korean:$mlkitRecognitionVersion")
implementation("com.google.mlkit:image-labeling-custom:17.0.2")
implementation("com.google.mlkit:segmentation-selfie:16.0.0-beta5")
implementation("com.google.mlkit:image-labeling-custom:17.0.3")
implementation("com.google.mlkit:segmentation-selfie:16.0.0-beta6")

// TF Lite
implementation("org.tensorflow:tensorflow-lite:2.16.1")
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/java/com/skyd/rays/base/mvi/MviSingleEvent.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
package com.skyd.rays.base.mvi

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.skyd.rays.ext.collectIn
import kotlinx.coroutines.flow.Flow

/**
* Immutable object which represents a single event
* like snack bar message, navigation event, a dialog trigger, etc...
*/
interface MviSingleEvent

@Composable
fun <T : MviSingleEvent> MviEventListener(
eventFlow: Flow<T>,
onEach: suspend (event: T) -> Unit,
) {
val lifecycleOwner = LocalLifecycleOwner.current
LaunchedEffect(Unit) {
eventFlow.collectIn(lifecycleOwner, action = onEach)
}
}
14 changes: 14 additions & 0 deletions app/src/main/java/com/skyd/rays/ext/FlowExt.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.skyd.rays.ext

import androidx.lifecycle.Lifecycle
import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.flowWithLifecycle
import androidx.lifecycle.lifecycleScope
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineStart
import kotlinx.coroutines.Job
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.FlowCollector
import kotlinx.coroutines.flow.buffer
Expand Down Expand Up @@ -64,4 +69,13 @@ fun <T> Flow<Flow<T>>.flattenFirst(): Flow<T> = channelFlow {
}
}
}
}

// collect with lifecycle
fun <T> Flow<T>.collectIn(
lifecycleOwner: LifecycleOwner,
minActiveState: Lifecycle.State = Lifecycle.State.STARTED,
action: suspend (T) -> Unit = {},
): Job = lifecycleOwner.lifecycleScope.launch {
flowWithLifecycle(lifecycleOwner.lifecycle, minActiveState).collect(action)
}
14 changes: 12 additions & 2 deletions app/src/main/java/com/skyd/rays/ext/NavExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,12 @@ package com.skyd.rays.ext
import android.os.Bundle
import androidx.core.net.toUri
import androidx.lifecycle.Lifecycle
import androidx.navigation.*
import androidx.navigation.NavBackStackEntry
import androidx.navigation.NavController
import androidx.navigation.NavDeepLinkRequest
import androidx.navigation.NavDestination
import androidx.navigation.NavOptions
import androidx.navigation.Navigator

fun NavController.navigate(
route: String,
Expand All @@ -20,7 +25,12 @@ fun NavController.navigate(
if (deepLinkMatch != null) {
val destination = deepLinkMatch.destination
val id = destination.id
navigate(id, args, navOptions, navigatorExtras)
navigate(
id,
args.apply { putAll(deepLinkMatch.matchingArgs ?: Bundle()) },
navOptions,
navigatorExtras
)
} else {
navigate(route, navOptions, navigatorExtras)
}
Expand Down
23 changes: 0 additions & 23 deletions app/src/main/java/com/skyd/rays/ext/SnackbarExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package com.skyd.rays.ext

import androidx.compose.material3.SnackbarDuration
import androidx.compose.material3.SnackbarHostState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.launch

Expand All @@ -22,25 +20,4 @@ fun SnackbarHostState.showSnackbar(
duration = duration,
)
}
}

@Composable
fun SnackbarHostState.showSnackbarWithLaunchedEffect(
message: String,
key1: Any? = this,
key2: Any? = null,
key3: Any? = null,
actionLabel: String? = null,
withDismissAction: Boolean = true,
duration: SnackbarDuration = if (actionLabel == null) SnackbarDuration.Short else SnackbarDuration.Indefinite
): SnackbarHostState {
LaunchedEffect(key1, key2, key3) {
showSnackbar(
message = message,
actionLabel = actionLabel,
withDismissAction = withDismissAction,
duration = duration,
)
}
return this
}
13 changes: 7 additions & 6 deletions app/src/main/java/com/skyd/rays/ui/component/Toast.kt
Original file line number Diff line number Diff line change
@@ -1,16 +1,17 @@
package com.skyd.rays.ui.component

import android.os.Handler
import android.os.Looper
import android.widget.Toast
import com.skyd.rays.appContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch

private var uiThreadHandler: Handler = Handler(Looper.getMainLooper())
private var scope = CoroutineScope(Dispatchers.Main.immediate)

fun CharSequence.showToast(duration: Int = Toast.LENGTH_SHORT) {
uiThreadHandler.post {
val toast = Toast.makeText(appContext, this, duration)
scope.launch {
val toast = Toast.makeText(appContext, this@showToast, duration)
toast.duration = duration
toast.show()
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ import androidx.compose.ui.viewinterop.AndroidView
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.skyd.rays.R
import com.skyd.rays.base.mvi.MviEventListener
import com.skyd.rays.base.mvi.getDispatcher
import com.skyd.rays.model.bean.UpdateBean
import com.skyd.rays.model.preference.IgnoreUpdateVersionPreference
Expand All @@ -54,8 +55,6 @@ fun UpdateDialog(
viewModel: UpdateViewModel = hiltViewModel()
) {
val uiState by viewModel.viewState.collectAsStateWithLifecycle()
val uiEvent by viewModel.singleEvent.collectAsStateWithLifecycle(initialValue = null)

val dispatch = viewModel.getDispatcher(startWith = UpdateIntent.CheckUpdate(isRetry = false))

LaunchedEffect(Unit) {
Expand Down Expand Up @@ -95,13 +94,11 @@ fun UpdateDialog(
}
}

when (val event = uiEvent) {
is UpdateEvent.CheckError -> LaunchedEffect(event) {
onError(event.msg)
MviEventListener(viewModel.singleEvent) { event ->
when (event) {
is UpdateEvent.CheckError -> onError(event.msg)
is UpdateEvent.CheckSuccess -> onSuccess()
}

is UpdateEvent.CheckSuccess -> onSuccess()
null -> Unit
}
}

Expand Down
53 changes: 22 additions & 31 deletions app/src/main/java/com/skyd/rays/ui/screen/add/AddScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -96,11 +96,11 @@ import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import androidx.navigation.NavOptions
import com.skyd.rays.R
import com.skyd.rays.base.mvi.MviEventListener
import com.skyd.rays.base.mvi.getDispatcher
import com.skyd.rays.ext.navigate
import com.skyd.rays.ext.popBackStackWithLifecycle
import com.skyd.rays.ext.showSnackbar
import com.skyd.rays.ext.showSnackbarWithLaunchedEffect
import com.skyd.rays.model.bean.StickerBean
import com.skyd.rays.model.bean.StickerWithTags
import com.skyd.rays.model.bean.TagBean
Expand Down Expand Up @@ -152,8 +152,6 @@ fun AddScreen(
var openErrorDialog by rememberSaveable { mutableStateOf<String?>(null) }
var saveButtonEnable by rememberSaveable { mutableStateOf(true) }
val uiState by viewModel.viewState.collectAsStateWithLifecycle()
val uiEvent by viewModel.singleEvent.collectAsStateWithLifecycle(initialValue = null)

val dispatch = viewModel.getDispatcher(startWith = AddIntent.Init(initStickers))

// 添加/修改完成后重设页面数据
Expand Down Expand Up @@ -339,29 +337,28 @@ fun AddScreen(
}
}

when (val event = uiEvent) {
is AddEvent.AddStickersResultEvent.Duplicate -> LaunchedEffect(event) {
saveButtonEnable = true
openDuplicateDialog = true
onGetStickersWithTagsStateChanged()
}
MviEventListener(viewModel.singleEvent) { event ->
when (event) {
is AddEvent.AddStickersResultEvent.Duplicate -> {
saveButtonEnable = true
openDuplicateDialog = true
onGetStickersWithTagsStateChanged()
}

is AddEvent.AddStickersResultEvent.Failed -> {
saveButtonEnable = true
snackbarHostState.showSnackbarWithLaunchedEffect(
message = context.getString(R.string.failed_info, event.msg),
key1 = event,
)
}
is AddEvent.AddStickersResultEvent.Failed -> {
saveButtonEnable = true
snackbarHostState.showSnackbar(
context.getString(R.string.failed_info, event.msg)
)
}

is AddEvent.AddStickersResultEvent.Success -> LaunchedEffect(event) {
saveButtonEnable = true
resetStickerData()
processNext()
}
is AddEvent.AddStickersResultEvent.Success -> {
saveButtonEnable = true
resetStickerData()
processNext()
}

AddEvent.CurrentStickerChanged -> {
LaunchedEffect(uiState.currentSticker) {
AddEvent.CurrentStickerChanged -> {
val currentSticker = uiState.currentSticker
resetStickerData()
if (currentSticker?.stickerUuid.isNullOrBlank()) {
Expand All @@ -373,18 +370,12 @@ fun AddScreen(
currentSticker.uri?.let { uri ->
dispatch(AddIntent.GetSuggestTags(uri))
}
} else {
processNext()
}
}
}

AddEvent.GetStickersWithTagsStateChanged -> LaunchedEffect(uiState.getStickersWithTagsState) {
onGetStickersWithTagsStateChanged()
AddEvent.GetStickersWithTagsStateChanged -> onGetStickersWithTagsStateChanged()
is AddEvent.InitFailed -> openErrorDialog = event.msg
}

is AddEvent.InitFailed -> openErrorDialog = event.msg
null -> Unit
}

RaysDialog(
Expand Down
33 changes: 10 additions & 23 deletions app/src/main/java/com/skyd/rays/ui/screen/detail/DetailScreen.kt
Original file line number Diff line number Diff line change
Expand Up @@ -67,12 +67,12 @@ import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import androidx.navigation.NavHostController
import com.skyd.rays.R
import com.skyd.rays.base.mvi.MviEventListener
import com.skyd.rays.base.mvi.getDispatcher
import com.skyd.rays.ext.isCompact
import com.skyd.rays.ext.navigate
import com.skyd.rays.ext.popBackStackWithLifecycle
import com.skyd.rays.ext.showSnackbar
import com.skyd.rays.ext.showSnackbarWithLaunchedEffect
import com.skyd.rays.ext.toDateTimeString
import com.skyd.rays.model.bean.StickerWithTags
import com.skyd.rays.model.bean.UriWithStickerUuidBean
Expand Down Expand Up @@ -126,7 +126,6 @@ fun DetailScreen(stickerUuid: String, viewModel: DetailViewModel = hiltViewModel
var openStickerInfoDialog by rememberSaveable { mutableStateOf(false) }
var openStickerScaleSheet by rememberSaveable { mutableStateOf(false) }
val uiState by viewModel.viewState.collectAsStateWithLifecycle()
val uiEvent by viewModel.singleEvent.collectAsStateWithLifecycle(initialValue = null)
val mainCardScrollState = rememberScrollState()
val windowSizeClass = LocalWindowSizeClass.current
var fabHeight by remember { mutableStateOf(0.dp) }
Expand Down Expand Up @@ -264,31 +263,19 @@ fun DetailScreen(stickerUuid: String, viewModel: DetailViewModel = hiltViewModel
)
}

when (uiEvent) {
is DetailEvent.DeleteResult.Success -> navController.popBackStackWithLifecycle()
MviEventListener(viewModel.singleEvent) { event ->
when (event) {
is DetailEvent.DeleteResult.Success -> navController.popBackStackWithLifecycle()

DetailEvent.ExportResult.Success -> {
snackbarHostState.showSnackbarWithLaunchedEffect(
message = context.getString(R.string.export_sticker_success),
key2 = uiEvent,
)
}
DetailEvent.ExportResult.Success ->
snackbarHostState.showSnackbar(context.getString(R.string.export_sticker_success))

DetailEvent.ExportResult.Failed -> {
snackbarHostState.showSnackbarWithLaunchedEffect(
message = context.getString(R.string.export_sticker_failed),
key2 = uiEvent,
)
}
DetailEvent.ExportResult.Failed ->
snackbarHostState.showSnackbar(context.getString(R.string.export_sticker_failed))

DetailEvent.DeleteResult.Failed -> {
snackbarHostState.showSnackbarWithLaunchedEffect(
message = context.getString(R.string.delete_sticker_failed),
key2 = uiEvent,
)
DetailEvent.DeleteResult.Failed ->
snackbarHostState.showSnackbar(context.getString(R.string.delete_sticker_failed))
}

null -> Unit
}

DeleteWarningDialog(
Expand Down
Loading

0 comments on commit 665c341

Please sign in to comment.