Compare commits

...
Sign in to create a new pull request.

6 commits
dev ... main

Author SHA1 Message Date
rebel onion
a93b4f5b11 Merge branch 'main' of https://github.com/rebelonion/Dantotsu 2025-05-14 21:40:08 -05:00
rebel onion
69c44b7d20 chore: formatting changes 2025-05-14 21:40:06 -05:00
Rishvaish
a684aac0b1
To install multiple mangas (#582)
users can enter the value required to install as there is an EditText field instead of the Text View
2025-04-02 10:40:39 +05:30
Daniele Santoru
6c49839f87
Fixed missing manga pages when downloading (#586) 2025-04-02 10:39:33 +05:30
rebel onion
7053a7b4b2
Update README.md 2025-01-16 20:27:24 -06:00
rebel onion
1c156053d0
Merge pull request #565 from rebelonion/dev
Dev
2025-01-16 00:15:34 -06:00
14 changed files with 178 additions and 120 deletions

View file

@ -14,7 +14,7 @@ Dantotsu is an [Anilist](https://anilist.co/) only client.
> **Dantotsu (断トツ; Dan-totsu)** literally means "the best of the best" in Japanese. Try it out for yourself and be the judge!
<a href="https://www.buymeacoffee.com/rebelonion"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=rebelonion&button_colour=FFDD00&font_colour=030201&font_family=Poppins&outline_colour=000000&coffee_colour=ffffff" /></a>
<a href="https://www.buymeacoffee.com/rebelonion"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=rebelonion&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff" /></a>
## Terms of Use
By downloading, installing, or using this application, you agree to:

View file

@ -17,9 +17,8 @@ android {
applicationId "ani.dantotsu"
minSdk 21
targetSdk 35
versionCode((System.currentTimeMillis() / 60000).toInteger())
versionName "3.2.1"
versionCode 300200100
versionName "3.2.2"
versionCode 300200200
signingConfig signingConfigs.debug
}

View file

@ -50,7 +50,8 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) {
val assetApi = RPCExternalAsset(data.applicationId, token!!, client, json)
suspend fun String.discordUrl() = assetApi.getDiscordUri(this)
return json.encodeToString(Presence.Response(
return json.encodeToString(
Presence.Response(
3,
Presence(
activities = listOf(

View file

@ -232,12 +232,18 @@ class MangaDownloaderService : Service() {
image.page,
image.source
)
if (bitmap == null) {
snackString("${task.chapter} - Retrying to download page ${index.ofLength(3)}, attempt ${retryCount + 1}.")
}
retryCount++
}
if (bitmap != null) {
saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
if (bitmap == null) {
outputDir.deleteRecursively(this@MangaDownloaderService, false)
throw Exception("${task.chapter} - Unable to download all pages after $retryCount attempts. Try again.")
}
saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
farthest++
builder.setProgress(task.imageData.size, farthest, false)

View file

@ -427,7 +427,8 @@ class ExoplayerView :
false -> 0f
}
val textElevation = PrefManager.getVal<Float>(PrefName.SubBottomMargin) / 50 * resources.displayMetrics.heightPixels
val textElevation =
PrefManager.getVal<Float>(PrefName.SubBottomMargin) / 50 * resources.displayMetrics.heightPixels
textView.translationY = -textElevation
}
@ -1044,7 +1045,8 @@ class ExoplayerView :
}
}
override fun onSingleClick(event: MotionEvent) = if (isSeeking) doubleTap(false, event) else handleController()
override fun onSingleClick(event: MotionEvent) =
if (isSeeking) doubleTap(false, event) else handleController()
},
)
val rewindArea = playerView.findViewById<View>(R.id.exo_rewind_area)
@ -1079,7 +1081,8 @@ class ExoplayerView :
}
}
override fun onSingleClick(event: MotionEvent) = if (isSeeking) doubleTap(true, event) else handleController()
override fun onSingleClick(event: MotionEvent) =
if (isSeeking) doubleTap(true, event) else handleController()
},
)
val forwardArea = playerView.findViewById<View>(R.id.exo_forward_area)
@ -1449,7 +1452,8 @@ class ExoplayerView :
else -> mutableListOf()
}
val startTimestamp = Calendar.getInstance()
val durationInSeconds = if (exoPlayer.duration != C.TIME_UNSET) (exoPlayer.duration / 1000).toInt() else 1440
val durationInSeconds =
if (exoPlayer.duration != C.TIME_UNSET) (exoPlayer.duration / 1000).toInt() else 1440
val endTimestamp =
Calendar.getInstance().apply {
@ -1567,7 +1571,11 @@ class ExoplayerView :
subtitle = intent.getSerialized("subtitle")
?: when (
val subLang: String? =
PrefManager.getNullableCustomVal("subLang_${media.id}", null, String::class.java)
PrefManager.getNullableCustomVal(
"subLang_${media.id}",
null,
String::class.java
)
) {
null -> {
when (episode.selectedSubtitle) {
@ -1575,8 +1583,12 @@ class ExoplayerView :
-1 ->
ext.subtitles.find {
it.language.contains(lang, ignoreCase = true) ||
it.language.contains(getLanguageCode(lang), ignoreCase = true)
it.language.contains(
getLanguageCode(lang),
ignoreCase = true
)
}
else -> ext.subtitles.getOrNull(episode.selectedSubtitle!!)
}
}
@ -1651,7 +1663,8 @@ class ExoplayerView :
}.build()
val dataSourceFactory =
DataSource.Factory {
val dataSource: HttpDataSource = OkHttpDataSource.Factory(httpClient).createDataSource()
val dataSource: HttpDataSource =
OkHttpDataSource.Factory(httpClient).createDataSource()
defaultHeaders.forEach {
dataSource.setRequestProperty(it.key, it.value)
}
@ -1719,12 +1732,14 @@ class ExoplayerView :
it.name?.endsWith(".mp4") == true ||
it.name?.endsWith(".mkv") == true ||
it.name?.endsWith(
".${Injekt
".${
Injekt
.get<DownloadAddonManager>()
.extension
?.extension
?.getFileExtension()
?.first ?: "ts"}",
?.first ?: "ts"
}",
) ==
true
}
@ -1928,7 +1943,7 @@ class ExoplayerView :
if (PrefManager.getVal<Boolean>(PrefName.TextviewSubtitles)) {
exoSubtitleView.visibility = View.GONE
customSubtitleView.visibility = View.VISIBLE
val newCues = cueGroup.cues.map { it.text.toString() ?: "" }
val newCues = cueGroup.cues.map { it.text.toString() }
if (newCues.isEmpty()) {
customSubtitleView.text = ""
@ -1940,7 +1955,9 @@ class ExoplayerView :
val currentPosition = exoPlayer.currentPosition
if ((lastSubtitle?.length ?: 0) < 20 || (lastPosition != 0L && currentPosition - lastPosition > 1500)) {
if ((lastSubtitle?.length
?: 0) < 20 || (lastPosition != 0L && currentPosition - lastPosition > 1500)
) {
activeSubtitles.clear()
}
@ -2213,7 +2230,8 @@ class ExoplayerView :
override fun onTick(millisUntilFinished: Long) {
if (new == null) {
skipTimeButton.visibility = View.GONE
exoSkip.isVisible = PrefManager.getVal<Int>(PrefName.SkipTime) > 0
exoSkip.isVisible =
PrefManager.getVal<Int>(PrefName.SkipTime) > 0
disappeared = false
functionstarted = false
cancelTimer()
@ -2222,7 +2240,8 @@ class ExoplayerView :
override fun onFinish() {
skipTimeButton.visibility = View.GONE
exoSkip.isVisible = PrefManager.getVal<Int>(PrefName.SkipTime) > 0
exoSkip.isVisible =
PrefManager.getVal<Int>(PrefName.SkipTime) > 0
disappeared = true
functionstarted = false
cancelTimer()

View file

@ -7,9 +7,11 @@ import android.view.View
import android.view.ViewGroup
import android.widget.ArrayAdapter
import android.widget.CheckBox
import android.widget.EditText
import android.widget.ImageButton
import android.widget.LinearLayout
import android.widget.NumberPicker
import android.widget.TextView
import androidx.core.content.ContextCompat
import androidx.core.content.ContextCompat.getString
import androidx.core.content.ContextCompat.startActivity
@ -265,19 +267,22 @@ class MangaReadAdapter(
}
// Multi download
downloadNo.text = "0"
//downloadNo.text = "0"
mediaDownloadTop.setOnClickListener {
// Alert dialog asking for the number of chapters to download
fragment.requireContext().customAlertDialog().apply {
setTitle("Multi Chapter Downloader")
setMessage("Enter the number of chapters to download")
val input = NumberPicker(currContext())
input.minValue = 1
input.maxValue = 20
input.value = 1
val input = View.inflate(currContext(), R.layout.dialog_layout, null)
val editText = input.findViewById<EditText>(R.id.downloadNo)
setCustomView(input)
setPosButton(R.string.ok) {
downloadNo.text = "${input.value}"
val value = editText.text.toString().toIntOrNull()
if (value != null && value > 0) {
downloadNo.setText(value.toString(), TextView.BufferType.EDITABLE)
fragment.multiDownload(value)
} else {
toast("Please enter a valid number")
}
}
setNegButton(R.string.cancel)
show()
@ -382,8 +387,9 @@ class MangaReadAdapter(
setCustomView(root)
setPosButton("OK") {
if (run) fragment.onIconPressed(style, reversed)
if (downloadNo.text != "0") {
fragment.multiDownload(downloadNo.text.toString().toInt())
val value = downloadNo.text.toString().toIntOrNull()
if (value != null && value > 0) {
fragment.multiDownload(value)
}
if (refresh) fragment.loadChapters(source, true)
}

View file

@ -474,7 +474,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
scanlator = chapter.scanlator ?: "Unknown",
imageData = images,
sourceMedia = media,
retries = 2,
retries = 25,
simultaneousDownloads = 2
)

View file

@ -193,7 +193,8 @@ class SettingsCommonActivity : AppCompatActivity() {
PrefManager.setVal(PrefName.OverridePassword, true)
}
val password = view.passwordInput.text.toString()
val confirmPassword = view.confirmPasswordInput.text.toString()
val confirmPassword =
view.confirmPasswordInput.text.toString()
if (password == confirmPassword && password.isNotEmpty()) {
PrefManager.setVal(PrefName.AppPassword, password)
if (view.biometricCheckbox.isChecked) {
@ -205,7 +206,9 @@ class SettingsCommonActivity : AppCompatActivity() {
if (canBiometricPrompt) {
val biometricPrompt =
BiometricPromptUtils.createBiometricPrompt(this@SettingsCommonActivity) { _ ->
BiometricPromptUtils.createBiometricPrompt(
this@SettingsCommonActivity
) { _ ->
val token = UUID.randomUUID().toString()
PrefManager.setVal(
PrefName.BiometricToken,
@ -235,12 +238,14 @@ class SettingsCommonActivity : AppCompatActivity() {
setOnShowListener {
view.passwordInput.requestFocus()
val canAuthenticate =
BiometricManager.from(applicationContext).canAuthenticate(
BiometricManager.from(applicationContext)
.canAuthenticate(
BiometricManager.Authenticators.BIOMETRIC_WEAK,
) == BiometricManager.BIOMETRIC_SUCCESS
view.biometricCheckbox.isVisible = canAuthenticate
view.biometricCheckbox.isChecked =
PrefManager.getVal(PrefName.BiometricToken, "").isNotEmpty()
PrefManager.getVal(PrefName.BiometricToken, "")
.isNotEmpty()
view.forgotPasswordCheckbox.isChecked =
PrefManager.getVal(PrefName.OverridePassword)
}
@ -314,7 +319,8 @@ class SettingsCommonActivity : AppCompatActivity() {
setTitle(R.string.change_download_location)
setMessage(R.string.download_location_msg)
setPosButton(R.string.ok) {
val oldUri = PrefManager.getVal<String>(PrefName.DownloadsDir)
val oldUri =
PrefManager.getVal<String>(PrefName.DownloadsDir)
launcher.registerForCallback { success ->
if (success) {
toast(getString(R.string.please_wait))

View file

@ -82,9 +82,18 @@ class SettingsNotificationActivity : AppCompatActivity() {
setTitle(R.string.subscriptions_checking_time)
singleChoiceItems(timeNames, curTime) { i ->
curTime = i
it.settingsTitle.text = getString(R.string.subscriptions_checking_time_s, timeNames[i])
PrefManager.setVal(PrefName.SubscriptionNotificationInterval, curTime)
TaskScheduler.create(context, PrefManager.getVal(PrefName.UseAlarmManager)).scheduleAllTasks(context)
it.settingsTitle.text = getString(
R.string.subscriptions_checking_time_s,
timeNames[i]
)
PrefManager.setVal(
PrefName.SubscriptionNotificationInterval,
curTime
)
TaskScheduler.create(
context,
PrefManager.getVal(PrefName.UseAlarmManager)
).scheduleAllTasks(context)
}
show()
}
@ -125,7 +134,8 @@ class SettingsNotificationActivity : AppCompatActivity() {
types.map { name ->
name.replace("_", " ").lowercase().replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString()
} }.toTypedArray(),
}
}.toTypedArray(),
selected
) { updatedSelected ->
types.forEachIndexed { index, type ->

View file

@ -96,7 +96,8 @@ class SettingsThemeActivity : AppCompatActivity(), SimpleDialog.OnDialogResultLi
themeSwitcher.apply {
setText(themeText)
setAdapter(
ArrayAdapter(context,
ArrayAdapter(
context,
R.layout.item_dropdown,
ThemeManager.Companion.Theme.entries.map {
it.theme.substring(

View file

@ -52,7 +52,8 @@ class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
}
groupedSubscriptions.forEach { (parserName, mediaList) ->
adapter.add(SubscriptionSource(
adapter.add(
SubscriptionSource(
parserName,
mediaList.toMutableList(),
adapter,

View file

@ -8,7 +8,7 @@
android:padding="16dp">
<LinearLayout
android:layout_width="wrap_content"
android:layout_width="326dp"
android:layout_height="wrap_content"
android:orientation="vertical">
@ -160,8 +160,8 @@
android:orientation="horizontal">
<LinearLayout
android:layout_width="265dp"
android:layout_height="match_parent"
android:layout_width="263dp"
android:layout_height="60dp"
android:orientation="vertical">
<TextView
@ -171,14 +171,23 @@
android:fontFamily="@font/poppins_bold"
android:text="@string/download" />
<TextView
<EditText
android:id="@+id/downloadNo"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:fontFamily="@font/poppins_bold"
android:textColor="?attr/colorSecondary"
android:textSize="12dp"
tools:ignore="TextContrastCheck"
tools:text="number" />
tools:text="Number" />
<!-- <TextView-->
<!-- android:id="@+id/downloadNo"-->
<!-- android:layout_width="match_parent"-->
<!-- android:layout_height="wrap_content"-->
<!-- android:fontFamily="@font/poppins_bold"-->
<!-- android:textColor="?attr/colorSecondary"-->
<!-- tools:ignore="TextContrastCheck"-->
<!-- tools:text="number" />-->
</LinearLayout>
<androidx.cardview.widget.CardView
@ -191,7 +200,7 @@
<ImageButton
android:id="@+id/mediaDownloadTop"
android:layout_width="48dp"
android:layout_height="48dp"
android:layout_height="60dp"
android:background="?android:attr/selectableItemBackground"
app:srcCompat="@drawable/ic_download_24"
app:tint="?attr/colorOnBackground"
@ -313,9 +322,9 @@
android:text="@string/reset" />
<TextView
android:id="@+id/reset_progress_def"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:id="@+id/reset_progress_def"
android:fontFamily="@font/poppins_bold"
android:text=""
android:textColor="?attr/colorSecondary"

View file

@ -12,7 +12,7 @@ buildscript {
}
dependencies {
classpath 'com.android.tools.build:gradle:8.7.3'
classpath 'com.android.tools.build:gradle:8.9.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
classpath "com.google.devtools.ksp:symbol-processing-api:$ksp_version"

View file

@ -1,6 +1,6 @@
#Wed Aug 30 19:57:04 IST 2023
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists