From 730ea4a90c844b44694890a2346dd4fbbd931091 Mon Sep 17 00:00:00 2001 From: Syer10 Date: Sun, 27 Feb 2022 15:29:43 -0500 Subject: [PATCH] Android reader support --- android/src/main/AndroidManifest.xml | 5 ++ .../main/java/ca/gosyer/jui/android/App.kt | 4 +- .../ca/gosyer/jui/android/ReaderActivity.kt | 75 +++++++++++++++++++ buildSrc/src/main/kotlin/Config.kt | 2 +- .../ca/gosyer/ui/reader/AndroidReaderMenu.kt | 31 ++++++++ .../ca/gosyer/ui/reader/openReaderMenu.kt | 10 --- .../ca/gosyer/ui/reader/DesktopReaderMenu.kt | 16 +++- .../ui/manga/components/MangaScreenContent.kt | 9 ++- .../kotlin/ca/gosyer/ui/reader/ReaderMenu.kt | 16 ++-- .../gosyer/ui/reader/ReaderMenuViewModel.kt | 26 ++++--- .../ca/gosyer/ui/updates/UpdatesScreen.kt | 5 +- 11 files changed, 159 insertions(+), 40 deletions(-) create mode 100644 android/src/main/java/ca/gosyer/jui/android/ReaderActivity.kt create mode 100644 presentation/src/androidMain/kotlin/ca/gosyer/ui/reader/AndroidReaderMenu.kt delete mode 100644 presentation/src/androidMain/kotlin/ca/gosyer/ui/reader/openReaderMenu.kt diff --git a/android/src/main/AndroidManifest.xml b/android/src/main/AndroidManifest.xml index 9c48a7e8..cb7f95aa 100644 --- a/android/src/main/AndroidManifest.xml +++ b/android/src/main/AndroidManifest.xml @@ -22,5 +22,10 @@ + + \ No newline at end of file diff --git a/android/src/main/java/ca/gosyer/jui/android/App.kt b/android/src/main/java/ca/gosyer/jui/android/App.kt index 9fe213b8..4bc5f2e1 100644 --- a/android/src/main/java/ca/gosyer/jui/android/App.kt +++ b/android/src/main/java/ca/gosyer/jui/android/App.kt @@ -21,9 +21,9 @@ class App : Application(), DefaultLifecycleObserver { override fun onCreate() { super.onCreate() - /*if (BuildConfig.DEBUG) { + if (BuildConfig.DEBUG) { System.setProperty("kotlinx.coroutines.debug", "on") - }*/ + } ProcessLifecycleOwner.get().lifecycle.addObserver(this) diff --git a/android/src/main/java/ca/gosyer/jui/android/ReaderActivity.kt b/android/src/main/java/ca/gosyer/jui/android/ReaderActivity.kt new file mode 100644 index 00000000..a1a54039 --- /dev/null +++ b/android/src/main/java/ca/gosyer/jui/android/ReaderActivity.kt @@ -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() + + 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) + } + } +} \ No newline at end of file diff --git a/buildSrc/src/main/kotlin/Config.kt b/buildSrc/src/main/kotlin/Config.kt index 6d5e4907..f2637049 100644 --- a/buildSrc/src/main/kotlin/Config.kt +++ b/buildSrc/src/main/kotlin/Config.kt @@ -13,5 +13,5 @@ object Config { val desktopJvmTarget = JavaVersion.VERSION_16 val androidJvmTarget = JavaVersion.VERSION_11 - const val androidDev = true + const val androidDev = false } \ No newline at end of file diff --git a/presentation/src/androidMain/kotlin/ca/gosyer/ui/reader/AndroidReaderMenu.kt b/presentation/src/androidMain/kotlin/ca/gosyer/ui/reader/AndroidReaderMenu.kt new file mode 100644 index 00000000..e508844c --- /dev/null +++ b/presentation/src/androidMain/kotlin/ca/gosyer/ui/reader/AndroidReaderMenu.kt @@ -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) } +} \ No newline at end of file diff --git a/presentation/src/androidMain/kotlin/ca/gosyer/ui/reader/openReaderMenu.kt b/presentation/src/androidMain/kotlin/ca/gosyer/ui/reader/openReaderMenu.kt deleted file mode 100644 index 81e36aac..00000000 --- a/presentation/src/androidMain/kotlin/ca/gosyer/ui/reader/openReaderMenu.kt +++ /dev/null @@ -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) { -} \ No newline at end of file diff --git a/presentation/src/desktopMain/kotlin/ca/gosyer/ui/reader/DesktopReaderMenu.kt b/presentation/src/desktopMain/kotlin/ca/gosyer/ui/reader/DesktopReaderMenu.kt index 4c2f3404..da247e52 100644 --- a/presentation/src/desktopMain/kotlin/ca/gosyer/ui/reader/DesktopReaderMenu.kt +++ b/presentation/src/desktopMain/kotlin/ca/gosyer/ui/reader/DesktopReaderMenu.kt @@ -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 ( diff --git a/presentation/src/jvmMain/kotlin/ca/gosyer/ui/manga/components/MangaScreenContent.kt b/presentation/src/jvmMain/kotlin/ca/gosyer/ui/manga/components/MangaScreenContent.kt index 9ab6b346..93b7641c 100644 --- a/presentation/src/jvmMain/kotlin/ca/gosyer/ui/manga/components/MangaScreenContent.kt +++ b/presentation/src/jvmMain/kotlin/ca/gosyer/ui/manga/components/MangaScreenContent.kt @@ -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, diff --git a/presentation/src/jvmMain/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt b/presentation/src/jvmMain/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt index 6c09e165..964003ca 100644 --- a/presentation/src/jvmMain/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt +++ b/presentation/src/jvmMain/kotlin/ca/gosyer/ui/reader/ReaderMenu.kt @@ -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) { diff --git a/presentation/src/jvmMain/kotlin/ca/gosyer/ui/reader/ReaderMenuViewModel.kt b/presentation/src/jvmMain/kotlin/ca/gosyer/ui/reader/ReaderMenuViewModel.kt index adedf844..3e004b86 100644 --- a/presentation/src/jvmMain/kotlin/ca/gosyer/ui/reader/ReaderMenuViewModel.kt +++ b/presentation/src/jvmMain/kotlin/ca/gosyer/ui/reader/ReaderMenuViewModel.kt @@ -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,9 +96,11 @@ class ReaderMenuViewModel @Inject constructor( } fun init() { - scope.launch(Dispatchers.Default) { - initManga(params.mangaId) - initChapters(params.mangaId, params.chapterIndex) + scope.launchDefault { + runCatching { + initManga(params.mangaId) + initChapters(params.mangaId, params.chapterIndex) + } } } @@ -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) } } diff --git a/presentation/src/jvmMain/kotlin/ca/gosyer/ui/updates/UpdatesScreen.kt b/presentation/src/jvmMain/kotlin/ca/gosyer/ui/updates/UpdatesScreen.kt index 5832ef9d..2c5c4ba2 100644 --- a/presentation/src/jvmMain/kotlin/ca/gosyer/ui/updates/UpdatesScreen.kt +++ b/presentation/src/jvmMain/kotlin/ca/gosyer/ui/updates/UpdatesScreen.kt @@ -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() 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,