Browse Source

Migrate reader shortcut menus to Compose

Contents' UIs should probably be improved, but that can happen separately.
arkon 1 năm trước cách đây
mục cha
commit
7308090288

+ 56 - 0
app/src/main/java/eu/kanade/presentation/reader/OrientationModeSelectDialog.kt

@@ -0,0 +1,56 @@
+package eu.kanade.presentation.reader
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.FilterChip
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import eu.kanade.domain.manga.model.orientationType
+import eu.kanade.presentation.components.AdaptiveSheet
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
+import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
+import tachiyomi.presentation.core.components.SettingsChipRow
+import tachiyomi.presentation.core.components.material.padding
+
+private val orientationTypeOptions = OrientationType.entries.map { it.stringRes to it }
+
+@Composable
+fun OrientationModeSelectDialog(
+    onDismissRequest: () -> Unit,
+    screenModel: ReaderSettingsScreenModel,
+    onChange: (Int) -> Unit,
+) {
+    val manga by screenModel.mangaFlow.collectAsState()
+    val orientationType = remember(manga) { OrientationType.fromPreference(manga?.orientationType?.toInt()) }
+
+    AdaptiveSheet(
+        onDismissRequest = onDismissRequest,
+    ) {
+        Row(
+            modifier = Modifier.padding(vertical = 16.dp),
+            horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
+        ) {
+            SettingsChipRow(R.string.rotation_type) {
+                orientationTypeOptions.map { (stringRes, it) ->
+                    FilterChip(
+                        selected = it == orientationType,
+                        onClick = {
+                            screenModel.onChangeOrientation(it)
+                            onChange(stringRes)
+                        },
+                        label = { Text(stringResource(stringRes)) },
+                    )
+                }
+            }
+        }
+    }
+}

+ 1 - 1
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderPageActionsDialog.kt → app/src/main/java/eu/kanade/presentation/reader/ReaderPageActionsDialog.kt

@@ -1,4 +1,4 @@
-package eu.kanade.tachiyomi.ui.reader
+package eu.kanade.presentation.reader
 
 import androidx.compose.foundation.layout.Arrangement
 import androidx.compose.foundation.layout.Row

+ 56 - 0
app/src/main/java/eu/kanade/presentation/reader/ReadingModeSelectDialog.kt

@@ -0,0 +1,56 @@
+package eu.kanade.presentation.reader
+
+import androidx.compose.foundation.layout.Arrangement
+import androidx.compose.foundation.layout.Row
+import androidx.compose.foundation.layout.padding
+import androidx.compose.material3.FilterChip
+import androidx.compose.material3.MaterialTheme
+import androidx.compose.material3.Text
+import androidx.compose.runtime.Composable
+import androidx.compose.runtime.collectAsState
+import androidx.compose.runtime.getValue
+import androidx.compose.runtime.remember
+import androidx.compose.ui.Modifier
+import androidx.compose.ui.res.stringResource
+import androidx.compose.ui.unit.dp
+import eu.kanade.domain.manga.model.readingModeType
+import eu.kanade.presentation.components.AdaptiveSheet
+import eu.kanade.tachiyomi.R
+import eu.kanade.tachiyomi.ui.reader.setting.ReaderSettingsScreenModel
+import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
+import tachiyomi.presentation.core.components.SettingsChipRow
+import tachiyomi.presentation.core.components.material.padding
+
+private val readingModeOptions = ReadingModeType.entries.map { it.stringRes to it }
+
+@Composable
+fun ReadingModeSelectDialog(
+    onDismissRequest: () -> Unit,
+    screenModel: ReaderSettingsScreenModel,
+    onChange: (Int) -> Unit,
+) {
+    val manga by screenModel.mangaFlow.collectAsState()
+    val readingMode = remember(manga) { ReadingModeType.fromPreference(manga?.readingModeType?.toInt()) }
+
+    AdaptiveSheet(
+        onDismissRequest = onDismissRequest,
+    ) {
+        Row(
+            modifier = Modifier.padding(vertical = 16.dp),
+            horizontalArrangement = Arrangement.spacedBy(MaterialTheme.padding.small),
+        ) {
+            SettingsChipRow(R.string.pref_category_reading_mode) {
+                readingModeOptions.map { (stringRes, it) ->
+                    FilterChip(
+                        selected = it == readingMode,
+                        onClick = {
+                            screenModel.onChangeReadingMode(it)
+                            onChange(stringRes)
+                        },
+                        label = { Text(stringResource(stringRes)) },
+                    )
+                }
+            }
+        }
+    }
+}

