Skip to content

Commit

Permalink
[optimize|fix|build] Optimize the MVI event listening method; fix wro…
Browse files Browse the repository at this point in the history
…ng player window inset handling; optimize copy experience; update dependencies
  • Loading branch information
SkyD666 committed Aug 25, 2024
1 parent e444831 commit 367b368
Show file tree
Hide file tree
Showing 31 changed files with 320 additions and 352 deletions.
4 changes: 2 additions & 2 deletions app/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ android {
minSdk = 24
targetSdk = 35
versionCode = 22
versionName = "2.1-alpha22"
versionName = "2.1-alpha23"

testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"

Expand Down Expand Up @@ -207,7 +207,7 @@ dependencies {
exclude(group = "net.sf.kxml", module = "kxml2")
}
implementation("net.dankito.readability4j:readability4j:1.0.8")
implementation("sh.calvin.reorderable:reorderable:2.3.0")
implementation("sh.calvin.reorderable:reorderable:2.3.1")

implementation("org.libtorrent4j:libtorrent4j-android-arm64:2.1.0-31")
implementation("org.libtorrent4j:libtorrent4j-android-arm:2.1.0-31")
Expand Down
18 changes: 18 additions & 0 deletions app/src/main/java/com/skyd/anivu/base/mvi/MviSingleEvent.kt
Original file line number Diff line number Diff line change
@@ -1,6 +1,24 @@
package com.skyd.anivu.base.mvi

import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.lifecycle.compose.LocalLifecycleOwner
import com.skyd.anivu.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)
}
}
4 changes: 2 additions & 2 deletions app/src/main/java/com/skyd/anivu/ext/FlowExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -74,11 +74,11 @@ 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: (T) -> Unit
action: suspend (T) -> Unit = {},
): Job = lifecycleOwner.lifecycleScope.launch {
flowWithLifecycle(lifecycleOwner.lifecycle, minActiveState).collect(action)
}
9 changes: 6 additions & 3 deletions app/src/main/java/com/skyd/anivu/ext/IOExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import android.content.Context
import android.content.Intent
import android.database.Cursor
import android.net.Uri
import android.os.Build
import android.provider.DocumentsContract
import android.provider.OpenableColumns
import android.webkit.URLUtil
Expand Down Expand Up @@ -244,8 +245,10 @@ inline val String.extName: String
fun Uri.copyToClipboard(context: Context, mimeType: String? = null) {
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(
ClipData("AniVu", arrayOf(mimeType), ClipData.Item(this)).apply {
addItem(ClipData.Item(this@copyToClipboard))
}
ClipData(context.applicationInfo.name, arrayOf(mimeType), ClipData.Item(this))
)
// If you show a copy confirmation toast in Android 13, the user sees duplicate messages.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
context.getString(R.string.copied).showToast()
}
}
23 changes: 0 additions & 23 deletions app/src/main/java/com/skyd/anivu/ext/SnackbarExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,6 @@ package com.skyd.anivu.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? = null,
key2: Any? = null,
key3: Any? = this,
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
}
7 changes: 7 additions & 0 deletions app/src/main/java/com/skyd/anivu/ext/StringExt.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,10 @@ import android.content.ClipData
import android.content.ClipboardManager
import android.content.Context
import android.net.Uri
import android.os.Build
import androidx.core.text.parseAsHtml
import com.skyd.anivu.R
import com.skyd.anivu.ui.component.showToast
import net.dankito.readability4j.extended.Readability4JExtended

