Skip to content

Commit e5752f5

Browse files
committed
feat:support auto play
1 parent ec41475 commit e5752f5

File tree

10 files changed

+297
-2
lines changed

10 files changed

+297
-2
lines changed

app/src/main/java/eu/kanade/tachiyomi/data/preference/PreferencesHelper.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -484,4 +484,6 @@ class PreferencesHelper(val context: Context) {
484484
fun coverColors() = flowPrefs.getStringSet(Keys.coverColors, emptySet())
485485

486486
fun useStaggeredGrid() = flowPrefs.getBoolean("use_staggered_grid", false)
487+
488+
fun useAutoPlayProgress() = flowPrefs.getBoolean("use_auto_play_progress", false)
487489
}
Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,147 @@
1+
package eu.kanade.tachiyomi.ui.reader
2+
3+
import android.annotation.SuppressLint
4+
import android.app.Dialog
5+
import android.os.Bundle
6+
import androidx.appcompat.app.AppCompatDelegate
7+
import androidx.compose.foundation.clickable
8+
import androidx.compose.foundation.isSystemInDarkTheme
9+
import androidx.compose.foundation.layout.Arrangement
10+
import androidx.compose.foundation.layout.Box
11+
import androidx.compose.foundation.layout.Column
12+
import androidx.compose.foundation.layout.Row
13+
import androidx.compose.foundation.layout.fillMaxWidth
14+
import androidx.compose.foundation.layout.padding
15+
import androidx.compose.material3.LocalTextStyle
16+
import androidx.compose.material3.MaterialTheme
17+
import androidx.compose.material3.ProvideTextStyle
18+
import androidx.compose.material3.Switch
19+
import androidx.compose.runtime.Composable
20+
import androidx.compose.runtime.MutableState
21+
import androidx.compose.runtime.State
22+
import androidx.compose.runtime.collectAsState
23+
import androidx.compose.runtime.getValue
24+
import androidx.compose.runtime.mutableStateOf
25+
import androidx.compose.runtime.remember
26+
import androidx.compose.runtime.setValue
27+
import androidx.compose.ui.Alignment
28+
import androidx.compose.ui.Modifier
29+
import androidx.compose.ui.graphics.Color
30+
import androidx.compose.ui.platform.ComposeView
31+
import androidx.compose.ui.platform.LocalContext
32+
import androidx.compose.ui.unit.dp
33+
import androidx.fragment.app.DialogFragment
34+
import com.chargemap.compose.numberpicker.ListItemPicker
35+
import com.google.android.material.dialog.MaterialAlertDialogBuilder
36+
import eu.kanade.tachiyomi.R
37+
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
38+
import uy.kohesive.injekt.injectLazy
39+
40+
class AutoPlayDialogFragment : DialogFragment() {
41+
42+
private var positiveListener: ((Int) -> Unit)? = null
43+
44+
private val preferences: PreferencesHelper by injectLazy()
45+
46+
@SuppressLint("ResourceType")
47+
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
48+
val possibleValues = (1..15).map {
49+
"$it S"
50+
}
51+
val state = mutableStateOf(possibleValues[0])
52+
53+
return MaterialAlertDialogBuilder(requireContext())
54+
.setTitle(requireContext().getString(R.string.auto_play))
55+
.setView(
56+
ComposeView(requireContext()).apply {
57+
setContent {
58+
val isDarkTheme = when (AppCompatDelegate.getDefaultNightMode()) {
59+
AppCompatDelegate.MODE_NIGHT_YES -> true
60+
AppCompatDelegate.MODE_NIGHT_NO -> false
61+
else -> isSystemInDarkTheme() // You can define this function to check system theme preference if needed
62+
}
63+
MaterialTheme {
64+
ProvideTextStyle(
65+
value = LocalTextStyle.current.copy(
66+
color = if (isDarkTheme) Color.White else Color.Black,
67+
),
68+
) {
69+
AutoPlayDialogContent(
70+
state,
71+
possibleValues,
72+
preferences.useAutoPlayProgress().asFlow()
73+
.collectAsState(
74+
initial = preferences.useAutoPlayProgress().get(),
75+
),
76+
) {
77+
preferences.useAutoPlayProgress().set(it)
78+
}
79+
}
80+
}
81+
}
82+
},
83+
).apply {
84+
setPositiveButton(requireContext().getString(R.string.start)) { _, _ ->
85+
val position = possibleValues.indexOf(state.value) + 1
86+
positiveListener?.invoke(position * 1000)
87+
}
88+
setNegativeButton(requireContext().getString(R.string.cancel)) { _, _ ->
89+
}
90+
}
91+
.create()
92+
}
93+
94+
fun setPositiveListener(listener: (Int) -> Unit) {
95+
positiveListener = listener
96+
}
97+
}
98+
99+
@Composable
100+
fun AutoPlayDialogContent(
101+
number: MutableState<String>,
102+
possibleValues: List<String>,
103+
useProgress: State<Boolean>,
104+
onProgressEnable: (Boolean) -> Unit = {},
105+
) {
106+
Box(
107+
contentAlignment = Alignment.Center,
108+
) {
109+
var state by remember { number }
110+
val isUseProgress by remember { useProgress }
111+
Column(
112+
modifier = Modifier
113+
.fillMaxWidth(),
114+
horizontalAlignment = Alignment.CenterHorizontally,
115+
) {
116+
ListItemPicker(
117+
label = { it },
118+
value = state,
119+
onValueChange = { state = it },
120+
dividersColor = MaterialTheme.colorScheme.primary,
121+
list = possibleValues,
122+
textStyle = LocalTextStyle.current,
123+
)
124+
Row(
125+
modifier = Modifier
126+
.fillMaxWidth()
127+
.clickable {
128+
onProgressEnable.invoke(useProgress.value.not())
129+
}
130+
.padding(horizontal = 16.dp, vertical = 8.dp),
131+
horizontalArrangement = Arrangement.SpaceBetween,
132+
verticalAlignment = Alignment.CenterVertically,
133+
) {
134+
androidx.compose.material3.Text(
135+
text = LocalContext.current.getString(R.string.use_auto_play_progress),
136+
fontSize = MaterialTheme.typography.titleMedium.fontSize,
137+
)
138+
Switch(
139+
checked = isUseProgress,
140+
onCheckedChange = {
141+
onProgressEnable.invoke(it)
142+
},
143+
)
144+
}
145+
}
146+
}
147+
}

