Compose bottom reader menu (#479)

* Migrate bottom reader menu to Compose

* Use Compose to animate bottom reader menu bars

* Cleanup unused

* Refine

* Cleanup to use DropdownMenu

* Cleanup unused

* Refine BottomReaderBar layout

---------

Co-authored-by: GG Bond <whatever@tagkong.com>
This commit is contained in:
Vladimir DostoyevskiyandGG Bond authored and GitHub committed 2023-12-08 17:23:51 +00:00
1 parent bbae24aba9
commit 0746938cb9
15 files changed
+193 -413

No files matched your search

@@ -12,6 +12,8 @@ import androidx.compose.runtime.remember
import com.hippo.ehviewer.client.CHROME_USER_AGENT
import com.hippo.ehviewer.client.EhUtils
import com.hippo.ehviewer.client.data.FavListUrlBuilder
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
import java.util.Locale
import kotlin.reflect.KProperty
import kotlinx.coroutines.flow.Flow
@@ -177,6 +179,9 @@ object Settings : DataStorePreferences(null) {
val colorFilterMode = intPref("color_filter_mode", 0)
val customBrightness = boolPref("pref_custom_brightness_key", false)
val customBrightnessValue = intPref("custom_brightness_value", 0)
val readingMode = intPref("pref_default_reading_mode_key", ReadingModeType.LEFT_TO_RIGHT.flagValue)
val orientationMode = intPref("pref_default_orientation_type_key", OrientationType.FREE.flagValue)
val showReaderSeekbar = boolPref("pref_show_reader_seekbar", true)
init {
if ("CN" == Locale.getDefault().country) {
@@ -0,0 +1,118 @@
package eu.kanade.tachiyomi.ui.reader
import androidx.compose.foundation.background
import androidx.compose.foundation.isSystemInDarkTheme
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Check
import androidx.compose.material.icons.outlined.Settings
import androidx.compose.material3.DropdownMenu
import androidx.compose.material3.DropdownMenuItem
import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.material3.surfaceColorAtElevation
import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.DpOffset
import androidx.compose.ui.unit.dp
import com.hippo.ehviewer.R
import com.hippo.ehviewer.Settings
import com.hippo.ehviewer.collectAsState
import eu.kanade.tachiyomi.ui.reader.setting.OrientationType
import eu.kanade.tachiyomi.ui.reader.setting.ReadingModeType
@Composable
fun BottomReaderBar(onClickSettings: () -> Unit) {
// Match with toolbar background color set in ReaderActivity
val backgroundColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp).copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f)
val readingMode by Settings.readingMode.collectAsState { ReadingModeType.fromPreference(it) }
var readingModeExpanded by remember { mutableStateOf(false) }
Box {
DropdownMenu(
readingModeExpanded,
onDismissRequest = { readingModeExpanded = false },
) {
ReadingModeType.entries.forEach {
DropdownMenuItem(
text = { Text(stringResource(it.stringRes)) },
onClick = {
Settings.readingMode.value = it.flagValue
readingModeExpanded = false
},
modifier = Modifier.width(192.dp),
leadingIcon = {
if (readingMode == it) {
Icon(imageVector = Icons.Default.Check, contentDescription = null)
}
},
)
}
}
}
val orientationMode by Settings.orientationMode.collectAsState { OrientationType.fromPreference(it) }
var orientationModeExpanded by remember { mutableStateOf(false) }
Box {
DropdownMenu(
orientationModeExpanded,
onDismissRequest = { orientationModeExpanded = false },
offset = DpOffset(320.dp, 0.dp),
) {
OrientationType.entries.forEach {
DropdownMenuItem(
text = { Text(stringResource(it.stringRes)) },
onClick = {
Settings.orientationMode.value = it.flagValue
orientationModeExpanded = false
},
modifier = Modifier.width(192.dp),
leadingIcon = {
if (orientationMode == it) {
Icon(imageVector = Icons.Default.Check, contentDescription = null)
}
},
)
}
}
}
Row(
modifier = Modifier.fillMaxWidth().background(backgroundColor).padding(8.dp),
horizontalArrangement = Arrangement.SpaceEvenly,
verticalAlignment = Alignment.CenterVertically,
) {
IconButton(onClick = { readingModeExpanded = true }) {
Icon(
painter = painterResource(readingMode.iconRes),
contentDescription = stringResource(R.string.viewer),
)
}
IconButton(onClick = { orientationModeExpanded = true }) {
Icon(
painter = painterResource(orientationMode.iconRes),
contentDescription = stringResource(R.string.pref_rotation_type),
)
}
IconButton(onClick = onClickSettings) {
Icon(
imageVector = Icons.Outlined.Settings,
contentDescription = stringResource(R.string.action_settings),
)
}
}
}
@@ -48,14 +48,15 @@ fun ChapterNavigator(
val maxTickCount = configuration.screenWidthDp / 6
val showTicks = totalPages < maxTickCount
// Match with toolbar background color set in ReaderActivity
val backgroundColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp).copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f)
// We explicitly handle direction based on the reader viewer rather than the system direction
CompositionLocalProvider(LocalLayoutDirection provides LayoutDirection.Ltr) {
Row(
modifier = Modifier.fillMaxWidth().padding(horizontal = horizontalPadding),
verticalAlignment = Alignment.CenterVertically,
) {
// Match with toolbar background color set in ReaderActivity
val backgroundColor = MaterialTheme.colorScheme.surfaceColorAtElevation(3.dp).copy(alpha = if (isSystemInDarkTheme()) 0.9f else 0.95f)
if (totalPages > 1) {
CompositionLocalProvider(LocalLayoutDirection provides layoutDirection) {
Row(
@@ -37,8 +37,6 @@ import android.view.KeyEvent
import android.view.MotionEvent
import android.view.View.LAYER_TYPE_HARDWARE
import android.view.WindowManager
import android.view.animation.Animation
import android.view.animation.AnimationUtils
import android.webkit.MimeTypeMap
import android.widget.Toast
import androidx.activity.result.contract.ActivityResultContracts.CreateDocument
@@ -102,12 +100,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.R2LPagerViewer
import eu.kanade.tachiyomi.util.lang.launchIO
import eu.kanade.tachiyomi.util.lang.launchUI
import eu.kanade.tachiyomi.util.lang.withUIContext
import eu.kanade.tachiyomi.util.system.applySystemAnimatorScale
import eu.kanade.tachiyomi.util.system.isNightMode
import eu.kanade.tachiyomi.util.view.copy
import eu.kanade.tachiyomi.util.view.popupMenu
import eu.kanade.tachiyomi.util.view.setTooltip
import eu.kanade.tachiyomi.widget.listener.SimpleAnimationListener
import java.io.File
import java.io.IOException
import kotlinx.coroutines.FlowPreview
@@ -142,8 +135,7 @@ class ReaderActivity : EhActivity() {
/**
* Whether the menu is currently visible.
*/
var menuVisible = false
private set
var menuVisible by mutableStateOf(false)
private var saveImageToLauncher = registerForActivityResult(
CreateDocument("todo/todo"),
@@ -321,6 +313,21 @@ class ReaderActivity : EhActivity() {
}
}
dialogState.Intercept()
val showSeekbar by Settings.showReaderSeekbar.collectAsState()
ReaderAppBars(
visible = menuVisible,
isRtl = isRtl,
showSeekBar = showSeekbar,
currentPage = currentPage,
totalPages = totalPage,
onSliderValueChange = {
isScrollingThroughPages = true
moveToPageIndex(it)
},
onClickSettings = { readerSettingSheetDialog?.show() },
)
ReaderContentOverlay(
brightness = { brightnessValue }.takeIf { brightness && brightnessValue < 0 },
color = { colorOverlay }.takeIf { colorOverlayEnabled },
@@ -351,8 +358,6 @@ class ReaderActivity : EhActivity() {
mCurrentIndex = if (mPage >= 0) mPage else mGalleryProvider!!.startPage
}
totalPage = mGalleryProvider!!.size
val viewerMode = ReadingModeType.fromPreference(ReaderPreferences.defaultReadingMode().get())
binding.actionReadingMode.setImageResource(viewerMode.iconRes)
viewer?.destroy()
viewer = ReadingModeType.toViewer(ReaderPreferences.defaultReadingMode().get(), this)
isRtl = viewer is R2LPagerViewer
@@ -618,17 +623,10 @@ class ReaderActivity : EhActivity() {
* Sets the visibility of the menu according to [visible] and with an optional parameter to
* [animate] the views.
*/
fun setMenuVisibility(visible: Boolean, animate: Boolean = true) {
fun setMenuVisibility(visible: Boolean) {
menuVisible = visible
if (visible) {
windowInsetsController.show(WindowInsetsCompat.Type.systemBars())
binding.readerMenu.isVisible = true
if (animate) {
val bottomAnimation = AnimationUtils.loadAnimation(this, R.anim.enter_from_bottom)
bottomAnimation.applySystemAnimatorScale(this)
binding.readerMenuBottom.startAnimation(bottomAnimation)
}
if (ReaderPreferences.showPageNumber().get()) {
config?.setPageNumberVisibility(false)
@@ -640,19 +638,6 @@ class ReaderActivity : EhActivity() {
WindowInsetsControllerCompat.BEHAVIOR_SHOW_TRANSIENT_BARS_BY_SWIPE
}
if (animate) {
val bottomAnimation = AnimationUtils.loadAnimation(this, R.anim.exit_to_bottom)
bottomAnimation.applySystemAnimatorScale(this)
bottomAnimation.setAnimationListener(
object : SimpleAnimationListener() {
override fun onAnimationEnd(animation: Animation) {
binding.readerMenu.isVisible = false
}
},
)
binding.readerMenuBottom.startAnimation(bottomAnimation)
}
if (ReaderPreferences.showPageNumber().get()) {
config?.setPageNumberVisibility(true)
}
@@ -668,9 +653,9 @@ class ReaderActivity : EhActivity() {
*/
@SuppressLint("PrivateResource")
private fun initializeMenu() {
binding.readerMenuBottom.applyInsetter {
binding.dialogStub.applyInsetter {
type(navigationBars = true) {
margin(bottom = true, horizontal = true)
margin(vertical = true, horizontal = true)
}
}
@@ -686,26 +671,12 @@ class ReaderActivity : EhActivity() {
}
}
// Init listeners on bottom menu
binding.readerNav.setMD3Content {
ChapterNavigator(
isRtl = isRtl,
currentPage = currentPage,
totalPages = totalPage,
onSliderValueChange = {
isScrollingThroughPages = true
moveToPageIndex(it)
},
)
}
initBottomShortcuts()
readerSettingSheetDialog = ReaderSettingsSheet(this@ReaderActivity)
val toolbarBackground = MaterialShapeDrawable.createWithElevationOverlay(this).apply {
elevation = resources.getDimension(com.google.android.material.R.dimen.m3_sys_elevation_level2)
alpha = if (isNightMode()) 230 else 242 // 90% dark 95% light
}
binding.toolbarBottom.background = toolbarBackground.copy(this@ReaderActivity)
val toolbarColor = ColorUtils.setAlphaComponent(
toolbarBackground.resolvedTintColor,
@@ -719,59 +690,6 @@ class ReaderActivity : EhActivity() {
setMenuVisibility(menuVisible)
}
private fun initBottomShortcuts() {
// Reading mode
with(binding.actionReadingMode) {
setTooltip(R.string.viewer)
setOnClickListener {
popupMenu(
items = ReadingModeType.entries.map { it.flagValue to it.stringRes },
selectedItemId = ReaderPreferences.defaultReadingMode().get(),
) {
val newReadingMode = ReadingModeType.fromPreference(itemId)
ReaderPreferences.defaultReadingMode().set(newReadingMode.flagValue)
}
}
}
// Rotation
with(binding.actionRotation) {
setTooltip(R.string.rotation_type)
setOnClickListener {
popupMenu(
items = OrientationType.entries.map { it.flagValue to it.stringRes },
selectedItemId = ReaderPreferences.defaultOrientationType().get(),
) {
val newOrientation = OrientationType.fromPreference(itemId)
ReaderPreferences.defaultOrientationType().set(newOrientation.flagValue)
}
}
}
// Settings sheet
with(binding.actionSettings) {
setTooltip(R.string.action_settings)
readerSettingSheetDialog = ReaderSettingsSheet(this@ReaderActivity)
setOnClickListener {
if (!readerSettingSheetDialog!!.isShowing) {
readerSettingSheetDialog!!.show()
}
}
setOnLongClickListener {
ReaderSettingsSheet(this@ReaderActivity, showColorFilterSettings = true).show()
true
}
}
}
private fun updateOrientationShortcut(preference: Int) {
val orientation = OrientationType.fromPreference(preference)
binding.actionRotation.setImageResource(orientation.iconRes)
}
/**
* Dispatches a key event. If the viewer doesn't handle it, call the default implementation.
*/
@@ -828,15 +746,6 @@ class ReaderActivity : EhActivity() {
setMenuVisibility(!menuVisible)
}
/**
* Called from the viewer to show the menu.
*/
fun showMenu() {
if (!menuVisible) {
setMenuVisibility(true)
}
}
/**
* Called from the viewer to hide the menu.
*/
@@ -854,7 +763,6 @@ class ReaderActivity : EhActivity() {
if (newOrientation.flag != requestedOrientation) {
requestedOrientation = newOrientation.flag
}
updateOrientationShortcut(ReaderPreferences.defaultOrientationType().get())
}
/**
@@ -930,14 +838,6 @@ class ReaderActivity : EhActivity() {
.onEach { setPageNumberVisibility(it) }
.launchIn(lifecycleScope)
ReaderPreferences.showReaderSeekbar().changes()
.onEach { setReaderSeekbarVisibility(it) }
.launchIn(lifecycleScope)
ReaderPreferences.trueColor().changes()
.onEach { setTrueColor(it) }
.launchIn(lifecycleScope)
if (hasCutout) {
setCutoutShort(ReaderPreferences.cutoutShort().get())
ReaderPreferences.cutoutShort().changes()
@@ -996,17 +896,6 @@ class ReaderActivity : EhActivity() {
binding.pageNumber.isVisible = visible
}
fun setReaderSeekbarVisibility(visible: Boolean) {
binding.readerNav.isVisible = visible
}
/**
* Sets the 32-bit color mode according to [enabled].
*/
private fun setTrueColor(enabled: Boolean) {
// TODO()
}
@RequiresApi(Build.VERSION_CODES.P)
private fun setCutoutShort(enabled: Boolean) {
window.attributes.layoutInDisplayCutoutMode = when (enabled) {
@@ -0,0 +1,47 @@
package eu.kanade.tachiyomi.ui.reader
import androidx.compose.animation.AnimatedVisibility
import androidx.compose.animation.core.tween
import androidx.compose.animation.slideInVertically
import androidx.compose.animation.slideOutVertically
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.runtime.Composable
import androidx.compose.ui.Modifier
import androidx.compose.ui.unit.IntOffset
import androidx.compose.ui.unit.dp
private val animationSpec = tween<IntOffset>(200)
@Composable
fun ReaderAppBars(
visible: Boolean,
isRtl: Boolean,
showSeekBar: Boolean,
currentPage: Int,
totalPages: Int,
onSliderValueChange: (Int) -> Unit,
onClickSettings: () -> Unit,
) = Column(modifier = Modifier.fillMaxHeight(), verticalArrangement = Arrangement.SpaceBetween) {
Spacer(modifier = Modifier.weight(1f))
AnimatedVisibility(
visible = visible,
enter = slideInVertically(initialOffsetY = { it }, animationSpec = animationSpec),
exit = slideOutVertically(targetOffsetY = { it }, animationSpec = animationSpec),
) {
Column(verticalArrangement = Arrangement.spacedBy(8.dp)) {
if (showSeekBar) {
ChapterNavigator(
isRtl = isRtl,
currentPage = currentPage,
totalPages = totalPages,
onSliderValueChange = onSliderValueChange,
)
}
BottomReaderBar(onClickSettings = onClickSettings)
}
}
}
@@ -10,17 +10,6 @@ import kotlinx.coroutines.NonCancellable
import kotlinx.coroutines.launch
import kotlinx.coroutines.withContext
/**
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
*
* **Possible replacements**
* - suspend function
* - custom scope like view or presenter scope
*/
@DelicateCoroutinesApi
fun launchUI(block: suspend CoroutineScope.() -> Unit): Job =
GlobalScope.launch(Dispatchers.Main, CoroutineStart.DEFAULT, block)
/**
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
*
@@ -32,17 +21,6 @@ fun launchUI(block: suspend CoroutineScope.() -> Unit): Job =
fun launchIO(block: suspend CoroutineScope.() -> Unit): Job =
GlobalScope.launch(Dispatchers.IO, CoroutineStart.DEFAULT, block)
/**
* Think twice before using this. This is a delicate API. It is easy to accidentally create resource or memory leaks when GlobalScope is used.
*
* **Possible replacements**
* - suspend function
* - custom scope like view or presenter scope
*/
@DelicateCoroutinesApi
fun launchNow(block: suspend CoroutineScope.() -> Unit): Job =
GlobalScope.launch(Dispatchers.Main, CoroutineStart.UNDISPATCHED, block)
fun CoroutineScope.launchUI(block: suspend CoroutineScope.() -> Unit): Job =
launch(Dispatchers.Main, block = block)
@@ -26,8 +26,3 @@ operator fun <T> Preference<Set<T>>.plusAssign(item: T) {
operator fun <T> Preference<Set<T>>.minusAssign(item: T) {
set(get() - item)
}
fun Preference<Boolean>.toggle(): Boolean {
set(!get())
return get()
}
@@ -1,22 +0,0 @@
package eu.kanade.tachiyomi.util.system
import android.content.Context
import android.view.ViewPropertyAnimator
import android.view.animation.Animation
import androidx.constraintlayout.motion.widget.MotionScene.Transition
/** Scale the duration of this [Animation] by [Context.animatorDurationScale] */
fun Animation.applySystemAnimatorScale(context: Context) {
this.duration = (this.duration * context.animatorDurationScale).toLong()
}
/** Scale the duration of this [Transition] by [Context.animatorDurationScale] */
fun Transition.applySystemAnimatorScale(context: Context) {
// End layout of cover expanding animation tends to break when the transition is less than ~25ms
this.duration = (this.duration * context.animatorDurationScale).toInt().coerceAtLeast(25)
}
/** Scale the duration of this [ViewPropertyAnimator] by [Context.animatorDurationScale] */
fun ViewPropertyAnimator.applySystemAnimatorScale(context: Context): ViewPropertyAnimator = apply {
this.duration = (this.duration * context.animatorDurationScale).toLong()
}
@@ -1,111 +1,11 @@
@file:Suppress("NOTHING_TO_INLINE")
package eu.kanade.tachiyomi.util.view
import android.annotation.SuppressLint
import android.content.Context
import android.content.res.Resources
import android.graphics.Rect
import android.graphics.drawable.Drawable
import android.view.Gravity
import android.view.Menu
import android.view.MenuItem
import android.view.View
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.core.view.forEach
import com.google.android.material.shape.MaterialShapeDrawable
import com.hippo.ehviewer.R
import eu.kanade.tachiyomi.util.system.getResourceColor
/**
* Adds a tooltip shown on long press.
*
* @param stringRes String resource for tooltip.
*/
inline fun View.setTooltip(@StringRes stringRes: Int) {
setTooltip(context.getString(stringRes))
}
/**
* Adds a tooltip shown on long press.
*
* @param text Text for tooltip.
*/
inline fun View.setTooltip(text: String) {
TooltipCompat.setTooltipText(this, text)
}
/**
* Shows a popup menu on top of this view.
*
* @param menuRes menu items to inflate the menu with.
* @param initMenu function to execute when the menu after is inflated.
* @param onMenuItemClick function to execute when a menu item is clicked.
*/
inline fun View.popupMenu(
@MenuRes menuRes: Int,
noinline initMenu: (Menu.() -> Unit)? = null,
noinline onMenuItemClick: MenuItem.() -> Unit,
): PopupMenu {
val popup = PopupMenu(context, this, Gravity.NO_GRAVITY, androidx.appcompat.R.attr.actionOverflowMenuStyle, 0)
popup.menuInflater.inflate(menuRes, popup.menu)
if (initMenu != null) {
popup.menu.initMenu()
}
popup.setOnMenuItemClickListener {
it.onMenuItemClick()
true
}
popup.show()
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, androidx.appcompat.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]
@@ -1,11 +0,0 @@
package eu.kanade.tachiyomi.widget.listener
import android.view.animation.Animation
open class SimpleAnimationListener : Animation.AnimationListener {
override fun onAnimationRepeat(animation: Animation) {}
override fun onAnimationEnd(animation: Animation) {}
override fun onAnimationStart(animation: Animation) {}
}
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="200"
android:fromYDelta="100%"
android:toYDelta="0%" />
</set>
-8
View File
@@ -1,8 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<set xmlns:android="http://schemas.android.com/apk/res/android"
android:shareInterpolator="false">
<translate
android:duration="200"
android:fromYDelta="0%"
android:toYDelta="100%" />
</set>
@@ -1,9 +0,0 @@
<vector xmlns:android="http://schemas.android.com/apk/res/android"
android:width="24dp"
android:height="24dp"
android:viewportWidth="24"
android:viewportHeight="24">
<path
android:fillColor="@android:color/black"
android:pathData="M19.43,12.98c0.04,-0.32 0.07,-0.64 0.07,-0.98 0,-0.34 -0.03,-0.66 -0.07,-0.98l2.11,-1.65c0.19,-0.15 0.24,-0.42 0.12,-0.64l-2,-3.46c-0.09,-0.16 -0.26,-0.25 -0.44,-0.25 -0.06,0 -0.12,0.01 -0.17,0.03l-2.49,1c-0.52,-0.4 -1.08,-0.73 -1.69,-0.98l-0.38,-2.65C14.46,2.18 14.25,2 14,2h-4c-0.25,0 -0.46,0.18 -0.49,0.42l-0.38,2.65c-0.61,0.25 -1.17,0.59 -1.69,0.98l-2.49,-1c-0.06,-0.02 -0.12,-0.03 -0.18,-0.03 -0.17,0 -0.34,0.09 -0.43,0.25l-2,3.46c-0.13,0.22 -0.07,0.49 0.12,0.64l2.11,1.65c-0.04,0.32 -0.07,0.65 -0.07,0.98 0,0.33 0.03,0.66 0.07,0.98l-2.11,1.65c-0.19,0.15 -0.24,0.42 -0.12,0.64l2,3.46c0.09,0.16 0.26,0.25 0.44,0.25 0.06,0 0.12,-0.01 0.17,-0.03l2.49,-1c0.52,0.4 1.08,0.73 1.69,0.98l0.38,2.65c0.03,0.24 0.24,0.42 0.49,0.42h4c0.25,0 0.46,-0.18 0.49,-0.42l0.38,-2.65c0.61,-0.25 1.17,-0.59 1.69,-0.98l2.49,1c0.06,0.02 0.12,0.03 0.18,0.03 0.17,0 0.34,-0.09 0.43,-0.25l2,-3.46c0.12,-0.22 0.07,-0.49 -0.12,-0.64l-2.11,-1.65zM17.45,11.27c0.04,0.31 0.05,0.52 0.05,0.73 0,0.21 -0.02,0.43 -0.05,0.73l-0.14,1.13 0.89,0.7 1.08,0.84 -0.7,1.21 -1.27,-0.51 -1.04,-0.42 -0.9,0.68c-0.43,0.32 -0.84,0.56 -1.25,0.73l-1.06,0.43 -0.16,1.13 -0.2,1.35h-1.4l-0.19,-1.35 -0.16,-1.13 -1.06,-0.43c-0.43,-0.18 -0.83,-0.41 -1.23,-0.71l-0.91,-0.7 -1.06,0.43 -1.27,0.51 -0.7,-1.21 1.08,-0.84 0.89,-0.7 -0.14,-1.13c-0.03,-0.31 -0.05,-0.54 -0.05,-0.74s0.02,-0.43 0.05,-0.73l0.14,-1.13 -0.89,-0.7 -1.08,-0.84 0.7,-1.21 1.27,0.51 1.04,0.42 0.9,-0.68c0.43,-0.32 0.84,-0.56 1.25,-0.73l1.06,-0.43 0.16,-1.13 0.2,-1.35h1.39l0.19,1.35 0.16,1.13 1.06,0.43c0.43,0.18 0.83,0.41 1.23,0.71l0.91,0.7 1.06,-0.43 1.27,-0.51 0.7,1.21 -1.07,0.85 -0.89,0.7 0.14,1.13zM12,8c-2.21,0 -4,1.79 -4,4s1.79,4 4,4 4,-1.79 4,-4 -1.79,-4 -4,-4zM12,14c-1.1,0 -2,-0.9 -2,-2s0.9,-2 2,-2 2,0.9 2,2 -0.9,2 -2,2z" />
</vector>
-18
View File
@@ -1,18 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/standard_bottom_sheet"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="vertical">
<com.google.android.material.bottomsheet.BottomSheetDragHandleView
android:id="@+id/drag_handle"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
<androidx.compose.ui.platform.ComposeView
android:id="@+id/content"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>
@@ -1,6 +1,4 @@
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent">
@@ -37,79 +35,4 @@
android:layout_width="match_parent"
android:layout_height="match_parent" />
<FrameLayout
android:id="@+id/reader_menu"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:theme="?attr/actionBarTheme"
android:visibility="invisible"
tools:visibility="visible">
<LinearLayout
android:id="@+id/reader_menu_bottom"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_gravity="bottom"
android:orientation="vertical">
<androidx.compose.ui.platform.ComposeView
android:id="@+id/reader_nav"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_marginBottom="16dp"
android:layoutDirection="ltr" />
<androidx.constraintlayout.widget.ConstraintLayout
android:id="@+id/toolbar_bottom"
android:layout_width="match_parent"
android:layout_height="?attr/actionBarSize"
android:layout_gravity="bottom"
android:clickable="true"
tools:ignore="KeyboardInaccessibleWidget">
<ImageButton
android:id="@+id/action_reading_mode"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/viewer"
android:padding="@dimen/screen_edge_margin"
app:layout_constraintEnd_toStartOf="@id/action_rotation"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_reader_default_24dp"
app:tint="?attr/colorOnSurface" />
<ImageButton
android:id="@+id/action_rotation"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/pref_rotation_type"
android:padding="@dimen/screen_edge_margin"
app:layout_constraintEnd_toStartOf="@id/action_settings"
app:layout_constraintStart_toEndOf="@+id/action_reading_mode"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_screen_rotation_24dp"
app:tint="?attr/colorOnSurface" />
<ImageButton
android:id="@+id/action_settings"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:background="?attr/selectableItemBackgroundBorderless"
android:contentDescription="@string/action_settings"
android:padding="@dimen/screen_edge_margin"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toEndOf="@id/action_rotation"
app:layout_constraintTop_toTopOf="parent"
app:srcCompat="@drawable/ic_settings_24dp"
app:tint="?attr/colorOnSurface" />
</androidx.constraintlayout.widget.ConstraintLayout>
</LinearLayout>
</FrameLayout>
</FrameLayout>