fun String.toEncodedUrl(allow: String? = ":/-![].,%?&="): String {
Expand All @@ -24,6 +27,10 @@ fun String.copy(context: Context) {
try {
val systemService = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
systemService.setPrimaryClip(ClipData.newPlainText("text", this))
// If you show a copy confirmation toast in Android 13, the user sees duplicate messages.
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.S_V2) {
context.getString(R.string.copied).showToast()
}
} catch (e: Exception) {
e.printStackTrace()
}
Expand Down
18 changes: 17 additions & 1 deletion app/src/main/java/com/skyd/anivu/ext/WindowExt.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
package com.skyd.anivu.ext

import android.graphics.Rect
import androidx.compose.foundation.layout.WindowInsets
import androidx.compose.material3.windowsizeclass.WindowSizeClass
import androidx.compose.material3.windowsizeclass.WindowWidthSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.platform.LocalLayoutDirection

val WindowSizeClass.isCompact: Boolean
get() = widthSizeClass == WindowWidthSizeClass.Compact
Expand All @@ -10,4 +15,15 @@ val WindowSizeClass.isMedium: Boolean
get() = widthSizeClass == WindowWidthSizeClass.Medium

val WindowSizeClass.isExpanded: Boolean
get() = widthSizeClass == WindowWidthSizeClass.Expanded
get() = widthSizeClass == WindowWidthSizeClass.Expanded

@Composable
fun WindowInsets.toRect(): Rect {
val density = LocalDensity.current
return Rect(
getLeft(density, LocalLayoutDirection.current),
getTop(density),
getRight(density, LocalLayoutDirection.current),
getBottom(density)
)
}
Original file line number Diff line number Diff line change
Expand Up @@ -69,31 +69,27 @@ class ReadRepository @Inject constructor(
fun shareImage(url: String): Flow<Unit> {
return flow {
val imageFile = getImageByCoil(url)
val tempImg = File(TEMP_PICTURES_DIR, imageFile.name)
val format = imageFile.inputStream().use { ImageFormatChecker.check(it) }
val tempImg = File(TEMP_PICTURES_DIR, imageFile.name + format.toString())
imageFile.copyTo(tempImg, overwrite = true)

val nowTime = System.currentTimeMillis().milliseconds

coroutineScope { deleteOldTempFiles(currentFile = imageFile) }

val format = tempImg.inputStream().use { ImageFormatChecker.check(it) }
tempImg.toUri(appContext).share(appContext, mimeType = format.toMimeType())

emit(Unit)
}.flowOn(Dispatchers.IO)
}

fun copyImage(url: String): Flow<Unit> {
return flow {
val imageFile = getImageByCoil(url)
val tempImg = File(TEMP_PICTURES_DIR, imageFile.name)
val format = imageFile.inputStream().use { ImageFormatChecker.check(it) }
val tempImg = File(TEMP_PICTURES_DIR, imageFile.name + format.toString())
imageFile.copyTo(tempImg, overwrite = true)

coroutineScope { deleteOldTempFiles(currentFile = imageFile) }

val format = tempImg.inputStream().use { ImageFormatChecker.check(it) }
tempImg.toUri(appContext).copyToClipboard(appContext, format.toMimeType())

emit(Unit)
}.flowOn(Dispatchers.IO)
}
Expand Down
22 changes: 3 additions & 19 deletions app/src/main/java/com/skyd/anivu/ui/activity/CrashActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,16 @@ import androidx.compose.material3.windowsizeclass.calculateWindowSizeClass
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.remember
import androidx.compose.runtime.rememberCoroutineScope
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.ClipboardManager
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.unit.dp
import com.skyd.anivu.R
import com.skyd.anivu.config.Const.GITHUB_NEW_ISSUE_URL
import com.skyd.anivu.ext.copy
import com.skyd.anivu.ext.getAppVersionCode
import com.skyd.anivu.ext.getAppVersionName
import com.skyd.anivu.ext.openBrowser
import com.skyd.anivu.ext.showSnackbar
import com.skyd.anivu.model.preference.SettingsProvider
import com.skyd.anivu.ui.local.LocalDarkMode
import com.skyd.anivu.ui.local.LocalWindowSizeClass
Expand Down Expand Up @@ -111,8 +107,6 @@ private fun CrashScreen(
) {
val snackbarHostState = remember { SnackbarHostState() }
val context = LocalContext.current
val scope = rememberCoroutineScope()
val clipboardManager = LocalClipboardManager.current

Scaffold(
snackbarHost = { SnackbarHost(hostState = snackbarHostState) },
Expand All @@ -138,20 +132,14 @@ private fun CrashScreen(

Spacer(modifier = Modifier.height(30.dp))
Row(modifier = Modifier.fillMaxWidth(), horizontalArrangement = Arrangement.End) {
TextButton(onClick = {
copyToClipboard(message, clipboardManager)
snackbarHostState.showSnackbar(
scope = scope,
message = context.getString(R.string.copied),
)
}) {
TextButton(onClick = { message.copy(context) }) {
Text(text = stringResource(id = R.string.crash_screen_copy_crash_log))
}

Spacer(modifier = Modifier.width(12.dp))

Button(onClick = {
copyToClipboard(message, clipboardManager)
message.copy(context)
onReport()
}) {
Text(text = stringResource(id = R.string.submit_an_issue_on_github))
Expand All @@ -170,8 +158,4 @@ private fun CrashScreen(
}
}
}
}

private fun copyToClipboard(text: String, clipboardManager: ClipboardManager) {
clipboardManager.setText(AnnotatedString(text))
}
4 changes: 2 additions & 2 deletions app/src/main/java/com/skyd/anivu/ui/activity/MainActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -77,8 +77,8 @@ import com.skyd.anivu.ui.screen.filepicker.FILE_PICKER_SCREEN_ROUTE
import com.skyd.anivu.ui.screen.filepicker.FilePickerScreen
import com.skyd.anivu.ui.screen.filepicker.PATH_KEY
import com.skyd.anivu.ui.screen.filepicker.PICK_FOLDER_KEY
import com.skyd.anivu.ui.screen.license.LICENSE_SCREEN_ROUTE
import com.skyd.anivu.ui.screen.license.LicenseScreen
import com.skyd.anivu.ui.screen.about.license.LICENSE_SCREEN_ROUTE
import com.skyd.anivu.ui.screen.about.license.LicenseScreen
import com.skyd.anivu.ui.screen.media.sub.SUB_MEDIA_SCREEN_PATH_KEY
import com.skyd.anivu.ui.screen.media.sub.SUB_MEDIA_SCREEN_ROUTE
import com.skyd.anivu.ui.screen.media.sub.SubMediaScreenRoute
Expand Down
9 changes: 0 additions & 9 deletions app/src/main/java/com/skyd/anivu/ui/component/Toast.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.skyd.anivu.ui.component

import android.widget.Toast
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import com.skyd.anivu.appContext
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand All @@ -16,11 +14,4 @@ fun CharSequence.showToast(duration: Int = Toast.LENGTH_SHORT) {
toast.duration = duration
toast.show()
}
}

@Composable
fun Toast(vararg keys: Any?, text: CharSequence, duration: Int = Toast.LENGTH_SHORT) {
LaunchedEffect(*keys) {
text.showToast(duration)
}
}
15 changes: 8 additions & 7 deletions app/src/main/java/com/skyd/anivu/ui/mpv/PlayerView.kt
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ import androidx.core.view.doOnDetach
import androidx.hilt.navigation.compose.hiltViewModel
import androidx.lifecycle.Lifecycle
import androidx.lifecycle.compose.collectAsStateWithLifecycle
import com.skyd.anivu.base.mvi.MviEventListener
import com.skyd.anivu.base.mvi.getDispatcher
import com.skyd.anivu.config.Const
import com.skyd.anivu.ext.activity
Expand Down Expand Up @@ -157,7 +158,6 @@ fun PlayerView(
val scope = rememberCoroutineScope()
val context = LocalContext.current

val uiEvent by viewModel.singleEvent.collectAsStateWithLifecycle(initialValue = null)
val uiState by viewModel.viewState.collectAsStateWithLifecycle()
val dispatcher = viewModel.getDispatcher(startWith = null)

Expand Down Expand Up @@ -299,13 +299,14 @@ fun PlayerView(
}
}

when (val event = uiEvent) {
is PlayerEvent.TrySeekToLastResultEvent.Success -> LaunchedEffect(Unit) {
commandQueue.trySend(PlayerCommand.SeekTo((event.position / 1000).toInt().coerceAtLeast(0)))
}
MviEventListener(viewModel.singleEvent) { event ->
when (event) {
is PlayerEvent.TrySeekToLastResultEvent.Success -> commandQueue.trySend(
PlayerCommand.SeekTo((event.position / 1000).toInt().coerceAtLeast(0))
)

PlayerEvent.TrySeekToLastResultEvent.NoNeed,
null -> Unit
PlayerEvent.TrySeekToLastResultEvent.NoNeed -> Unit
}
}

val autoPip = LocalPlayerAutoPip.current
Expand Down
Loading

0 comments on commit 367b368

Please sign in to comment.