Skip to content

Commit

Permalink
fix: Fix copy popup menu not dismissing when clicking elsewhere (#172)
Browse files Browse the repository at this point in the history
While on that also add missing copyright headers in test files
---------
Signed-off-by: starry-shivam <[email protected]>
  • Loading branch information
starry-shivam authored May 11, 2024
1 parent edb0da2 commit 781f523
Show file tree
Hide file tree
Showing 10 changed files with 224 additions and 102 deletions.
2 changes: 1 addition & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand All @@ -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<String> {
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() }
)
}
}

Expand All @@ -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)
)
}
}
}
}

Expand All @@ -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<String> {
return text.splitToSequence("\n\n")
.filter { it.isNotBlank() }
.toList()
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
@@ -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)
}
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand Down
16 changes: 16 additions & 0 deletions app/src/test/java/com/starry/myne/BookTextMapperTest.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
16 changes: 16 additions & 0 deletions app/src/test/java/com/starry/myne/BookUtilsTest.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
16 changes: 16 additions & 0 deletions app/src/test/java/com/starry/myne/EpubParserTest.kt
Original file line number Diff line number Diff line change
@@ -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
Expand Down
Loading

0 comments on commit 781f523

Please sign in to comment.