+ 6 - 5
app/src/main/java/eu/kanade/presentation/reader/settings/GeneralSettingsPage.kt

@@ -42,11 +42,12 @@ internal fun ColumnScope.GeneralPage(screenModel: ReaderSettingsScreenModel) {
         pref = screenModel.preferences.fullscreen(),
     )
 
-    // TODO: hide if there's no cutout
-    CheckboxItem(
-        label = stringResource(R.string.pref_cutout_short),
-        pref = screenModel.preferences.cutoutShort(),
-    )
+    if (screenModel.hasDisplayCutout) {
+        CheckboxItem(
+            label = stringResource(R.string.pref_cutout_short),
+            pref = screenModel.preferences.cutoutShort(),
+        )
+    }
 
     CheckboxItem(
         label = stringResource(R.string.pref_keep_screen_on),

+ 31 - 30
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt

@@ -49,9 +49,11 @@ import com.google.android.material.shape.MaterialShapeDrawable
 import com.google.android.material.transition.platform.MaterialContainerTransform
 import dev.chrisbanes.insetter.applyInsetter
 import eu.kanade.domain.base.BasePreferences
-import eu.kanade.domain.manga.model.orientationType
 import eu.kanade.presentation.reader.ChapterNavigator
+import eu.kanade.presentation.reader.OrientationModeSelectDialog
 import eu.kanade.presentation.reader.PageIndicatorText
+import eu.kanade.presentation.reader.ReaderPageActionsDialog
+import eu.kanade.presentation.reader.ReadingModeSelectDialog
 import eu.kanade.presentation.reader.settings.ReaderSettingsDialog
 import eu.kanade.tachiyomi.R
 import eu.kanade.tachiyomi.data.notification.NotificationReceiver
@@ -79,7 +81,6 @@ import eu.kanade.tachiyomi.util.system.isNightMode
 import eu.kanade.tachiyomi.util.system.toShareIntent
 import eu.kanade.tachiyomi.util.system.toast
 import eu.kanade.tachiyomi.util.view.copy
-import eu.kanade.tachiyomi.util.view.popupMenu
 import eu.kanade.tachiyomi.util.view.setComposeContent
 import eu.kanade.tachiyomi.util.view.setTooltip
 import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener
@@ -124,7 +125,7 @@ class ReaderActivity : BaseActivity() {
     val viewModel by viewModels<ReaderViewModel>()
     private var assistUrl: String? = null
 
-    val hasCutout by lazy { hasDisplayCutout() }
+    private val hasCutout by lazy { hasDisplayCutout() }
 
     /**
      * Configuration at reader level, like background color or forced orientation.
@@ -393,6 +394,7 @@ class ReaderActivity : BaseActivity() {
             val settingsScreenModel = remember {
                 ReaderSettingsScreenModel(
                     readerState = viewModel.state,
+                    hasDisplayCutout = hasCutout,
                     onChangeReadingMode = viewModel::setMangaReadingMode,
                     onChangeOrientation = viewModel::setMangaOrientationType,
                 )
@@ -423,6 +425,30 @@ class ReaderActivity : BaseActivity() {
                         screenModel = settingsScreenModel,
                     )
                 }
+                is ReaderViewModel.Dialog.ReadingModeSelect -> {
+                    ReadingModeSelectDialog(
+                        onDismissRequest = onDismissRequest,
+                        screenModel = settingsScreenModel,
+                        onChange = { stringRes ->
+                            menuToggleToast?.cancel()
+                            if (!readerPreferences.showReadingMode().get()) {
+                                menuToggleToast = toast(stringRes)
+                            }
+
+                            updateCropBordersShortcut()
+                        },
+                    )
+                }
+                is ReaderViewModel.Dialog.OrientationModeSelect -> {
+                    OrientationModeSelectDialog(
+                        onDismissRequest = onDismissRequest,
+                        screenModel = settingsScreenModel,
+                        onChange = { stringRes ->
+                            menuToggleToast?.cancel()
+                            menuToggleToast = toast(stringRes)
+                        },
+                    )
+                }
                 is ReaderViewModel.Dialog.PageActions -> {
                     ReaderPageActionsDialog(
                         onDismissRequest = onDismissRequest,
@@ -484,21 +510,7 @@ class ReaderActivity : BaseActivity() {
             setTooltip(R.string.viewer)
 
             setOnClickListener {
-                popupMenu(
-                    items = ReadingModeType.entries.map { it.flagValue to it.stringRes },
-                    selectedItemId = viewModel.getMangaReadingMode(resolveDefault = false),
-                ) {
-                    val newReadingMode = ReadingModeType.fromPreference(itemId)
-
-                    viewModel.setMangaReadingMode(newReadingMode)
-
-                    menuToggleToast?.cancel()
-                    if (!readerPreferences.showReadingMode().get()) {
-                        menuToggleToast = toast(newReadingMode.stringRes)
-                    }
-
-                    updateCropBordersShortcut()
-                }
+                viewModel.openReadingModeSelectDialog()
             }
         }
 
@@ -537,18 +549,7 @@ class ReaderActivity : BaseActivity() {
             setTooltip(R.string.rotation_type)
 
             setOnClickListener {
-                popupMenu(
-                    items = OrientationType.entries.map { it.flagValue to it.stringRes },
-                    selectedItemId = viewModel.manga?.orientationType?.toInt()
-                        ?: readerPreferences.defaultOrientationType().get(),
-                ) {
-                    val newOrientation = OrientationType.fromPreference(itemId)
-
-                    viewModel.setMangaOrientationType(newOrientation)
-
-                    menuToggleToast?.cancel()
-                    menuToggleToast = toast(newOrientation.stringRes)
-                }
+                viewModel.openOrientationModeSelectDialog()
             }
         }
 

+ 10 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderViewModel.kt

@@ -683,6 +683,14 @@ class ReaderViewModel(
         mutableState.update { it.copy(dialog = Dialog.Loading) }
     }
 
+    fun openReadingModeSelectDialog() {
+        mutableState.update { it.copy(dialog = Dialog.ReadingModeSelect) }
+    }
+
+    fun openOrientationModeSelectDialog() {
+        mutableState.update { it.copy(dialog = Dialog.OrientationModeSelect) }
+    }
+
     fun openPageDialog(page: ReaderPage) {
         mutableState.update { it.copy(dialog = Dialog.PageActions(page)) }
     }
@@ -863,6 +871,8 @@ class ReaderViewModel(
     sealed interface Dialog {
         data object Loading : Dialog
         data object Settings : Dialog
+        data object ReadingModeSelect : Dialog
+        data object OrientationModeSelect : Dialog
         data class PageActions(val page: ReaderPage) : Dialog
     }
 

+ 1 - 0
app/src/main/java/eu/kanade/tachiyomi/ui/reader/setting/ReaderSettingsScreenModel.kt

@@ -13,6 +13,7 @@ import uy.kohesive.injekt.api.get
 
 class ReaderSettingsScreenModel(
     readerState: StateFlow<ReaderViewModel.State>,
+    val hasDisplayCutout: Boolean,
     val onChangeReadingMode: (ReadingModeType) -> Unit,
     val onChangeOrientation: (OrientationType) -> Unit,
     val preferences: ReaderPreferences = Injekt.get(),

+ 0 - 27
app/src/main/java/eu/kanade/tachiyomi/util/system/ContextExtensions.kt

@@ -7,20 +7,13 @@ import android.content.Context
 import android.content.Intent
 import android.content.pm.PackageManager
 import android.content.res.Configuration
-import android.graphics.Color
 import android.graphics.drawable.Drawable
 import android.net.Uri
 import android.os.Build
 import android.os.PowerManager
-import androidx.annotation.AttrRes
-import androidx.annotation.ColorInt
 import androidx.appcompat.view.ContextThemeWrapper
 import androidx.core.content.PermissionChecker
 import androidx.core.content.getSystemService
-import androidx.core.graphics.alpha
-import androidx.core.graphics.blue
-import androidx.core.graphics.green
-import androidx.core.graphics.red
 import androidx.core.net.toUri
 import com.hippo.unifile.UniFile
 import eu.kanade.domain.ui.UiPreferences
@@ -35,7 +28,6 @@ import tachiyomi.core.util.system.logcat
 import uy.kohesive.injekt.Injekt
 import uy.kohesive.injekt.api.get
 import java.io.File
-import kotlin.math.roundToInt
 
 /**
  * Copies a string to clipboard
@@ -69,25 +61,6 @@ fun Context.copyToClipboard(label: String, content: String) {
  */
 fun Context.hasPermission(permission: String) = PermissionChecker.checkSelfPermission(this, permission) == PermissionChecker.PERMISSION_GRANTED
 
-/**
- * Returns the color for the given attribute.
- *
- * @param resource the attribute.
- * @param alphaFactor the alpha number [0,1].
- */
-@ColorInt fun Context.getResourceColor(@AttrRes resource: Int, alphaFactor: Float = 1f): Int {
-    val typedArray = obtainStyledAttributes(intArrayOf(resource))
-    val color = typedArray.getColor(0, 0)
-    typedArray.recycle()
-
-    if (alphaFactor < 1f) {
-        val alpha = (color.alpha * alphaFactor).roundToInt()
-        return Color.argb(alpha, color.red, color.green, color.blue)
-    }
-
-    return color
-}
-
 val Context.powerManager: PowerManager
     get() = getSystemService()!!
 

+ 0 - 45
app/src/main/java/eu/kanade/tachiyomi/util/view/ViewExtensions.kt

@@ -2,7 +2,6 @@
 
 package eu.kanade.tachiyomi.util.view
 
-import android.annotation.SuppressLint
 import android.content.Context
 import android.content.res.Resources
 import android.graphics.Rect
@@ -15,8 +14,6 @@ import androidx.activity.ComponentActivity
 import androidx.activity.compose.setContent
 import androidx.annotation.MenuRes
 import androidx.annotation.StringRes
-import androidx.appcompat.content.res.AppCompatResources
-import androidx.appcompat.view.menu.MenuBuilder
 import androidx.appcompat.widget.PopupMenu
 import androidx.appcompat.widget.TooltipCompat
 import androidx.compose.material3.LocalContentColor
@@ -27,11 +24,9 @@ import androidx.compose.runtime.CompositionContext
 import androidx.compose.runtime.CompositionLocalProvider
 import androidx.compose.ui.platform.ComposeView
 import androidx.compose.ui.platform.ViewCompositionStrategy
-import androidx.core.view.forEach
 import com.google.android.material.shape.MaterialShapeDrawable
 import eu.kanade.presentation.theme.TachiyomiTheme
 import eu.kanade.tachiyomi.R
-import eu.kanade.tachiyomi.util.system.getResourceColor
 
 inline fun ComponentActivity.setComposeContent(
     parent: CompositionContext? = null,
@@ -110,46 +105,6 @@ inline fun View.popupMenu(
     return popup
 }
 
-/**
- * Shows a popup menu on top of this view.
- *
- * @param items menu item names to inflate the menu with. List of itemId to stringRes pairs.
- * @param selectedItemId optionally show a checkmark beside an item with this itemId.
- * @param onMenuItemClick function to execute when a menu item is clicked.
- */
-@SuppressLint("RestrictedApi")
-inline fun View.popupMenu(
-    items: List<Pair<Int, Int>>,
-    selectedItemId: Int? = null,
-    noinline onMenuItemClick: MenuItem.() -> Unit,
-): PopupMenu {
-    val popup = PopupMenu(context, this, Gravity.NO_GRAVITY, R.attr.actionOverflowMenuStyle, 0)
-    items.forEach { (id, stringRes) ->
-        popup.menu.add(0, id, 0, stringRes)
-    }
-
-    if (selectedItemId != null) {
-        (popup.menu as? MenuBuilder)?.setOptionalIconsVisible(true)
-        val emptyIcon = AppCompatResources.getDrawable(context, R.drawable.ic_blank_24dp)
-        popup.menu.forEach { item ->
-            item.icon = when (item.itemId) {
-                selectedItemId -> AppCompatResources.getDrawable(context, R.drawable.ic_check_24dp)?.mutate()?.apply {
-                    setTint(context.getResourceColor(android.R.attr.textColorPrimary))
-                }
-                else -> emptyIcon
-            }
-        }
-    }
-
-    popup.setOnMenuItemClickListener {
-        it.onMenuItemClick()
-        true
-    }
-
-    popup.show()
-    return popup
-}
-
 /**
  * Returns a deep copy of the provided [Drawable]
  */

+ 0 - 9
app/src/main/res/drawable/ic_check_24dp.xml

@@ -1,9 +0,0 @@
-<vector xmlns:android="http://schemas.android.com/apk/res/android"
-    android:width="24dp"
-    android:height="24dp"
-    android:viewportWidth="24.0"
-    android:viewportHeight="24.0">
-    <path
-        android:fillColor="#000"
-        android:pathData="M9,16.17L4.83,12l-1.42,1.41L9,19 21,7l-1.41,-1.41z" />
-</vector>