Android reader support

This commit is contained in:
Syer10
2022-02-27 15:29:43 -05:00
parent 223281372f
commit 730ea4a90c
11 changed files with 159 additions and 40 deletions

View File

@@ -22,5 +22,10 @@
<category android:name="android.intent.category.LAUNCHER"/>
</intent-filter>
</activity>
<activity
android:name=".ReaderActivity"
android:launchMode="singleTask"
android:exported="false">
</activity>
</application>
</manifest>

View File

@@ -21,9 +21,9 @@ class App : Application(), DefaultLifecycleObserver {
override fun onCreate() {
super<Application>.onCreate()
/*if (BuildConfig.DEBUG) {
if (BuildConfig.DEBUG) {
System.setProperty("kotlinx.coroutines.debug", "on")
}*/
}
ProcessLifecycleOwner.get().lifecycle.addObserver(this)

View File

@@ -0,0 +1,75 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.jui.android
import android.content.Context
import android.content.Intent
import android.os.Bundle
import androidx.activity.compose.setContent
import androidx.appcompat.app.AppCompatActivity
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.ui.input.key.KeyEvent
import androidx.compose.ui.input.key.NativeKeyEvent
import androidx.compose.ui.input.key.key
import androidx.lifecycle.lifecycleScope
import ca.gosyer.ui.AppComponent
import ca.gosyer.ui.base.theme.AppTheme
import ca.gosyer.ui.reader.ReaderMenu
import ca.gosyer.ui.reader.supportedKeyList
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
class ReaderActivity : AppCompatActivity() {
companion object {
fun newIntent(context: Context, mangaId: Long, chapterIndex: Int): Intent {
return Intent(context, ReaderActivity::class.java).apply {
putExtra("manga", mangaId)
putExtra("chapter", chapterIndex)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP)
}
}
}
private val hotkeyFlow = MutableSharedFlow<KeyEvent>()
override fun onCreate(savedInstanceState: Bundle?) {
super.onCreate(savedInstanceState)
val hooks = AppComponent.getInstance(applicationContext).uiComponent.getHooks()
val mangaId = intent.extras!!.getLong("manga", -1)
val chapterIndex = intent.extras!!.getInt("chapter", -1)
if (mangaId == -1L || chapterIndex == -1) {
finish()
return
}
setContent {
CompositionLocalProvider(
*hooks
) {
AppTheme {
ReaderMenu(chapterIndex, mangaId, hotkeyFlow)
}
}
}
}
override fun onKeyUp(keyCode: Int, event: android.view.KeyEvent?): Boolean {
event ?: super.onKeyUp(keyCode, event)
val composeKeyEvent = KeyEvent(event as NativeKeyEvent)
lifecycleScope.launch {
hotkeyFlow.emit(composeKeyEvent)
}
return if (composeKeyEvent.key in supportedKeyList) {
true
} else {
super.onKeyUp(keyCode, event)
}
}
}

View File

@@ -13,5 +13,5 @@ object Config {
val desktopJvmTarget = JavaVersion.VERSION_16
val androidJvmTarget = JavaVersion.VERSION_11
const val androidDev = true
const val androidDev = false
}

View File

@@ -0,0 +1,31 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.ui.reader
import android.content.Context
import android.content.Intent
import androidx.compose.runtime.Composable
import androidx.compose.runtime.remember
import androidx.compose.ui.platform.LocalContext
actual class ReaderLauncher(private val context: Context) {
actual fun launch(
chapterIndex: Int,
mangaId: Long
) {
Intent(context, Class.forName("ca.gosyer.jui.android.ReaderActivity")).apply {
putExtra("manga", mangaId)
putExtra("chapter", chapterIndex)
addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP or Intent.FLAG_ACTIVITY_NEW_TASK)
}.let(context::startActivity)
}
}
@Composable
actual fun rememberReaderLauncher(): ReaderLauncher {
val context = LocalContext.current
return remember(context) { ReaderLauncher(context) }
}

View File

