From 781f52317071867af83ccb40e9ddc64c5dc8dd52 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?St=C9=91rry=20Shiv=C9=91m?= Date: Sat, 11 May 2024 15:18:04 +0530 Subject: [PATCH] fix: Fix copy popup menu not dismissing when clicking elsewhere (#172) While on that also add missing copyright headers in test files --------- Signed-off-by: starry-shivam --- app/build.gradle | 2 +- .../reader/composables/ReaderContent.kt | 147 ++++++++++-------- .../reader/composables/ReaderScreen.kt | 2 +- .../ui/screens/reader/others/ReaderFont.kt | 56 +++++++ .../reader/viewmodels/ReaderViewModel.kt | 39 +---- .../com/starry/myne/BookTextMapperTest.kt | 16 ++ .../java/com/starry/myne/BookUtilsTest.kt | 16 ++ .../java/com/starry/myne/EpubParserTest.kt | 16 ++ .../com/starry/myne/EpubXMLFileParserTest.kt | 16 ++ .../java/com/starry/myne/PaginatorTest.kt | 16 ++ 10 files changed, 224 insertions(+), 102 deletions(-) create mode 100644 app/src/main/java/com/starry/myne/ui/screens/reader/others/ReaderFont.kt diff --git a/app/build.gradle b/app/build.gradle index 83dc4bd3..103104f5 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -28,7 +28,7 @@ android { minSdk 26 targetSdk 34 versionCode 370 - versionName "3.7.0" + versionName "3.8.0-dev" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" vectorDrawables { diff --git a/app/src/main/java/com/starry/myne/ui/screens/reader/composables/ReaderContent.kt b/app/src/main/java/com/starry/myne/ui/screens/reader/composables/ReaderContent.kt index a49f7513..0f2a136d 100644 --- a/app/src/main/java/com/starry/myne/ui/screens/reader/composables/ReaderContent.kt +++ b/app/src/main/java/com/starry/myne/ui/screens/reader/composables/ReaderContent.kt @@ -18,6 +18,9 @@ package com.starry.myne.ui.screens.reader.composables import androidx.compose.animation.core.animateFloatAsState import androidx.compose.animation.core.tween +import androidx.compose.foundation.gestures.awaitEachGesture +import androidx.compose.foundation.gestures.awaitFirstDown +import androidx.compose.foundation.gestures.waitForUpOrCancellation import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.fillMaxSize @@ -34,6 +37,8 @@ import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue import androidx.compose.runtime.remember import androidx.compose.ui.Modifier +import androidx.compose.ui.input.pointer.PointerInputChange +import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.text.font.FontWeight @@ -44,39 +49,30 @@ import coil.compose.AsyncImage import coil.request.ImageRequest import com.starry.myne.epub.BookTextMapper import com.starry.myne.epub.models.EpubChapter -import com.starry.myne.helpers.noRippleClickable import com.starry.myne.ui.screens.reader.viewmodels.ReaderScreenState import com.starry.myne.ui.screens.reader.viewmodels.ReaderViewModel import com.starry.myne.ui.theme.pacificoFont -private fun chunkText(text: String): List { - return text.splitToSequence("\n\n") - .filter { it.isNotBlank() } - .toList() -} - @Composable fun ReaderContent( viewModel: ReaderViewModel, lazyListState: LazyListState, ) { - SelectionContainer { - LazyColumn( - modifier = Modifier.fillMaxSize(), - state = lazyListState - ) { - items( - count = viewModel.state.epubBook!!.chapters.size, - key = { index -> viewModel.state.epubBook!!.chapters[index].hashCode() } - ) { index -> - val chapter = viewModel.state.epubBook!!.chapters[index] - ChapterLazyItemItem( - chapter = chapter, - state = viewModel.state, - onClick = { viewModel.toggleReaderMenu() } - ) - } + LazyColumn( + modifier = Modifier.fillMaxSize(), + state = lazyListState + ) { + items( + count = viewModel.state.epubBook!!.chapters.size, + key = { index -> viewModel.state.epubBook!!.chapters[index].hashCode() } + ) { index -> + val chapter = viewModel.state.epubBook!!.chapters[index] + ChapterLazyItemItem( + chapter = chapter, + state = viewModel.state, + onClick = { viewModel.toggleReaderMenu() } + ) } } @@ -103,50 +99,70 @@ private fun ChapterLazyItemItem( label = "titleFontSize" ) - Column( - modifier = Modifier - .fillMaxWidth() - .noRippleClickable { onClick() } - ) { - Text( - modifier = Modifier.padding(start = 12.dp, end = 4.dp, top = 10.dp), - text = chapter.title, - fontSize = titleFontSize.sp, - lineHeight = 32.sp, - fontFamily = pacificoFont, - fontWeight = FontWeight.Medium, - color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.88f) - ) - Spacer(modifier = Modifier.height(12.dp)) + SelectionContainer { + Column( + modifier = Modifier + .fillMaxWidth() + .pointerInput(Unit) { + // We're using awaitEachGesture instead of Modifier.clickable or + // Modifier.detectTapGestures so that we can propagate the click + // event to the parent, which in this case is SelectionContainer. + // Without this, the click event would be consumed before reaching + // SelectionContainer, preventing the copy/paste popup from being + // dismissed until the user copies the selected text. + awaitEachGesture { + val down: PointerInputChange = awaitFirstDown() + val up: PointerInputChange? = waitForUpOrCancellation() + // only trigger the click if the pointer hasn't moved up or down + // i.e only on tap gesture + if (up != null && down.id == up.id) { + onClick() + } + } - paragraphs.forEach { para -> - val imgEntry = BookTextMapper.ImgEntry.fromXMLString(para) - when { - imgEntry == null -> { - Text( - text = para, - fontSize = fontSize.sp, - lineHeight = 1.3.em, - fontFamily = state.fontFamily.fontFamily, - modifier = Modifier.padding(start = 14.dp, end = 14.dp, bottom = 8.dp), - ) } + // .noRippleClickable { onClick() } + ) { + Text( + modifier = Modifier.padding(start = 12.dp, end = 4.dp, top = 10.dp), + text = chapter.title, + fontSize = titleFontSize.sp, + lineHeight = 32.sp, + fontFamily = pacificoFont, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.88f) + ) + Spacer(modifier = Modifier.height(12.dp)) - else -> { - val image = epubBook?.images?.find { it.absPath == imgEntry.path } - image?.let { - AsyncImage( - model = ImageRequest.Builder(LocalContext.current) - .data(image.image) - .crossfade(true) - .build(), - contentDescription = null, - contentScale = ContentScale.Crop, - modifier = Modifier - .fillMaxSize() - .padding(vertical = 6.dp) + paragraphs.forEach { para -> + val imgEntry = BookTextMapper.ImgEntry.fromXMLString(para) + when { + imgEntry == null -> { + Text( + text = para, + fontSize = fontSize.sp, + lineHeight = 1.3.em, + fontFamily = state.fontFamily.fontFamily, + modifier = Modifier.padding(start = 14.dp, end = 14.dp, bottom = 8.dp), ) } + + else -> { + val image = epubBook?.images?.find { it.absPath == imgEntry.path } + image?.let { + AsyncImage( + model = ImageRequest.Builder(LocalContext.current) + .data(image.image) + .crossfade(true) + .build(), + contentDescription = null, + contentScale = ContentScale.Crop, + modifier = Modifier + .fillMaxSize() + .padding(vertical = 6.dp) + ) + } + } } } @@ -157,4 +173,11 @@ private fun ChapterLazyItemItem( color = MaterialTheme.colorScheme.onBackground.copy(alpha = 0.2f) ) } +} + +// Helper function to chunk text into paragraphs +private fun chunkText(text: String): List { + return text.splitToSequence("\n\n") + .filter { it.isNotBlank() } + .toList() } \ No newline at end of file diff --git a/app/src/main/java/com/starry/myne/ui/screens/reader/composables/ReaderScreen.kt b/app/src/main/java/com/starry/myne/ui/screens/reader/composables/ReaderScreen.kt index 36340929..14aba7fc 100644 --- a/app/src/main/java/com/starry/myne/ui/screens/reader/composables/ReaderScreen.kt +++ b/app/src/main/java/com/starry/myne/ui/screens/reader/composables/ReaderScreen.kt @@ -94,7 +94,7 @@ import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.starry.myne.R -import com.starry.myne.ui.screens.reader.viewmodels.ReaderFont +import com.starry.myne.ui.screens.reader.others.ReaderFont import com.starry.myne.ui.screens.reader.viewmodels.ReaderViewModel import com.starry.myne.ui.screens.settings.viewmodels.SettingsViewModel import com.starry.myne.ui.screens.settings.viewmodels.ThemeMode diff --git a/app/src/main/java/com/starry/myne/ui/screens/reader/others/ReaderFont.kt b/app/src/main/java/com/starry/myne/ui/screens/reader/others/ReaderFont.kt new file mode 100644 index 00000000..9a43b7a7 --- /dev/null +++ b/app/src/main/java/com/starry/myne/ui/screens/reader/others/ReaderFont.kt @@ -0,0 +1,56 @@ +/** + * Copyright (c) [2022 - Present] Stɑrry Shivɑm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package com.starry.myne.ui.screens.reader.others + +import androidx.annotation.Keep +import androidx.compose.ui.text.font.Font +import androidx.compose.ui.text.font.FontFamily +import com.starry.myne.R +import com.starry.myne.ui.theme.figeronaFont + +@Keep +sealed class ReaderFont(val id: String, val name: String, val fontFamily: FontFamily) { + + companion object { + fun getAllFonts() = + ReaderFont::class.sealedSubclasses.mapNotNull { it.objectInstance }.sortedBy { it.name } + + fun getFontByName(name: String) = getAllFonts().find { it.name == name }!! + } + + @Keep + data object System : ReaderFont("system", "System Default", FontFamily.Default) + + @Keep + data object Serif : ReaderFont("serif", "Serif", FontFamily.Serif) + + @Keep + data object Cursive : ReaderFont("cursive", "Cursive", FontFamily.Cursive) + + @Keep + data object SansSerif : ReaderFont("sans-serif", "SansSerif", FontFamily.SansSerif) + + @Keep + data object Inter : ReaderFont("inter", "Inter", FontFamily(Font(R.font.reader_inter_font))) + + @Keep + data object Dyslexic : + ReaderFont("dyslexic", "OpenDyslexic", FontFamily(Font(R.font.reader_inter_font))) + + @Keep + data object Lora : ReaderFont("figerona", "Figerona", figeronaFont) +} \ No newline at end of file diff --git a/app/src/main/java/com/starry/myne/ui/screens/reader/viewmodels/ReaderViewModel.kt b/app/src/main/java/com/starry/myne/ui/screens/reader/viewmodels/ReaderViewModel.kt index 3307ee06..50fbeeba 100644 --- a/app/src/main/java/com/starry/myne/ui/screens/reader/viewmodels/ReaderViewModel.kt +++ b/app/src/main/java/com/starry/myne/ui/screens/reader/viewmodels/ReaderViewModel.kt @@ -17,25 +17,21 @@ package com.starry.myne.ui.screens.reader.viewmodels -import androidx.annotation.Keep import androidx.compose.runtime.State import androidx.compose.runtime.getValue import androidx.compose.runtime.mutableFloatStateOf import androidx.compose.runtime.mutableIntStateOf import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.setValue -import androidx.compose.ui.text.font.Font -import androidx.compose.ui.text.font.FontFamily import androidx.lifecycle.ViewModel import androidx.lifecycle.viewModelScope -import com.starry.myne.R import com.starry.myne.database.library.LibraryDao import com.starry.myne.database.reader.ReaderDao import com.starry.myne.database.reader.ReaderData import com.starry.myne.epub.EpubParser import com.starry.myne.epub.models.EpubBook import com.starry.myne.helpers.PreferenceUtil -import com.starry.myne.ui.theme.figeronaFont +import com.starry.myne.ui.screens.reader.others.ReaderFont import dagger.hilt.android.lifecycle.HiltViewModel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.delay @@ -44,39 +40,6 @@ import java.io.FileInputStream import javax.inject.Inject -@Keep -sealed class ReaderFont(val id: String, val name: String, val fontFamily: FontFamily) { - - companion object { - fun getAllFonts() = - ReaderFont::class.sealedSubclasses.mapNotNull { it.objectInstance }.sortedBy { it.name } - - fun getFontByName(name: String) = getAllFonts().find { it.name == name }!! - } - - @Keep - data object System : ReaderFont("system", "System Default", FontFamily.Default) - - @Keep - data object Serif : ReaderFont("serif", "Serif", FontFamily.Serif) - - @Keep - data object Cursive : ReaderFont("cursive", "Cursive", FontFamily.Cursive) - - @Keep - data object SansSerif : ReaderFont("sans-serif", "SansSerif", FontFamily.SansSerif) - - @Keep - data object Inter : ReaderFont("inter", "Inter", FontFamily(Font(R.font.reader_inter_font))) - - @Keep - data object Dyslexic : - ReaderFont("dyslexic", "OpenDyslexic", FontFamily(Font(R.font.reader_inter_font))) - - @Keep - data object Lora : ReaderFont("figerona", "Figerona", figeronaFont) -} - data class ReaderScreenState( val isLoading: Boolean = true, val showReaderMenu: Boolean = false, diff --git a/app/src/test/java/com/starry/myne/BookTextMapperTest.kt b/app/src/test/java/com/starry/myne/BookTextMapperTest.kt index 05bd122c..c09e47f7 100644 --- a/app/src/test/java/com/starry/myne/BookTextMapperTest.kt +++ b/app/src/test/java/com/starry/myne/BookTextMapperTest.kt @@ -1,3 +1,19 @@ +/** + * Copyright (c) [2022 - Present] Stɑrry Shivɑm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.starry.myne import com.google.common.truth.Truth.assertThat diff --git a/app/src/test/java/com/starry/myne/BookUtilsTest.kt b/app/src/test/java/com/starry/myne/BookUtilsTest.kt index 5b0a431a..9efc61e7 100644 --- a/app/src/test/java/com/starry/myne/BookUtilsTest.kt +++ b/app/src/test/java/com/starry/myne/BookUtilsTest.kt @@ -1,3 +1,19 @@ +/** + * Copyright (c) [2022 - Present] Stɑrry Shivɑm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.starry.myne import com.google.common.truth.Truth.assertThat diff --git a/app/src/test/java/com/starry/myne/EpubParserTest.kt b/app/src/test/java/com/starry/myne/EpubParserTest.kt index b009eddc..95ca8b88 100644 --- a/app/src/test/java/com/starry/myne/EpubParserTest.kt +++ b/app/src/test/java/com/starry/myne/EpubParserTest.kt @@ -1,3 +1,19 @@ +/** + * Copyright (c) [2022 - Present] Stɑrry Shivɑm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.starry.myne import android.graphics.Bitmap diff --git a/app/src/test/java/com/starry/myne/EpubXMLFileParserTest.kt b/app/src/test/java/com/starry/myne/EpubXMLFileParserTest.kt index b98054a1..cc5a5402 100644 --- a/app/src/test/java/com/starry/myne/EpubXMLFileParserTest.kt +++ b/app/src/test/java/com/starry/myne/EpubXMLFileParserTest.kt @@ -1,3 +1,19 @@ +/** + * Copyright (c) [2022 - Present] Stɑrry Shivɑm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.starry.myne import com.google.common.truth.Truth.assertThat diff --git a/app/src/test/java/com/starry/myne/PaginatorTest.kt b/app/src/test/java/com/starry/myne/PaginatorTest.kt index 8ec8d354..56ac44e3 100644 --- a/app/src/test/java/com/starry/myne/PaginatorTest.kt +++ b/app/src/test/java/com/starry/myne/PaginatorTest.kt @@ -1,3 +1,19 @@ +/** + * Copyright (c) [2022 - Present] Stɑrry Shivɑm + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + package com.starry.myne