app/src/main/java/eu/kanade/tachiyomi/ui/reader/ReaderActivity.kt

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -104,6 +104,7 @@ import eu.kanade.tachiyomi.ui.reader.viewer.pager.VerticalPagerViewer
104104
import eu.kanade.tachiyomi.ui.reader.viewer.webtoon.WebtoonViewer
105105
import eu.kanade.tachiyomi.ui.security.SecureActivityDelegate
106106
import eu.kanade.tachiyomi.ui.webview.WebViewActivity
107+
import eu.kanade.tachiyomi.util.AutoPlayTimer
107108
import eu.kanade.tachiyomi.util.chapter.ChapterUtil.Companion.preferredChapterName
108109
import eu.kanade.tachiyomi.util.storage.getUriCompat
109110
import eu.kanade.tachiyomi.util.system.ThemeUtil
@@ -228,6 +229,8 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
228229
private var backPressedCallback: OnBackPressedCallback? = null
229230

230231
var isScrollingThroughPagesOrChapters = false
232+
233+
private var autoPlayTimer: AutoPlayTimer? = null
231234
private var hingeGapSize = 0
232235
set(value) {
233236
field = value
@@ -1167,6 +1170,30 @@ class ReaderActivity : BaseActivity<ReaderActivityBinding>() {
11671170
}
11681171
binding.chaptersSheet.chaptersBottomSheet.sheetBehavior?.collapse()
11691172
}
1173+
1174+
binding.toolbar.menu.findItem(R.id.action_auto_play)?.setOnMenuItemClickListener {
1175+
autoPlayTimer?.cancelTickAndProgress()
1176+
1177+
val dialog = AutoPlayDialogFragment()
1178+
dialog.setPositiveListener {
1179+
autoPlayTimer?.cancelTickAndProgress()
1180+
autoPlayTimer = AutoPlayTimer(
1181+
2 * 60 * 60 * 1000,
1182+
10,
1183+
max = it,
1184+
progressBar = binding.autoPlayProgressBar,
1185+
).apply {
1186+
nextPageFun = {
1187+
viewer?.moveToNext()
1188+
}
1189+
}
1190+
autoPlayTimer?.startTickAndProgress()
1191+
setMenuVisibility(false)
1192+
}
1193+
dialog.show(supportFragmentManager, "AutoPlayDialog")
1194+
true
1195+
}
1196+
autoPlayTimer?.cancelTickAndProgress()
11701197
} else {
11711198
if (preferences.fullscreen().get()) {
11721199
wic.hide(systemBars())
Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,102 @@
1+
package eu.kanade.tachiyomi.util
2+
3+
import android.os.CountDownTimer
4+
import androidx.core.view.isVisible
5+
import com.google.android.material.progressindicator.LinearProgressIndicator
6+
import eu.kanade.tachiyomi.data.preference.PreferencesHelper
7+
import uy.kohesive.injekt.injectLazy
8+
9+
class AutoPlayTimer(
10+
millisInFuture: Long,
11+
private val countDownInterval: Long,
12+
private val max: Int,
13+
private val progressBar: LinearProgressIndicator,
14+
) :
15+
CountDownTimer(millisInFuture, countDownInterval) {
16+
17+
private val preferences: PreferencesHelper by injectLazy()
18+
private var currentTime = 0
19+
var nextPageFun: (() -> Unit)? = null
20+
var doTick: DoTick = DoTick.PositivePlus
21+
override fun onTick(millisUntilFinished: Long) {
22+
currentTime += countDownInterval.toInt()
23+
doTick.doTick(currentTime, max, progressBar)
24+
if (currentTime >= max) {
25+
currentTime = 0
26+
doTick = when (doTick) {
27+
DoTick.PositivePlus -> DoTick.NegativeMinus
28+
DoTick.NegativeMinus -> DoTick.NegativePlus
29+
DoTick.NegativePlus -> DoTick.PositiveMinus
30+
else -> DoTick.PositivePlus
31+
}
32+
nextPageFun?.invoke()
33+
}
34+
}
35+
36+
override fun onFinish() {
37+
progressBar.progress = 0
38+
progressBar.isVisible = false
39+
}
40+
41+
fun cancelTickAndProgress() {
42+
onFinish()
43+
cancel()
44+
}
45+
46+
fun startTickAndProgress() {
47+
progressBar.isVisible = true && preferences.useAutoPlayProgress().get()
48+
start()
49+
}
50+
51+
sealed class DoTick {
52+
open fun doTick(currentProgress: Int, max: Int, progressBar: LinearProgressIndicator) {}
53+
54+
object PositivePlus : DoTick() {
55+
override fun doTick(
56+
currentProgress: Int,
57+
max: Int,
58+
progressBar: LinearProgressIndicator,
59+
) {
60+
progressBar.scaleX = 1f
61+
progressBar.progress = currentProgress
62+
progressBar.max = max
63+
}
64+
}
65+
66+
object NegativePlus : DoTick() {
67+
override fun doTick(
68+
currentProgress: Int,
69+
max: Int,
70+
progressBar: LinearProgressIndicator,
71+
) {
72+
progressBar.scaleX = -1f
73+
progressBar.progress = currentProgress
74+
progressBar.max = max
75+
}
76+
}
77+
78+
object PositiveMinus : DoTick() {
79+
override fun doTick(
80+
currentProgress: Int,
81+
max: Int,
82+
progressBar: LinearProgressIndicator,
83+
) {
84+
progressBar.scaleX = 1f
85+
progressBar.progress = max - currentProgress
86+
progressBar.max = max
87+
}
88+
}
89+
90+
object NegativeMinus : DoTick() {
91+
override fun doTick(
92+
currentProgress: Int,
93+
max: Int,
94+
progressBar: LinearProgressIndicator,
95+
) {
96+
progressBar.scaleX = -1f
97+
progressBar.progress = max - currentProgress
98+
progressBar.max = max
99+
}
100+
}
101+
}
102+
}

app/src/main/java/eu/kanade/tachiyomi/widget/preference/TrackLoginDialog.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package eu.kanade.tachiyomi.widget.preference
33
import android.os.Bundle
44
import android.view.View
55
import androidx.annotation.StringRes
6-
import br.com.simplepass.loadingbutton.animatedDrawables.ProgressType
6+
import com.github.leandroborgesferreira.loadingbutton.animatedDrawables.ProgressType
77
import eu.kanade.tachiyomi.R
88
import eu.kanade.tachiyomi.data.track.TrackManager
99
import eu.kanade.tachiyomi.data.track.TrackService

app/src/main/res/layout/pref_account_login.xml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@
5050
</com.google.android.material.textfield.TextInputLayout>
5151

5252

53-
<br.com.simplepass.loadingbutton.customViews.CircularProgressButton
53+
<com.github.leandroborgesferreira.loadingbutton.customViews.CircularProgressButton
5454
android:id="@+id/login"
5555
style="@style/Widget.Tachiyomi.Button.Primary"
5656
android:layout_width="match_parent"

app/src/main/res/layout/reader_activity.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -69,6 +69,14 @@
6969
android:layout_height="match_parent"
7070
/>
7171

72+
<com.google.android.material.progressindicator.LinearProgressIndicator
73+
android:id="@+id/auto_play_progress_bar"
74+
android:layout_width="match_parent"
75+
android:layout_height="wrap_content"
76+
android:layout_gravity="top"
77+
android:visibility="gone"
78+
tools:visibility="visible" />
79+
7280
<FrameLayout
7381
android:id="@+id/nav_layout"
7482
android:layout_width="match_parent"

app/src/main/res/menu/reader.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,11 @@
22
<menu xmlns:android="http://schemas.android.com/apk/res/android"
33
xmlns:app="http://schemas.android.com/apk/res-auto">
44

5+
<item
6+
android:id="@+id/action_auto_play"
7+
android:icon="@drawable/ic_play_arrow_24dp"
8+
android:title="@string/auto_play"
9+
app:showAsAction="always" />
510
<item
611
android:id="@+id/action_shift_double_page"
712
android:icon="@drawable/ic_page_next_outline_24dp"

app/src/main/res/values-zh-rCN/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1060,4 +1060,6 @@
10601060
<string name="invalid_repo_name">仓库网址无效</string>
10611061
<string name="delete_repo_confirmation">确定要删除仓库“%s”吗?</string>
10621062
<string name="error_repo_exists">此仓库网址已存在!</string>
1063+
<string name="auto_play">自动翻页</string>
1064+
<string name="use_auto_play_progress">显示翻页进度条</string>
10631065
</resources>

app/src/main/res/values/strings.xml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1203,4 +1203,6 @@
12031203
<string name="view_chapters">View chapters</string>
12041204
<string name="warning">Warning</string>
12051205
<string name="wifi">Wi-Fi</string>
1206+
<string name="auto_play">Auto play</string>
1207+
<string name="use_auto_play_progress">Use auto play progress bar</string>
12061208
</resources>

0 commit comments

Comments
 (0)