@@ -1,10 +0,0 @@
/*
* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at https://mozilla.org/MPL/2.0/.
*/
package ca.gosyer.ui.reader
actual fun openReaderMenu(chapterIndex: Int, mangaId: Long) {
}

View File

@@ -6,6 +6,7 @@
package ca.gosyer.ui.reader
import androidx.compose.runtime.Composable
import androidx.compose.runtime.CompositionLocalProvider
import androidx.compose.runtime.DisposableEffect
import androidx.compose.runtime.remember
@@ -28,8 +29,21 @@ import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.launch
actual class ReaderLauncher {
actual fun launch(
chapterIndex: Int,
mangaId: Long
) {
openReaderMenu(chapterIndex, mangaId)
}
}
@Composable
actual fun rememberReaderLauncher(): ReaderLauncher {
return remember { ReaderLauncher() }
}
@OptIn(DelicateCoroutinesApi::class)
actual fun openReaderMenu(chapterIndex: Int, mangaId: Long) {
fun openReaderMenu(chapterIndex: Int, mangaId: Long) {
val windowSettings = AppComponent.getInstance().dataComponent.uiPreferences
.readerWindow()
val (

View File

@@ -6,7 +6,6 @@
package ca.gosyer.ui.manga.components
import ca.gosyer.ui.base.components.VerticalScrollbar
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxWidth
@@ -15,7 +14,6 @@ import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.foundation.lazy.rememberLazyListState
import ca.gosyer.ui.base.components.rememberScrollbarAdapter
import androidx.compose.material.Scaffold
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.rounded.Favorite
@@ -32,9 +30,11 @@ import ca.gosyer.data.models.Category
import ca.gosyer.data.models.Manga
import ca.gosyer.i18n.MR
import ca.gosyer.ui.base.chapter.ChapterDownloadItem
import ca.gosyer.ui.base.components.VerticalScrollbar
import ca.gosyer.ui.base.components.rememberScrollbarAdapter
import ca.gosyer.ui.base.navigation.ActionItem
import ca.gosyer.ui.base.navigation.Toolbar
import ca.gosyer.ui.reader.openReaderMenu
import ca.gosyer.ui.reader.rememberReaderLauncher
import ca.gosyer.uicore.components.ErrorScreen
import ca.gosyer.uicore.components.LoadingScreen
import ca.gosyer.uicore.resources.stringResource
@@ -71,6 +71,7 @@ fun MangaScreenContent(
categoryDialogState.show()
}
}
val readerLauncher = rememberReaderLauncher()
Scaffold(
topBar = {
@@ -104,7 +105,7 @@ fun MangaScreenContent(
ChapterItem(
chapter,
dateTimeFormatter::format,
onClick = { openReaderMenu(it, manga.id) },
onClick = { readerLauncher.launch(it, manga.id) },
toggleRead = toggleRead,
toggleBookmarked = toggleBookmarked,
markPreviousAsRead = markPreviousRead,

View File

@@ -78,7 +78,14 @@ val supportedKeyList = listOf(
Key.DirectionRight
)
expect fun openReaderMenu(chapterIndex: Int, mangaId: Long)
expect class ReaderLauncher {
fun launch(
chapterIndex: Int,
mangaId: Long
)
}
@Composable
expect fun rememberReaderLauncher(): ReaderLauncher
@Composable
fun ReaderMenu(
@@ -106,13 +113,6 @@ fun ReaderMenu(
val currentPage by vm.currentPage.collectAsState()
val currentPageOffset by vm.currentPageOffset.collectAsState()
fun hotkey(block: () -> Unit): (KeyEvent) -> Boolean {
return {
block()
true
}
}
LaunchedEffect(hotkeyFlow) {
hotkeyFlow.collectLatest {
when (it.key) {

View File

@@ -6,6 +6,7 @@
package ca.gosyer.ui.reader
import ca.gosyer.core.lang.launchDefault
import ca.gosyer.core.lang.throwIfCancellation
import ca.gosyer.core.logging.CKLogger
import ca.gosyer.core.prefs.getAsFlow
@@ -27,7 +28,6 @@ import ca.gosyer.uicore.prefs.asStateIn
import ca.gosyer.uicore.vm.ContextWrapper
import ca.gosyer.uicore.vm.ViewModel
import kotlinx.coroutines.DelicateCoroutinesApi
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.GlobalScope
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.MutableStateFlow
@@ -96,11 +96,13 @@ class ReaderMenuViewModel @Inject constructor(
}
fun init() {
scope.launch(Dispatchers.Default) {
scope.launchDefault {
runCatching {
initManga(params.mangaId)
initChapters(params.mangaId, params.chapterIndex)
}
}
}
fun navigate(navigationRegion: Navigation) {
scope.launch {
@@ -147,7 +149,7 @@ class ReaderMenuViewModel @Inject constructor(
}
fun setMangaReaderMode(mode: String) {
scope.launch(Dispatchers.Default) {
scope.launchDefault {
_manga.value?.updateRemote(
mangaHandler,
mode
@@ -157,8 +159,8 @@ class ReaderMenuViewModel @Inject constructor(
}
fun prevChapter() {
scope.launch(Dispatchers.Default) {
val prevChapter = previousChapter.value ?: return@launch
scope.launchDefault {
val prevChapter = previousChapter.value ?: return@launchDefault
try {
_state.value = ReaderChapter.State.Wait
sendProgress()
@@ -170,8 +172,8 @@ class ReaderMenuViewModel @Inject constructor(
}
fun nextChapter() {
scope.launch(Dispatchers.Default) {
val nextChapter = nextChapter.value ?: return@launch
scope.launchDefault {
val nextChapter = nextChapter.value ?: return@launchDefault
try {
_state.value = ReaderChapter.State.Wait
sendProgress()
@@ -205,7 +207,7 @@ class ReaderMenuViewModel @Inject constructor(
)
val pages = loader.loadChapter(chapter)
viewerChapters.currChapter.value = chapter
scope.launch(Dispatchers.Default) {
scope.launchDefault {
val chapters = try {
chapterHandler.getChapters(mangaId)
} catch (e: Exception) {
@@ -265,7 +267,7 @@ class ReaderMenuViewModel @Inject constructor(
fun sendProgress(chapter: Chapter? = this.chapter.value?.chapter, lastPageRead: Int = currentPage.value) {
chapter ?: return
if (chapter.read) return
GlobalScope.launch {
GlobalScope.launchDefault {
chapterHandler.updateChapter(chapter.mangaId, chapter.index, lastPageRead = lastPageRead)
}
}
@@ -276,7 +278,7 @@ class ReaderMenuViewModel @Inject constructor(
@OptIn(DelicateCoroutinesApi::class)
private fun updateLastPageReadOffset(chapter: Chapter, offset: Int) {
GlobalScope.launch {
GlobalScope.launchDefault {
chapter.updateRemote(chapterHandler, offset)
}
}

View File

@@ -9,7 +9,7 @@ package ca.gosyer.ui.updates
import androidx.compose.runtime.Composable
import androidx.compose.runtime.collectAsState
import ca.gosyer.ui.manga.MangaScreen
import ca.gosyer.ui.reader.openReaderMenu
import ca.gosyer.ui.reader.rememberReaderLauncher
import ca.gosyer.ui.updates.components.UpdatesScreenContent
import ca.gosyer.uicore.vm.viewModel
import cafe.adriel.voyager.core.screen.Screen
@@ -26,11 +26,12 @@ class UpdatesScreen : Screen {
override fun Content() {
val vm = viewModel<UpdatesScreenViewModel>()
val navigator = LocalNavigator.currentOrThrow
val readerLauncher = rememberReaderLauncher()
UpdatesScreenContent(
isLoading = vm.isLoading.collectAsState().value,
updates = vm.updates.collectAsState().value,
loadNextPage = vm::loadNextPage,
openChapter = ::openReaderMenu,
openChapter = readerLauncher::launch,
openManga = { navigator push MangaScreen(it) },
downloadChapter = vm::downloadChapter,
deleteDownloadedChapter = vm::deleteDownloadedChapter,