diff --git a/README.md b/README.md
index 76bece4b..6b713a2a 100644
--- a/README.md
+++ b/README.md
@@ -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!
-
+
## Terms of Use
By downloading, installing, or using this application, you agree to:
diff --git a/app/build.gradle b/app/build.gradle
index 915b3ba8..0604d87e 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -17,9 +17,8 @@ android {
applicationId "ani.dantotsu"
minSdk 21
targetSdk 35
- versionCode((System.currentTimeMillis() / 60000).toInteger())
- versionName "3.2.0"
- versionCode 300200000
+ versionName "3.2.2"
+ versionCode 300200200
signingConfig signingConfigs.debug
}
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index 7459e050..a0e43c2e 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -1,4 +1,4 @@
-`
+
diff --git a/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt b/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt
index 4f3509f6..c833f913 100644
--- a/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt
+++ b/app/src/main/java/ani/dantotsu/connections/anilist/api/Media.kt
@@ -253,7 +253,7 @@ data class MediaStreamingEpisode(
// The site location of the streaming episode
@SerialName("site") var site: String?,
-)
+) : java.io.Serializable
@Serializable
data class MediaCoverImage(
diff --git a/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt b/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt
index 20bafb39..eec99b96 100644
--- a/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt
+++ b/app/src/main/java/ani/dantotsu/connections/discord/RPC.kt
@@ -50,8 +50,9 @@ 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(
- 3,
+ return json.encodeToString(
+ Presence.Response(
+ 3,
Presence(
activities = listOf(
Activity(
diff --git a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
index 58e7de38..2ea82108 100644
--- a/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
+++ b/app/src/main/java/ani/dantotsu/download/manga/MangaDownloaderService.kt
@@ -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)
diff --git a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
index f8a3f725..4de053dd 100644
--- a/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
+++ b/app/src/main/java/ani/dantotsu/media/anime/ExoplayerView.kt
@@ -152,6 +152,7 @@ import ani.dantotsu.toPx
import ani.dantotsu.toast
import ani.dantotsu.tryWithSuspend
import ani.dantotsu.util.Logger
+import ani.dantotsu.util.customAlertDialog
import com.anggrayudi.storage.file.extension
import com.bumptech.glide.Glide
import com.google.android.gms.cast.framework.CastButtonFactory
@@ -181,8 +182,10 @@ import kotlin.math.roundToInt
@UnstableApi
@SuppressLint("ClickableViewAccessibility")
-class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityListener {
-
+class ExoplayerView :
+ AppCompatActivity(),
+ Player.Listener,
+ SessionAvailabilityListener {
private val resumeWindow = "resumeWindow"
private val resumePosition = "resumePosition"
private val playerFullscreen = "playerFullscreen"
@@ -283,10 +286,11 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
val displayCutout = window.decorView.rootWindowInsets.displayCutout
if (displayCutout != null) {
if (displayCutout.boundingRects.size > 0) {
- notchHeight = min(
- displayCutout.boundingRects[0].width(),
- displayCutout.boundingRects[0].height()
- )
+ notchHeight =
+ min(
+ displayCutout.boundingRects[0].width(),
+ displayCutout.boundingRects[0].height(),
+ )
checkNotch()
}
}
@@ -297,7 +301,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
private fun checkNotch() {
if (notchHeight != 0) {
val orientation = resources.configuration.orientation
- playerView.findViewById(R.id.exo_controller_margin)
+ playerView
+ .findViewById(R.id.exo_controller_margin)
.updateLayoutParams {
if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
marginStart = notchHeight
@@ -327,28 +332,30 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
val secondaryColor = PrefManager.getVal(PrefName.SecondaryColor)
- val outline = when (PrefManager.getVal(PrefName.Outline)) {
- 0 -> EDGE_TYPE_OUTLINE // Normal
- 1 -> EDGE_TYPE_DEPRESSED // Shine
- 2 -> EDGE_TYPE_DROP_SHADOW // Drop shadow
- 3 -> EDGE_TYPE_NONE // No outline
- else -> EDGE_TYPE_OUTLINE // Normal
- }
+ val outline =
+ when (PrefManager.getVal(PrefName.Outline)) {
+ 0 -> EDGE_TYPE_OUTLINE // Normal
+ 1 -> EDGE_TYPE_DEPRESSED // Shine
+ 2 -> EDGE_TYPE_DROP_SHADOW // Drop shadow
+ 3 -> EDGE_TYPE_NONE // No outline
+ else -> EDGE_TYPE_OUTLINE // Normal
+ }
val subBackground = PrefManager.getVal(PrefName.SubBackground)
val subWindow = PrefManager.getVal(PrefName.SubWindow)
- val font = when (PrefManager.getVal(PrefName.Font)) {
- 0 -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
- 1 -> ResourcesCompat.getFont(this, R.font.poppins_bold)
- 2 -> ResourcesCompat.getFont(this, R.font.poppins)
- 3 -> ResourcesCompat.getFont(this, R.font.poppins_thin)
- 4 -> ResourcesCompat.getFont(this, R.font.century_gothic_regular)
- 5 -> ResourcesCompat.getFont(this, R.font.levenim_mt_bold)
- 6 -> ResourcesCompat.getFont(this, R.font.blocky)
- else -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
- }
+ val font =
+ when (PrefManager.getVal(PrefName.Font)) {
+ 0 -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
+ 1 -> ResourcesCompat.getFont(this, R.font.poppins_bold)
+ 2 -> ResourcesCompat.getFont(this, R.font.poppins)
+ 3 -> ResourcesCompat.getFont(this, R.font.poppins_thin)
+ 4 -> ResourcesCompat.getFont(this, R.font.century_gothic_regular)
+ 5 -> ResourcesCompat.getFont(this, R.font.levenim_mt_bold)
+ 6 -> ResourcesCompat.getFont(this, R.font.blocky)
+ else -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
+ }
val fontSize = PrefManager.getVal(PrefName.FontSize).toFloat()
playerView.subtitleView?.let { subtitles ->
@@ -362,8 +369,8 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
subWindow,
outline,
secondaryColor,
- font
- )
+ font,
+ ),
)
subtitles.alpha =
@@ -387,16 +394,17 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
val fontSize = PrefManager.getVal(PrefName.FontSize).toFloat()
- val font = when (PrefManager.getVal(PrefName.Font)) {
- 0 -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
- 1 -> ResourcesCompat.getFont(this, R.font.poppins_bold)
- 2 -> ResourcesCompat.getFont(this, R.font.poppins)
- 3 -> ResourcesCompat.getFont(this, R.font.poppins_thin)
- 4 -> ResourcesCompat.getFont(this, R.font.century_gothic_regular)
- 5 -> ResourcesCompat.getFont(this, R.font.levenim_mt_bold)
- 6 -> ResourcesCompat.getFont(this, R.font.blocky)
- else -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
- }
+ val font =
+ when (PrefManager.getVal(PrefName.Font)) {
+ 0 -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
+ 1 -> ResourcesCompat.getFont(this, R.font.poppins_bold)
+ 2 -> ResourcesCompat.getFont(this, R.font.poppins)
+ 3 -> ResourcesCompat.getFont(this, R.font.poppins_thin)
+ 4 -> ResourcesCompat.getFont(this, R.font.century_gothic_regular)
+ 5 -> ResourcesCompat.getFont(this, R.font.levenim_mt_bold)
+ 6 -> ResourcesCompat.getFont(this, R.font.blocky)
+ else -> ResourcesCompat.getFont(this, R.font.poppins_semi_bold)
+ }
textView.setBackgroundColor(subBackground)
textView.setTextColor(primaryColor)
@@ -431,8 +439,9 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
binding = ActivityExoplayerBinding.inflate(layoutInflater)
setContentView(binding.root)
- //Initialize
- isCastApiAvailable = GoogleApiAvailability.getInstance()
+ // Initialize
+ isCastApiAvailable = GoogleApiAvailability
+ .getInstance()
.isGooglePlayServicesAvailable(this) == ConnectionResult.SUCCESS
try {
castContext =
@@ -531,27 +540,34 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
isPlayerPlaying = savedInstanceState.getBoolean(playerOnPlay)
}
- //BackButton
+ // BackButton
playerView.findViewById(R.id.exo_back).setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
- //TimeStamps
+ // TimeStamps
model.timeStamps.observe(this) { it ->
isTimeStampsLoaded = true
- exoSkipOpEd.visibility = if (it != null) {
- val adGroups = it.flatMap {
- listOf(
- it.interval.startTime.toLong() * 1000,
- it.interval.endTime.toLong() * 1000
- )
- }.toLongArray()
- val playedAdGroups = it.flatMap {
- listOf(false, false)
- }.toBooleanArray()
- playerView.setExtraAdGroupMarkers(adGroups, playedAdGroups)
- View.VISIBLE
- } else View.GONE
+ exoSkipOpEd.visibility =
+ if (it != null) {
+ val adGroups =
+ it
+ .flatMap {
+ listOf(
+ it.interval.startTime.toLong() * 1000,
+ it.interval.endTime.toLong() * 1000,
+ )
+ }.toLongArray()
+ val playedAdGroups =
+ it
+ .flatMap {
+ listOf(false, false)
+ }.toBooleanArray()
+ playerView.setExtraAdGroupMarkers(adGroups, playedAdGroups)
+ View.VISIBLE
+ } else {
+ View.GONE
+ }
}
exoSkipOpEd.alpha = if (PrefManager.getVal(PrefName.AutoSkipOPED)) 1f else 0.3f
@@ -566,7 +582,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
exoSkipOpEd.alpha = if (PrefManager.getVal(PrefName.AutoSkipOPED)) 1f else 0.3f
}
- //Play Pause
+ // Play Pause
exoPlay.setOnClickListener {
if (isInitialized) {
isPlayerPlaying = exoPlayer.isPlaying
@@ -590,19 +606,21 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
// Picture-in-picture
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
pipEnabled =
- packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) && PrefManager.getVal(
- PrefName.Pip
- )
+ packageManager.hasSystemFeature(PackageManager.FEATURE_PICTURE_IN_PICTURE) &&
+ PrefManager.getVal(
+ PrefName.Pip,
+ )
if (pipEnabled) {
exoPip.visibility = View.VISIBLE
exoPip.setOnClickListener {
enterPipMode()
}
- } else exoPip.visibility = View.GONE
+ } else {
+ exoPip.visibility = View.GONE
+ }
}
-
- //Lock Button
+ // Lock Button
var locked = false
val container = playerView.findViewById(R.id.exo_controller_cont)
val screen = playerView.findViewById(R.id.exo_black_screen)
@@ -624,13 +642,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
timeline.setForceDisabled(false)
}
- //Skip Time Button
+ // Skip Time Button
var skipTime = PrefManager.getVal(PrefName.SkipTime)
if (skipTime > 0) {
exoSkip.findViewById(R.id.exo_skip_time).text = skipTime.toString()
exoSkip.setOnClickListener {
- if (isInitialized)
+ if (isInitialized) {
exoPlayer.seekTo(exoPlayer.currentPosition + skipTime * 1000)
+ }
}
exoSkip.setOnLongClickListener {
val dialog = Dialog(this, R.style.MyPopup)
@@ -639,7 +658,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
dialog.setCanceledOnTouchOutside(true)
dialog.window?.setLayout(
ViewGroup.LayoutParams.WRAP_CONTENT,
- ViewGroup.LayoutParams.WRAP_CONTENT
+ ViewGroup.LayoutParams.WRAP_CONTENT,
)
if (skipTime <= 120) {
dialog.findViewById(R.id.seekbar).value = skipTime.toFloat()
@@ -648,20 +667,24 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
dialog.findViewById(R.id.seekbar).addOnChangeListener { _, value, _ ->
skipTime = value.toInt()
- //saveData(player, settings)
+ // saveData(player, settings)
PrefManager.setVal(PrefName.SkipTime, skipTime)
playerView.findViewById(R.id.exo_skip_time).text =
skipTime.toString()
dialog.findViewById(R.id.seekbar_value).text =
skipTime.toString()
}
- dialog.findViewById(R.id.seekbar)
- .addOnSliderTouchListener(object : Slider.OnSliderTouchListener {
- override fun onStartTrackingTouch(slider: Slider) {}
- override fun onStopTrackingTouch(slider: Slider) {
- dialog.dismiss()
- }
- })
+ dialog
+ .findViewById(R.id.seekbar)
+ .addOnSliderTouchListener(
+ object : Slider.OnSliderTouchListener {
+ override fun onStartTrackingTouch(slider: Slider) {}
+
+ override fun onStopTrackingTouch(slider: Slider) {
+ dialog.dismiss()
+ }
+ },
+ )
dialog.findViewById(R.id.seekbar_title).text =
getString(R.string.skip_time)
dialog.findViewById(R.id.seekbar_value).text =
@@ -676,99 +699,136 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
val gestureSpeed = (300 * PrefManager.getVal(PrefName.AnimationSpeed)).toLong()
- //Player UI Visibility Handler
- val brightnessRunnable = Runnable {
- if (exoBrightnessCont.alpha == 1f)
- lifecycleScope.launch {
- ObjectAnimator.ofFloat(exoBrightnessCont, "alpha", 1f, 0f)
- .setDuration(gestureSpeed).start()
- delay(gestureSpeed)
- exoBrightnessCont.visibility = View.GONE
- checkNotch()
+ // Player UI Visibility Handler
+ val brightnessRunnable =
+ Runnable {
+ if (exoBrightnessCont.alpha == 1f) {
+ lifecycleScope.launch {
+ ObjectAnimator
+ .ofFloat(exoBrightnessCont, "alpha", 1f, 0f)
+ .setDuration(gestureSpeed)
+ .start()
+ delay(gestureSpeed)
+ exoBrightnessCont.visibility = View.GONE
+ checkNotch()
+ }
}
- }
- val volumeRunnable = Runnable {
- if (exoVolumeCont.alpha == 1f)
- lifecycleScope.launch {
- ObjectAnimator.ofFloat(exoVolumeCont, "alpha", 1f, 0f).setDuration(gestureSpeed)
- .start()
- delay(gestureSpeed)
- exoVolumeCont.visibility = View.GONE
- checkNotch()
- }
- }
- playerView.setControllerVisibilityListener(PlayerView.ControllerVisibilityListener { visibility ->
- if (visibility == View.GONE) {
- hideSystemBars()
- brightnessRunnable.run()
- volumeRunnable.run()
}
- })
+ val volumeRunnable =
+ Runnable {
+ if (exoVolumeCont.alpha == 1f) {
+ lifecycleScope.launch {
+ ObjectAnimator
+ .ofFloat(exoVolumeCont, "alpha", 1f, 0f)
+ .setDuration(gestureSpeed)
+ .start()
+ delay(gestureSpeed)
+ exoVolumeCont.visibility = View.GONE
+ checkNotch()
+ }
+ }
+ }
+ playerView.setControllerVisibilityListener(
+ PlayerView.ControllerVisibilityListener { visibility ->
+ if (visibility == View.GONE) {
+ hideSystemBars()
+ brightnessRunnable.run()
+ volumeRunnable.run()
+ }
+ },
+ )
val overshoot = AnimationUtils.loadInterpolator(this, R.anim.over_shoot)
val controllerDuration = (300 * PrefManager.getVal(PrefName.AnimationSpeed)).toLong()
+
fun handleController() {
if (if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) !isInPictureInPictureMode else true) {
if (playerView.isControllerFullyVisible) {
- ObjectAnimator.ofFloat(
- playerView.findViewById(R.id.exo_controller),
- "alpha",
- 1f,
- 0f
- )
- .setDuration(controllerDuration).start()
- ObjectAnimator.ofFloat(
- playerView.findViewById(R.id.exo_bottom_cont),
- "translationY",
- 0f,
- 128f
- )
- .apply { interpolator = overshoot;duration = controllerDuration;start() }
- ObjectAnimator.ofFloat(
- playerView.findViewById(R.id.exo_timeline_cont),
- "translationY",
- 0f,
- 128f
- )
- .apply { interpolator = overshoot;duration = controllerDuration;start() }
- ObjectAnimator.ofFloat(
- playerView.findViewById(R.id.exo_top_cont),
- "translationY",
- 0f,
- -128f
- )
- .apply { interpolator = overshoot;duration = controllerDuration;start() }
+ ObjectAnimator
+ .ofFloat(
+ playerView.findViewById(R.id.exo_controller),
+ "alpha",
+ 1f,
+ 0f,
+ ).setDuration(controllerDuration)
+ .start()
+ ObjectAnimator
+ .ofFloat(
+ playerView.findViewById(R.id.exo_bottom_cont),
+ "translationY",
+ 0f,
+ 128f,
+ ).apply {
+ interpolator = overshoot
+ duration = controllerDuration
+ start()
+ }
+ ObjectAnimator
+ .ofFloat(
+ playerView.findViewById(R.id.exo_timeline_cont),
+ "translationY",
+ 0f,
+ 128f,
+ ).apply {
+ interpolator = overshoot
+ duration = controllerDuration
+ start()
+ }
+ ObjectAnimator
+ .ofFloat(
+ playerView.findViewById(R.id.exo_top_cont),
+ "translationY",
+ 0f,
+ -128f,
+ ).apply {
+ interpolator = overshoot
+ duration = controllerDuration
+ start()
+ }
playerView.postDelayed({ playerView.hideController() }, controllerDuration)
} else {
checkNotch()
playerView.showController()
- ObjectAnimator.ofFloat(
- playerView.findViewById(R.id.exo_controller),
- "alpha",
- 0f,
- 1f
- )
- .setDuration(controllerDuration).start()
- ObjectAnimator.ofFloat(
- playerView.findViewById(R.id.exo_bottom_cont),
- "translationY",
- 128f,
- 0f
- )
- .apply { interpolator = overshoot;duration = controllerDuration;start() }
- ObjectAnimator.ofFloat(
- playerView.findViewById(R.id.exo_timeline_cont),
- "translationY",
- 128f,
- 0f
- )
- .apply { interpolator = overshoot;duration = controllerDuration;start() }
- ObjectAnimator.ofFloat(
- playerView.findViewById(R.id.exo_top_cont),
- "translationY",
- -128f,
- 0f
- )
- .apply { interpolator = overshoot;duration = controllerDuration;start() }
+ ObjectAnimator
+ .ofFloat(
+ playerView.findViewById(R.id.exo_controller),
+ "alpha",
+ 0f,
+ 1f,
+ ).setDuration(controllerDuration)
+ .start()
+ ObjectAnimator
+ .ofFloat(
+ playerView.findViewById(R.id.exo_bottom_cont),
+ "translationY",
+ 128f,
+ 0f,
+ ).apply {
+ interpolator = overshoot
+ duration = controllerDuration
+ start()
+ }
+ ObjectAnimator
+ .ofFloat(
+ playerView.findViewById(R.id.exo_timeline_cont),
+ "translationY",
+ 128f,
+ 0f,
+ ).apply {
+ interpolator = overshoot
+ duration = controllerDuration
+ start()
+ }
+ ObjectAnimator
+ .ofFloat(
+ playerView.findViewById(R.id.exo_top_cont),
+ "translationY",
+ -128f,
+ 0f,
+ ).apply {
+ interpolator = overshoot
+ duration = controllerDuration
+ start()
+ }
}
}
}
@@ -782,26 +842,29 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
val fastForwardCard = playerView.findViewById(R.id.exo_fast_forward)
val fastRewindCard = playerView.findViewById(R.id.exo_fast_rewind)
-
- //Seeking
+ // Seeking
val seekTimerF = ResettableTimer()
val seekTimerR = ResettableTimer()
var seekTimesF = 0
var seekTimesR = 0
- fun seek(forward: Boolean, event: MotionEvent? = null) {
+ fun seek(
+ forward: Boolean,
+ event: MotionEvent? = null,
+ ) {
val seekTime = PrefManager.getVal(PrefName.SeekTime)
- val (card, text) = if (forward) {
- val text = "+${seekTime * ++seekTimesF}"
- forwardText.text = text
- handler.post { exoPlayer.seekTo(exoPlayer.currentPosition + seekTime * 1000) }
- fastForwardCard to forwardText
- } else {
- val text = "-${seekTime * ++seekTimesR}"
- rewindText.text = text
- handler.post { exoPlayer.seekTo(exoPlayer.currentPosition - seekTime * 1000) }
- fastRewindCard to rewindText
- }
+ val (card, text) =
+ if (forward) {
+ val text = "+${seekTime * ++seekTimesF}"
+ forwardText.text = text
+ handler.post { exoPlayer.seekTo(exoPlayer.currentPosition + seekTime * 1000) }
+ fastForwardCard to forwardText
+ } else {
+ val text = "-${seekTime * ++seekTimesR}"
+ rewindText.text = text
+ handler.post { exoPlayer.seekTo(exoPlayer.currentPosition - seekTime * 1000) }
+ fastRewindCard to rewindText
+ }
//region Double Tap Animation
val showCardAnim = ObjectAnimator.ofFloat(card, "alpha", 0f, 1f).setDuration(300)
@@ -836,21 +899,27 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
isSeeking = true
if (forward) {
- seekTimerR.reset(object : TimerTask() {
- override fun run() {
- isSeeking = false
- stopAnim()
- seekTimesF = 0
- }
- }, 850)
+ seekTimerR.reset(
+ object : TimerTask() {
+ override fun run() {
+ isSeeking = false
+ stopAnim()
+ seekTimesF = 0
+ }
+ },
+ 850,
+ )
} else {
- seekTimerF.reset(object : TimerTask() {
- override fun run() {
- isSeeking = false
- stopAnim()
- seekTimesR = 0
- }
- }, 850)
+ seekTimerF.reset(
+ object : TimerTask() {
+ override fun run() {
+ isSeeking = false
+ stopAnim()
+ seekTimesR = 0
+ }
+ },
+ 850,
+ )
}
}
@@ -874,27 +943,30 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
keyMap[KEYCODE_DPAD_RIGHT] = { seek(true) }
keyMap[KEYCODE_DPAD_LEFT] = { seek(false) }
- //Screen Gestures
+ // Screen Gestures
if (PrefManager.getVal(PrefName.Gestures) || PrefManager.getVal(PrefName.DoubleTap)) {
-
- fun doubleTap(forward: Boolean, event: MotionEvent) {
+ fun doubleTap(
+ forward: Boolean,
+ event: MotionEvent,
+ ) {
if (!locked && isInitialized && PrefManager.getVal(PrefName.DoubleTap)) {
seek(forward, event)
}
}
- //Brightness
+ // Brightness
var brightnessTimer = Timer()
exoBrightnessCont.visibility = View.GONE
fun brightnessHide() {
brightnessTimer.cancel()
brightnessTimer.purge()
- val timerTask: TimerTask = object : TimerTask() {
- override fun run() {
- handler.post(brightnessRunnable)
+ val timerTask: TimerTask =
+ object : TimerTask() {
+ override fun run() {
+ handler.post(brightnessRunnable)
+ }
}
- }
brightnessTimer = Timer()
brightnessTimer.schedule(timerTask, 3000)
}
@@ -908,20 +980,22 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
brightnessHide()
}
- //Volume
+ // Volume
var volumeTimer = Timer()
exoVolumeCont.visibility = View.GONE
val volumeMax = audioManager.getStreamMaxVolume(STREAM_MUSIC)
exoVolume.value = audioManager.getStreamVolume(STREAM_MUSIC).toFloat() / volumeMax * 10
+
fun volumeHide() {
volumeTimer.cancel()
volumeTimer.purge()
- val timerTask: TimerTask = object : TimerTask() {
- override fun run() {
- handler.post(volumeRunnable)
+ val timerTask: TimerTask =
+ object : TimerTask() {
+ override fun run() {
+ handler.post(volumeRunnable)
+ }
}
- }
volumeTimer = Timer()
volumeTimer.schedule(timerTask, 3000)
}
@@ -931,6 +1005,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
volumeHide()
}
val fastForward = playerView.findViewById(R.id.exo_fast_forward_text)
+
fun fastForward() {
isFastForwarding = true
exoPlayer.setPlaybackSpeed(exoPlayer.playbackParameters.speed * 2)
@@ -947,29 +1022,33 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
- //FastRewind (Left Panel)
- val fastRewindDetector = GestureDetector(this, object : GesturesListener() {
- override fun onLongClick(event: MotionEvent) {
- if (PrefManager.getVal(PrefName.FastForward)) fastForward()
- }
-
- override fun onDoubleClick(event: MotionEvent) {
- doubleTap(false, event)
- }
-
- override fun onScrollYClick(y: Float) {
- if (!locked && PrefManager.getVal(PrefName.Gestures)) {
- exoBrightness.value = clamp(exoBrightness.value + y / 100, 0f, 10f)
- if (exoBrightnessCont.visibility != View.VISIBLE) {
- exoBrightnessCont.visibility = View.VISIBLE
+ // FastRewind (Left Panel)
+ val fastRewindDetector =
+ GestureDetector(
+ this,
+ object : GesturesListener() {
+ override fun onLongClick(event: MotionEvent) {
+ if (PrefManager.getVal(PrefName.FastForward)) fastForward()
}
- exoBrightnessCont.alpha = 1f
- }
- }
- override fun onSingleClick(event: MotionEvent) =
- if (isSeeking) doubleTap(false, event) else handleController()
- })
+ override fun onDoubleClick(event: MotionEvent) {
+ doubleTap(false, event)
+ }
+
+ override fun onScrollYClick(y: Float) {
+ if (!locked && PrefManager.getVal(PrefName.Gestures)) {
+ exoBrightness.value = clamp(exoBrightness.value + y / 100, 0f, 10f)
+ if (exoBrightnessCont.visibility != View.VISIBLE) {
+ exoBrightnessCont.visibility = View.VISIBLE
+ }
+ exoBrightnessCont.alpha = 1f
+ }
+ }
+
+ override fun onSingleClick(event: MotionEvent) =
+ if (isSeeking) doubleTap(false, event) else handleController()
+ },
+ )
val rewindArea = playerView.findViewById(R.id.exo_rewind_area)
rewindArea.isClickable = true
rewindArea.setOnTouchListener { v, event ->
@@ -979,29 +1058,33 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
true
}
- //FastForward (Right Panel)
- val fastForwardDetector = GestureDetector(this, object : GesturesListener() {
- override fun onLongClick(event: MotionEvent) {
- if (PrefManager.getVal(PrefName.FastForward)) fastForward()
- }
-
- override fun onDoubleClick(event: MotionEvent) {
- doubleTap(true, event)
- }
-
- override fun onScrollYClick(y: Float) {
- if (!locked && PrefManager.getVal(PrefName.Gestures)) {
- exoVolume.value = clamp(exoVolume.value + y / 100, 0f, 10f)
- if (exoVolumeCont.visibility != View.VISIBLE) {
- exoVolumeCont.visibility = View.VISIBLE
+ // FastForward (Right Panel)
+ val fastForwardDetector =
+ GestureDetector(
+ this,
+ object : GesturesListener() {
+ override fun onLongClick(event: MotionEvent) {
+ if (PrefManager.getVal(PrefName.FastForward)) fastForward()
}
- exoVolumeCont.alpha = 1f
- }
- }
- override fun onSingleClick(event: MotionEvent) =
- if (isSeeking) doubleTap(true, event) else handleController()
- })
+ override fun onDoubleClick(event: MotionEvent) {
+ doubleTap(true, event)
+ }
+
+ override fun onScrollYClick(y: Float) {
+ if (!locked && PrefManager.getVal(PrefName.Gestures)) {
+ exoVolume.value = clamp(exoVolume.value + y / 100, 0f, 10f)
+ if (exoVolumeCont.visibility != View.VISIBLE) {
+ exoVolumeCont.visibility = View.VISIBLE
+ }
+ exoVolumeCont.alpha = 1f
+ }
+ }
+
+ override fun onSingleClick(event: MotionEvent) =
+ if (isSeeking) doubleTap(true, event) else handleController()
+ },
+ )
val forwardArea = playerView.findViewById(R.id.exo_forward_area)
forwardArea.isClickable = true
forwardArea.setOnTouchListener { v, event ->
@@ -1012,7 +1095,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
- //Handle Media
+ // Handle Media
if (!initialized) return startMainActivity(this)
model.setMedia(media)
title = media.userPreferredName
@@ -1026,8 +1109,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
epChanging = !it
}
-
- //Anime Title
+ // Anime Title
animeTitle.text = media.userPreferredName
episodeArr = episodes.keys.toList()
@@ -1037,16 +1119,18 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
episodes.forEach {
val episode = it.value
val cleanedTitle = MediaNameAdapter.removeEpisodeNumberCompletely(episode.title ?: "")
- episodeTitleArr.add("Episode ${episode.number}${if (episode.filler) " [Filler]" else ""}${if (cleanedTitle.isNotBlank() && cleanedTitle != "null") ": $cleanedTitle" else ""}")
+ episodeTitleArr.add(
+ "Episode ${episode.number}${if (episode.filler) " [Filler]" else ""}${if (cleanedTitle.isNotBlank() && cleanedTitle != "null") ": $cleanedTitle" else ""}",
+ )
}
- //Episode Change
+ // Episode Change
fun change(index: Int) {
if (isInitialized) {
changingServer = false
PrefManager.setCustomVal(
"${media.id}_${episodeArr[currentEpisodeIndex]}",
- exoPlayer.currentPosition
+ exoPlayer.currentPosition,
)
exoPlayer.seekTo(0)
val prev = episodeArr[currentEpisodeIndex]
@@ -1057,29 +1141,37 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
model.epChanged.postValue(false)
model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "change")
model.onEpisodeClick(
- media, media.anime!!.selectedEpisode!!, this.supportFragmentManager,
+ media,
+ media.anime!!.selectedEpisode!!,
+ this.supportFragmentManager,
false,
- prev
+ prev,
)
}
}
- //EpisodeSelector
+ // EpisodeSelector
episodeTitle.adapter = NoPaddingArrayAdapter(this, R.layout.item_dropdown, episodeTitleArr)
episodeTitle.setSelection(currentEpisodeIndex)
- episodeTitle.onItemSelectedListener = object : AdapterView.OnItemSelectedListener {
- override fun onItemSelected(p0: AdapterView<*>?, p1: View?, position: Int, p3: Long) {
- if (position != currentEpisodeIndex) {
- disappeared = false
- functionstarted = false
- change(position)
+ episodeTitle.onItemSelectedListener =
+ object : AdapterView.OnItemSelectedListener {
+ override fun onItemSelected(
+ p0: AdapterView<*>?,
+ p1: View?,
+ position: Int,
+ p3: Long,
+ ) {
+ if (position != currentEpisodeIndex) {
+ disappeared = false
+ functionstarted = false
+ change(position)
+ }
}
+
+ override fun onNothingSelected(parent: AdapterView<*>) {}
}
- override fun onNothingSelected(parent: AdapterView<*>) {}
- }
-
- //Next Episode
+ // Next Episode
exoNext = playerView.findViewById(R.id.exo_next_ep)
exoNext.setOnClickListener {
if (isInitialized) {
@@ -1091,14 +1183,15 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
}
- //Prev Episode
+ // Prev Episode
exoPrev = playerView.findViewById(R.id.exo_prev_ep)
exoPrev.setOnClickListener {
if (currentEpisodeIndex > 0) {
disappeared = false
change(currentEpisodeIndex - 1)
- } else
+ } else {
snackString(getString(R.string.first_episode))
+ }
}
model.getEpisode().observe(this) { ep ->
@@ -1110,45 +1203,48 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
currentEpisodeIndex = episodeArr.indexOf(ep.number)
episodeTitle.setSelection(currentEpisodeIndex)
if (isInitialized) releasePlayer()
- playbackPosition = PrefManager.getCustomVal(
- "${media.id}_${ep.number}",
- 0
- )
+ playbackPosition =
+ PrefManager.getCustomVal(
+ "${media.id}_${ep.number}",
+ 0,
+ )
initPlayer()
preloading = false
updateProgress()
}
}
- //FullScreen
+ // FullScreen
isFullscreen = PrefManager.getCustomVal("${media.id}_fullscreenInt", isFullscreen)
- playerView.resizeMode = when (isFullscreen) {
- 0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
- 1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
- 2 -> AspectRatioFrameLayout.RESIZE_MODE_FILL
- else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
- }
-
- exoScreen.setOnClickListener {
- if (isFullscreen < 2) isFullscreen += 1 else isFullscreen = 0
- playerView.resizeMode = when (isFullscreen) {
+ playerView.resizeMode =
+ when (isFullscreen) {
0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
2 -> AspectRatioFrameLayout.RESIZE_MODE_FILL
else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
}
+
+ exoScreen.setOnClickListener {
+ if (isFullscreen < 2) isFullscreen += 1 else isFullscreen = 0
+ playerView.resizeMode =
+ when (isFullscreen) {
+ 0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
+ 1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
+ 2 -> AspectRatioFrameLayout.RESIZE_MODE_FILL
+ else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
+ }
snackString(
when (isFullscreen) {
0 -> "Original"
1 -> "Zoom"
2 -> "Stretch"
else -> "Original"
- }
+ },
)
PrefManager.setCustomVal("${media.id}_fullscreenInt", isFullscreen)
}
- //Cast
+ // Cast
if (PrefManager.getVal(PrefName.Cast)) {
playerView.findViewById(R.id.exo_cast).apply {
visibility = View.VISIBLE
@@ -1165,24 +1261,25 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
- //Settings
+ // Settings
exoSettings.setOnClickListener {
PrefManager.setCustomVal(
"${media.id}_${media.anime!!.selectedEpisode}",
- exoPlayer.currentPosition
+ exoPlayer.currentPosition,
)
- val intent = Intent(this, PlayerSettingsActivity::class.java).apply {
- putExtra("subtitle", subtitle)
- }
+ val intent =
+ Intent(this, PlayerSettingsActivity::class.java).apply {
+ putExtra("subtitle", subtitle)
+ }
exoPlayer.pause()
onChangeSettings.launch(intent)
}
- //Speed
+ // Speed
val speeds =
- if (PrefManager.getVal(PrefName.CursedSpeeds))
+ if (PrefManager.getVal(PrefName.CursedSpeeds)) {
arrayOf(1f, 1.25f, 1.5f, 1.75f, 2f, 2.5f, 3f, 4f, 5f, 10f, 25f, 50f)
- else
+ } else {
arrayOf(
0.25f,
0.33f,
@@ -1196,38 +1293,39 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
1.5f,
1.66f,
1.75f,
- 2f
+ 2f,
)
+ }
val speedsName = speeds.map { "${it}x" }.toTypedArray()
- //var curSpeed = loadData("${media.id}_speed", this) ?: settings.defaultSpeed
- var curSpeed = PrefManager.getCustomVal(
- "${media.id}_speed",
- PrefManager.getVal(PrefName.DefaultSpeed)
- )
+ // var curSpeed = loadData("${media.id}_speed", this) ?: settings.defaultSpeed
+ var curSpeed =
+ PrefManager.getCustomVal(
+ "${media.id}_speed",
+ PrefManager.getVal(PrefName.DefaultSpeed),
+ )
playbackParameters = PlaybackParameters(speeds[curSpeed])
var speed: Float
- val speedDialog =
- AlertDialog.Builder(this, R.style.MyPopup).setTitle(getString(R.string.speed))
exoSpeed.setOnClickListener {
- val dialog = speedDialog.setSingleChoiceItems(speedsName, curSpeed) { dialog, i ->
- if (isInitialized) {
+ customAlertDialog().apply {
+ setTitle(R.string.speed)
+ singleChoiceItems(speedsName, curSpeed) { i ->
PrefManager.setCustomVal("${media.id}_speed", i)
speed = speeds[i]
curSpeed = i
playbackParameters = PlaybackParameters(speed)
exoPlayer.playbackParameters = playbackParameters
- dialog.dismiss()
hideSystemBars()
}
- }.show()
- dialog.window?.setDimAmount(0.8f)
+ setOnCancelListener { hideSystemBars() }
+ show()
+ }
}
- speedDialog.setOnCancelListener { hideSystemBars() }
if (PrefManager.getVal(PrefName.AutoPlay)) {
var touchTimer = Timer()
+
fun touched() {
interacted = true
touchTimer.apply {
@@ -1235,11 +1333,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
purge()
}
touchTimer = Timer()
- touchTimer.schedule(object : TimerTask() {
- override fun run() {
- interacted = false
- }
- }, 1000 * 60 * 60)
+ touchTimer.schedule(
+ object : TimerTask() {
+ override fun run() {
+ interacted = false
+ }
+ },
+ 1000 * 60 * 60,
+ )
}
playerView.findViewById(R.id.exo_touch_view).setOnTouchListener { _, _ ->
touched()
@@ -1248,62 +1349,73 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
isFullscreen = PrefManager.getVal(PrefName.Resize)
- playerView.resizeMode = when (isFullscreen) {
- 0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
- 1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
- 2 -> AspectRatioFrameLayout.RESIZE_MODE_FILL
- else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
- }
+ playerView.resizeMode =
+ when (isFullscreen) {
+ 0 -> AspectRatioFrameLayout.RESIZE_MODE_FIT
+ 1 -> AspectRatioFrameLayout.RESIZE_MODE_ZOOM
+ 2 -> AspectRatioFrameLayout.RESIZE_MODE_FILL
+ else -> AspectRatioFrameLayout.RESIZE_MODE_FIT
+ }
preloading = false
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
val showProgressDialog =
- if (PrefManager.getVal(PrefName.AskIndividualPlayer)) PrefManager.getCustomVal(
- "${media.id}_ProgressDialog",
+ if (PrefManager.getVal(PrefName.AskIndividualPlayer)) {
+ PrefManager.getCustomVal(
+ "${media.id}_ProgressDialog",
+ true,
+ )
+ } else {
+ false
+ }
+ if (!incognito &&
+ showProgressDialog &&
+ Anilist.userid != null &&
+ if (media.isAdult) {
+ PrefManager.getVal(
+ PrefName.UpdateForHPlayer,
+ )
+ } else {
true
- ) else false
- if (!incognito && showProgressDialog && Anilist.userid != null && if (media.isAdult) PrefManager.getVal(
- PrefName.UpdateForHPlayer
- ) else true
+ }
) {
- AlertDialog.Builder(this, R.style.MyPopup)
- .setTitle(getString(R.string.auto_update, media.userPreferredName))
- .apply {
- setOnCancelListener { hideSystemBars() }
- setCancelable(false)
- setPositiveButton(getString(R.string.yes)) { dialog, _ ->
- PrefManager.setCustomVal(
- "${media.id}_ProgressDialog",
- false
- )
- PrefManager.setCustomVal(
- "${media.id}_save_progress",
- true
- )
- dialog.dismiss()
- model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
- }
- setNegativeButton(getString(R.string.no)) { dialog, _ ->
- PrefManager.setCustomVal(
- "${media.id}_ProgressDialog",
- false
- )
- PrefManager.setCustomVal(
- "${media.id}_save_progress",
- false
- )
- toast(getString(R.string.reset_auto_update))
- dialog.dismiss()
- model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
- }
- show()
+ customAlertDialog().apply {
+ setTitle(getString(R.string.auto_update, media.userPreferredName))
+ setCancelable(false)
+ setPosButton(R.string.yes) {
+ PrefManager.setCustomVal(
+ "${media.id}_ProgressDialog",
+ false,
+ )
+ PrefManager.setCustomVal(
+ "${media.id}_save_progress",
+ true,
+ )
+ model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
}
- } else model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
+ setNegButton(R.string.no) {
+ PrefManager.setCustomVal(
+ "${media.id}_ProgressDialog",
+ false,
+ )
+ PrefManager.setCustomVal(
+ "${media.id}_save_progress",
+ false,
+ )
+ toast(getString(R.string.reset_auto_update))
+ model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
+ }
+ setOnCancelListener { hideSystemBars() }
+ show()
+ }
+ } else {
+ model.setEpisode(episodes[media.anime!!.selectedEpisode!!]!!, "invoke")
+ }
- //Start the recursive Fun
- if (PrefManager.getVal(PrefName.TimeStampsEnabled))
+ // Start the recursive Fun
+ if (PrefManager.getVal(PrefName.TimeStampsEnabled)) {
updateTimeStamp()
-
+ }
}
private fun discordRPC() {
@@ -1315,60 +1427,68 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
if ((isOnline(context) && !offline) && Discord.token != null && !incognito && rpcenabled) {
lifecycleScope.launch {
val discordMode = PrefManager.getCustomVal("discord_mode", "dantotsu")
- val buttons = when (discordMode) {
- "nothing" -> mutableListOf(
- RPC.Link(getString(R.string.view_anime), media.shareLink ?: ""),
- )
+ val buttons =
+ when (discordMode) {
+ "nothing" ->
+ mutableListOf(
+ RPC.Link(getString(R.string.view_anime), media.shareLink ?: ""),
+ )
- "dantotsu" -> mutableListOf(
- RPC.Link(getString(R.string.view_anime), media.shareLink ?: ""),
- RPC.Link("Watch on Dantotsu", getString(R.string.dantotsu))
- )
+ "dantotsu" ->
+ mutableListOf(
+ RPC.Link(getString(R.string.view_anime), media.shareLink ?: ""),
+ RPC.Link("Watch on Dantotsu", getString(R.string.dantotsu)),
+ )
- "anilist" -> {
- val userId = PrefManager.getVal(PrefName.AnilistUserId)
- val anilistLink = "https://anilist.co/user/$userId/"
- mutableListOf(
- RPC.Link(getString(R.string.view_anime), media.shareLink ?: ""),
- RPC.Link("View My AniList", anilistLink)
- )
+ "anilist" -> {
+ val userId = PrefManager.getVal(PrefName.AnilistUserId)
+ val anilistLink = "https://anilist.co/user/$userId/"
+ mutableListOf(
+ RPC.Link(getString(R.string.view_anime), media.shareLink ?: ""),
+ RPC.Link("View My AniList", anilistLink),
+ )
+ }
+
+ else -> mutableListOf()
}
-
- else -> mutableListOf()
- }
val startTimestamp = Calendar.getInstance()
val durationInSeconds =
if (exoPlayer.duration != C.TIME_UNSET) (exoPlayer.duration / 1000).toInt() else 1440
- val endTimestamp = Calendar.getInstance().apply {
- timeInMillis = startTimestamp.timeInMillis
- add(Calendar.SECOND, durationInSeconds)
- }
- val presence = RPC.createPresence(
- RPC.Companion.RPCData(
- applicationId = Discord.application_Id,
- type = RPC.Type.WATCHING,
- activityName = media.userPreferredName,
- details = ep.title?.takeIf { it.isNotEmpty() } ?: getString(
- R.string.episode_num,
- ep.number
+ val endTimestamp =
+ Calendar.getInstance().apply {
+ timeInMillis = startTimestamp.timeInMillis
+ add(Calendar.SECOND, durationInSeconds)
+ }
+ val presence =
+ RPC.createPresence(
+ RPC.Companion.RPCData(
+ applicationId = Discord.application_Id,
+ type = RPC.Type.WATCHING,
+ activityName = media.userPreferredName,
+ details =
+ ep.title?.takeIf { it.isNotEmpty() } ?: getString(
+ R.string.episode_num,
+ ep.number,
+ ),
+ startTimestamp = startTimestamp.timeInMillis,
+ stopTimestamp = endTimestamp.timeInMillis,
+ state = "Episode : ${ep.number}/${media.anime?.totalEpisodes ?: "??"}",
+ largeImage =
+ media.cover?.let {
+ RPC.Link(
+ media.userPreferredName,
+ it,
+ )
+ },
+ smallImage = RPC.Link("Dantotsu", Discord.small_Image),
+ buttons = buttons,
),
- startTimestamp = startTimestamp.timeInMillis,
- stopTimestamp = endTimestamp.timeInMillis,
- state = "Episode : ${ep.number}/${media.anime?.totalEpisodes ?: "??"}",
- largeImage = media.cover?.let {
- RPC.Link(
- media.userPreferredName,
- it
- )
- },
- smallImage = RPC.Link("Dantotsu", Discord.small_Image),
- buttons = buttons
)
- )
- val intent = Intent(context, DiscordService::class.java).apply {
- putExtra("presence", presence)
- }
+ val intent =
+ Intent(context, DiscordService::class.java).apply {
+ putExtra("presence", presence)
+ }
DiscordServiceRunningSingleton.running = true
startService(intent)
}
@@ -1380,15 +1500,18 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
PrefManager.setCustomVal(
"${media.id}_current_ep",
- media.anime!!.selectedEpisode!!
+ media.anime!!.selectedEpisode!!,
)
@Suppress("UNCHECKED_CAST")
- val list = (PrefManager.getNullableCustomVal(
- "continueAnimeList",
- listOf(),
- List::class.java
- ) as List).toMutableList()
+ val list =
+ (
+ PrefManager.getNullableCustomVal(
+ "continueAnimeList",
+ listOf(),
+ List::class.java,
+ ) as List
+ ).toMutableList()
if (list.contains(media.id)) list.remove(media.id)
list.add(media.id)
PrefManager.setCustomVal("continueAnimeList", list)
@@ -1400,62 +1523,71 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
val ext = episode.extractors?.find { it.server.name == episode.selectedExtractor } ?: return
extractor = ext
video = ext.videos.getOrNull(episode.selectedVideo) ?: return
- val subLanguages = arrayOf(
- "Albanian",
- "Arabic",
- "Bosnian",
- "Bulgarian",
- "Chinese",
- "Croatian",
- "Czech",
- "Danish",
- "Dutch",
- "English",
- "Estonian",
- "Finnish",
- "French",
- "Georgian",
- "German",
- "Greek",
- "Hebrew",
- "Hindi",
- "Indonesian",
- "Irish",
- "Italian",
- "Japanese",
- "Korean",
- "Lithuanian",
- "Luxembourgish",
- "Macedonian",
- "Mongolian",
- "Norwegian",
- "Polish",
- "Portuguese",
- "Punjabi",
- "Romanian",
- "Russian",
- "Serbian",
- "Slovak",
- "Slovenian",
- "Spanish",
- "Turkish",
- "Ukrainian",
- "Urdu",
- "Vietnamese",
- )
+ val subLanguages =
+ arrayOf(
+ "Albanian",
+ "Arabic",
+ "Bosnian",
+ "Bulgarian",
+ "Chinese",
+ "Croatian",
+ "Czech",
+ "Danish",
+ "Dutch",
+ "English",
+ "Estonian",
+ "Finnish",
+ "French",
+ "Georgian",
+ "German",
+ "Greek",
+ "Hebrew",
+ "Hindi",
+ "Indonesian",
+ "Irish",
+ "Italian",
+ "Japanese",
+ "Korean",
+ "Lithuanian",
+ "Luxembourgish",
+ "Macedonian",
+ "Mongolian",
+ "Norwegian",
+ "Polish",
+ "Portuguese",
+ "Punjabi",
+ "Romanian",
+ "Russian",
+ "Serbian",
+ "Slovak",
+ "Slovenian",
+ "Spanish",
+ "Turkish",
+ "Ukrainian",
+ "Urdu",
+ "Vietnamese",
+ )
val lang = subLanguages[PrefManager.getVal(PrefName.SubLanguage)]
subtitle = intent.getSerialized("subtitle")
- ?: when (val subLang: String? =
- PrefManager.getNullableCustomVal("subLang_${media.id}", null, String::class.java)) {
+ ?: when (
+ val subLang: String? =
+ PrefManager.getNullableCustomVal(
+ "subLang_${media.id}",
+ null,
+ String::class.java
+ )
+ ) {
null -> {
when (episode.selectedSubtitle) {
null -> null
- -1 -> ext.subtitles.find {
- it.language.contains(
- lang,
- ignoreCase = true
- ) || it.language.contains(getLanguageCode(lang), ignoreCase = true)
- }
+ -1 ->
+ ext.subtitles.find {
+ it.language.contains(lang, ignoreCase = true) ||
+ it.language.contains(
+ getLanguageCode(lang),
+ ignoreCase = true
+ )
+ }
else -> ext.subtitles.getOrNull(episode.selectedSubtitle!!)
}
@@ -1465,7 +1597,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
else -> ext.subtitles.find { it.language == subLang }
}
- //Subtitles
+ // Subtitles
hasExtSubtitles = ext.subtitles.isNotEmpty()
if (hasExtSubtitles) {
exoSubtitle.isVisible = hasExtSubtitles
@@ -1477,43 +1609,43 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
emptyList().toMutableList()
ext.subtitles.forEach { subtitle ->
val subtitleUrl = if (!hasExtSubtitles) video!!.file.url else subtitle.file.url
- //var localFile: String? = null
+ // var localFile: String? = null
if (subtitle.type == SubtitleType.UNKNOWN) {
runBlocking {
val type = SubtitleDownloader.loadSubtitleType(subtitleUrl)
val fileUri = Uri.parse(subtitleUrl)
- sub += MediaItem.SubtitleConfiguration
- .Builder(fileUri)
- .setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
- .setMimeType(
- when (type) {
- SubtitleType.VTT -> MimeTypes.TEXT_SSA
- SubtitleType.ASS -> MimeTypes.TEXT_SSA
- SubtitleType.SRT -> MimeTypes.TEXT_SSA
- else -> MimeTypes.TEXT_SSA
- }
- )
- .setId("69")
- .setLanguage(subtitle.language)
- .build()
+ sub +=
+ MediaItem.SubtitleConfiguration
+ .Builder(fileUri)
+ .setSelectionFlags(C.SELECTION_FLAG_DEFAULT)
+ .setMimeType(
+ when (type) {
+ SubtitleType.VTT -> MimeTypes.TEXT_SSA
+ SubtitleType.ASS -> MimeTypes.TEXT_SSA
+ SubtitleType.SRT -> MimeTypes.TEXT_SSA
+ else -> MimeTypes.TEXT_SSA
+ },
+ ).setId("69")
+ .setLanguage(subtitle.language)
+ .build()
}
println("sub: $sub")
} else {
val subUri = Uri.parse(subtitleUrl)
- sub += MediaItem.SubtitleConfiguration
- .Builder(subUri)
- .setSelectionFlags(C.SELECTION_FLAG_FORCED)
- .setMimeType(
- when (subtitle.type) {
- SubtitleType.VTT -> MimeTypes.TEXT_VTT
- SubtitleType.ASS -> MimeTypes.TEXT_SSA
- SubtitleType.SRT -> MimeTypes.APPLICATION_SUBRIP
- else -> MimeTypes.TEXT_UNKNOWN
- }
- )
- .setId("69")
- .setLanguage(subtitle.language)
- .build()
+ sub +=
+ MediaItem.SubtitleConfiguration
+ .Builder(subUri)
+ .setSelectionFlags(C.SELECTION_FLAG_FORCED)
+ .setMimeType(
+ when (subtitle.type) {
+ SubtitleType.VTT -> MimeTypes.TEXT_VTT
+ SubtitleType.ASS -> MimeTypes.TEXT_SSA
+ SubtitleType.SRT -> MimeTypes.APPLICATION_SUBRIP
+ else -> MimeTypes.TEXT_UNKNOWN
+ },
+ ).setId("69")
+ .setLanguage(subtitle.language)
+ .build()
}
}
@@ -1521,98 +1653,140 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
ext.onVideoPlayed(video)
}
- val httpClient = okHttpClient.newBuilder().apply {
- ignoreAllSSLErrors()
- followRedirects(true)
- followSslRedirects(true)
- }.build()
- val dataSourceFactory = DataSource.Factory {
- val dataSource: HttpDataSource = OkHttpDataSource.Factory(httpClient).createDataSource()
- defaultHeaders.forEach {
- dataSource.setRequestProperty(it.key, it.value)
+ val httpClient =
+ okHttpClient
+ .newBuilder()
+ .apply {
+ ignoreAllSSLErrors()
+ followRedirects(true)
+ followSslRedirects(true)
+ }.build()
+ val dataSourceFactory =
+ DataSource.Factory {
+ val dataSource: HttpDataSource =
+ OkHttpDataSource.Factory(httpClient).createDataSource()
+ defaultHeaders.forEach {
+ dataSource.setRequestProperty(it.key, it.value)
+ }
+ video?.file?.headers?.forEach {
+ dataSource.setRequestProperty(it.key, it.value)
+ }
+ dataSource
}
- video?.file?.headers?.forEach {
- dataSource.setRequestProperty(it.key, it.value)
- }
- dataSource
- }
val dafuckDataSourceFactory = DefaultDataSource.Factory(this)
- cacheFactory = CacheDataSource.Factory().apply {
- setCache(VideoCache.getInstance(this@ExoplayerView))
- if (ext.server.offline) {
- setUpstreamDataSourceFactory(dafuckDataSourceFactory)
- } else {
- setUpstreamDataSourceFactory(dataSourceFactory)
+ cacheFactory =
+ CacheDataSource.Factory().apply {
+ setCache(VideoCache.getInstance(this@ExoplayerView))
+ if (ext.server.offline) {
+ setUpstreamDataSourceFactory(dafuckDataSourceFactory)
+ } else {
+ setUpstreamDataSourceFactory(dataSourceFactory)
+ }
+ setCacheWriteDataSinkFactory(null)
}
- setCacheWriteDataSinkFactory(null)
- }
- val mimeType = when (video?.format) {
- VideoType.M3U8 -> MimeTypes.APPLICATION_M3U8
- VideoType.DASH -> MimeTypes.APPLICATION_MPD
- else -> MimeTypes.APPLICATION_MP4
- }
+ val mimeType =
+ when (video?.format) {
+ VideoType.M3U8 -> MimeTypes.APPLICATION_M3U8
+ VideoType.DASH -> MimeTypes.APPLICATION_MPD
+ else -> MimeTypes.APPLICATION_MP4
+ }
- val downloadedMediaItem = if (ext.server.offline) {
- val titleName = ext.server.name.split("/").first()
- val episodeName = ext.server.name.split("/").last()
- downloadId = PrefManager.getAnimeDownloadPreferences()
- .getString("$titleName - $episodeName", null)
- ?: PrefManager.getAnimeDownloadPreferences()
- .getString(ext.server.name, null)
- val exoItem = if (downloadId != null) {
- Helper.downloadManager(this)
- .downloadIndex.getDownload(downloadId!!)?.request?.toMediaItem()
- } else null
- if (exoItem != null) {
- exoItem
- } else {
-
- val directory =
- getSubDirectory(this, MediaType.ANIME, false, titleName, episodeName)
- if (directory != null) {
- val files = directory.listFiles()
- println(files)
- val docFile = directory.listFiles().firstOrNull {
- it.name?.endsWith(".mp4") == true || it.name?.endsWith(".mkv") == true
- || it.name?.endsWith(".${Injekt.get().extension?.extension?.getFileExtension()?.first ?: "ts"}") == true
- }
- if (docFile != null) {
- val uri = docFile.uri
- val downloadedMimeType = when (docFile.extension) {
- "mp4" -> MimeTypes.APPLICATION_MP4
- "mkv" -> MimeTypes.APPLICATION_MATROSKA
- else -> MimeTypes.APPLICATION_MP4
- }
- MediaItem.Builder().setUri(uri).setMimeType(downloadedMimeType).build()
+ val downloadedMediaItem =
+ if (ext.server.offline) {
+ val titleName =
+ ext.server.name
+ .split("/")
+ .first()
+ val episodeName =
+ ext.server.name
+ .split("/")
+ .last()
+ downloadId = PrefManager
+ .getAnimeDownloadPreferences()
+ .getString("$titleName - $episodeName", null)
+ ?: PrefManager
+ .getAnimeDownloadPreferences()
+ .getString(ext.server.name, null)
+ val exoItem =
+ if (downloadId != null) {
+ Helper
+ .downloadManager(this)
+ .downloadIndex
+ .getDownload(downloadId!!)
+ ?.request
+ ?.toMediaItem()
} else {
- snackString("File not found")
null
}
+ if (exoItem != null) {
+ exoItem
} else {
- snackString("Directory not found")
- null
+ val directory =
+ getSubDirectory(this, MediaType.ANIME, false, titleName, episodeName)
+ if (directory != null) {
+ val files = directory.listFiles()
+ println(files)
+ val docFile =
+ directory.listFiles().firstOrNull {
+ it.name?.endsWith(".mp4") == true ||
+ it.name?.endsWith(".mkv") == true ||
+ it.name?.endsWith(
+ ".${
+ Injekt
+ .get()
+ .extension
+ ?.extension
+ ?.getFileExtension()
+ ?.first ?: "ts"
+ }",
+ ) ==
+ true
+ }
+ if (docFile != null) {
+ val uri = docFile.uri
+ val downloadedMimeType =
+ when (docFile.extension) {
+ "mp4" -> MimeTypes.APPLICATION_MP4
+ "mkv" -> MimeTypes.APPLICATION_MATROSKA
+ else -> MimeTypes.APPLICATION_MP4
+ }
+ MediaItem
+ .Builder()
+ .setUri(uri)
+ .setMimeType(downloadedMimeType)
+ .build()
+ } else {
+ snackString("File not found")
+ null
+ }
+ } else {
+ snackString("Directory not found")
+ null
+ }
+ }
+ } else {
+ null
+ }
+
+ mediaItem =
+ if (downloadedMediaItem == null) {
+ val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType)
+ Logger.log("url: ${video!!.file.url}")
+ Logger.log("mimeType: $mimeType")
+ builder.setSubtitleConfigurations(sub)
+ builder.build()
+ } else {
+ if (sub.isNotEmpty()) {
+ val addedSubsDownloadedMediaItem = downloadedMediaItem.buildUpon()
+ val addLanguage = sub[0].buildUpon().setLanguage("en").build()
+ addedSubsDownloadedMediaItem.setSubtitleConfigurations(listOf(addLanguage))
+ episode.selectedSubtitle = 0
+ addedSubsDownloadedMediaItem.build()
+ } else {
+ downloadedMediaItem
}
}
- } else null
-
- mediaItem = if (downloadedMediaItem == null) {
- val builder = MediaItem.Builder().setUri(video!!.file.url).setMimeType(mimeType)
- Logger.log("url: ${video!!.file.url}")
- Logger.log("mimeType: $mimeType")
- builder.setSubtitleConfigurations(sub)
- builder.build()
- } else {
- if (sub.isNotEmpty()) {
- val addedSubsDownloadedMediaItem = downloadedMediaItem.buildUpon()
- val addLanguage = sub[0].buildUpon().setLanguage("en").build()
- addedSubsDownloadedMediaItem.setSubtitleConfigurations(listOf(addLanguage))
- episode.selectedSubtitle = 0
- addedSubsDownloadedMediaItem.build()
- } else {
- downloadedMediaItem
- }
- }
val audioMediaItem = mutableListOf()
audioLanguages.clear()
@@ -1621,179 +1795,203 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
if (code == "all") code = "un"
audioLanguages.add(Pair(it.lang, code))
audioMediaItem.add(
- MediaItem.Builder()
+ MediaItem
+ .Builder()
.setUri(it.url)
.setMimeType(MimeTypes.AUDIO_UNKNOWN)
.setTag(code)
- .build()
+ .build(),
)
}
- val audioSources = audioMediaItem.map { mediaItem ->
- if (mediaItem.localConfiguration?.uri.toString().contains(".m3u8")) {
- HlsMediaSource.Factory(cacheFactory).createMediaSource(mediaItem)
- } else {
- DefaultMediaSourceFactory(cacheFactory).createMediaSource(mediaItem)
- }
- }.toTypedArray()
- val videoMediaSource = DefaultMediaSourceFactory(cacheFactory)
- .createMediaSource(mediaItem)
+ val audioSources =
+ audioMediaItem
+ .map { mediaItem ->
+ if (mediaItem.localConfiguration
+ ?.uri
+ .toString()
+ .contains(".m3u8")
+ ) {
+ HlsMediaSource.Factory(cacheFactory).createMediaSource(mediaItem)
+ } else {
+ DefaultMediaSourceFactory(cacheFactory).createMediaSource(mediaItem)
+ }
+ }.toTypedArray()
+ val videoMediaSource =
+ DefaultMediaSourceFactory(cacheFactory)
+ .createMediaSource(mediaItem)
mediaSource = MergingMediaSource(videoMediaSource, *audioSources)
-
- //Source
+ // Source
exoSource.setOnClickListener {
sourceClick()
}
- //Quality Track
+ // Quality Track
trackSelector = DefaultTrackSelector(this)
- val parameters = trackSelector.buildUponParameters()
- .setAllowVideoMixedMimeTypeAdaptiveness(true)
- .setAllowVideoNonSeamlessAdaptiveness(true)
- .setSelectUndeterminedTextLanguage(true)
- .setAllowAudioMixedMimeTypeAdaptiveness(true)
- .setAllowMultipleAdaptiveSelections(true)
- .setPreferredTextLanguage(subtitle?.language ?: Locale.getDefault().language)
- .setPreferredTextRoleFlags(C.ROLE_FLAG_SUBTITLE)
- .setRendererDisabled(TRACK_TYPE_VIDEO, false)
- .setRendererDisabled(TRACK_TYPE_AUDIO, false)
- .setRendererDisabled(TRACK_TYPE_TEXT, false)
- .setMaxVideoSize(1, 1)
+ val parameters =
+ trackSelector
+ .buildUponParameters()
+ .setAllowVideoMixedMimeTypeAdaptiveness(true)
+ .setAllowVideoNonSeamlessAdaptiveness(true)
+ .setSelectUndeterminedTextLanguage(true)
+ .setAllowAudioMixedMimeTypeAdaptiveness(true)
+ .setAllowMultipleAdaptiveSelections(true)
+ .setPreferredTextLanguage(subtitle?.language ?: Locale.getDefault().language)
+ .setPreferredTextRoleFlags(C.ROLE_FLAG_SUBTITLE)
+ .setRendererDisabled(TRACK_TYPE_VIDEO, false)
+ .setRendererDisabled(TRACK_TYPE_AUDIO, false)
+ .setRendererDisabled(TRACK_TYPE_TEXT, false)
+ .setMaxVideoSize(1, 1)
// .setOverrideForType(TrackSelectionOverride(trackSelector, TRACK_TYPE_VIDEO))
- if (PrefManager.getVal(PrefName.SettingsPreferDub))
+ if (PrefManager.getVal(PrefName.SettingsPreferDub)) {
parameters.setPreferredAudioLanguage(Locale.getDefault().language)
+ }
trackSelector.setParameters(parameters)
if (playbackPosition != 0L && !changingServer && !PrefManager.getVal(PrefName.AlwaysContinue)) {
- val time = String.format(
- "%02d:%02d:%02d", TimeUnit.MILLISECONDS.toHours(playbackPosition),
- TimeUnit.MILLISECONDS.toMinutes(playbackPosition) - TimeUnit.HOURS.toMinutes(
- TimeUnit.MILLISECONDS.toHours(
- playbackPosition
- )
- ),
- TimeUnit.MILLISECONDS.toSeconds(playbackPosition) - TimeUnit.MINUTES.toSeconds(
- TimeUnit.MILLISECONDS.toMinutes(
- playbackPosition
- )
+ val time =
+ String.format(
+ "%02d:%02d:%02d",
+ TimeUnit.MILLISECONDS.toHours(playbackPosition),
+ TimeUnit.MILLISECONDS.toMinutes(playbackPosition) -
+ TimeUnit.HOURS.toMinutes(
+ TimeUnit.MILLISECONDS.toHours(
+ playbackPosition,
+ ),
+ ),
+ TimeUnit.MILLISECONDS.toSeconds(playbackPosition) -
+ TimeUnit.MINUTES.toSeconds(
+ TimeUnit.MILLISECONDS.toMinutes(
+ playbackPosition,
+ ),
+ ),
)
- )
- val dialog = AlertDialog.Builder(this, R.style.MyPopup)
- .setTitle(getString(R.string.continue_from, time)).apply {
- setCancelable(false)
- setPositiveButton(getString(R.string.yes)) { d, _ ->
- buildExoplayer()
- d.dismiss()
- }
- setNegativeButton(getString(R.string.no)) { d, _ ->
- playbackPosition = 0L
- buildExoplayer()
- d.dismiss()
- }
- }.show()
- dialog.window?.setDimAmount(0.8f)
- } else buildExoplayer()
+ customAlertDialog().apply {
+ setTitle(getString(R.string.continue_from, time))
+ setCancelable(false)
+ setPosButton(getString(R.string.yes)) {
+ buildExoplayer()
+ }
+ setNegButton(getString(R.string.no)) {
+ playbackPosition = 0L
+ buildExoplayer()
+ }
+ show()
+ }
+ } else {
+ buildExoplayer()
+ }
}
private fun buildExoplayer() {
- //Player
- val loadControl = DefaultLoadControl.Builder()
- .setBackBuffer(1000 * 60 * 2, true)
- .setBufferDurationsMs(
- DEFAULT_MIN_BUFFER_MS,
- DEFAULT_MAX_BUFFER_MS,
- BUFFER_FOR_PLAYBACK_MS,
- BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS
- )
- .build()
+ // Player
+ val loadControl =
+ DefaultLoadControl
+ .Builder()
+ .setBackBuffer(1000 * 60 * 2, true)
+ .setBufferDurationsMs(
+ DEFAULT_MIN_BUFFER_MS,
+ DEFAULT_MAX_BUFFER_MS,
+ BUFFER_FOR_PLAYBACK_MS,
+ BUFFER_FOR_PLAYBACK_AFTER_REBUFFER_MS,
+ ).build()
hideSystemBars()
val useExtensionDecoder = PrefManager.getVal(PrefName.UseAdditionalCodec)
- val decoder = if (useExtensionDecoder) {
- DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
- } else {
- DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF
- }
- val renderersFactory = NextRenderersFactory(this)
- .setEnableDecoderFallback(true)
- .setExtensionRendererMode(decoder)
-
- exoPlayer = ExoPlayer.Builder(this, renderersFactory)
- .setMediaSourceFactory(DefaultMediaSourceFactory(cacheFactory))
- .setTrackSelector(trackSelector)
- .setLoadControl(loadControl)
- .build().apply {
- playWhenReady = true
- this.playbackParameters = this@ExoplayerView.playbackParameters
- setMediaSource(mediaSource)
- prepare()
- PrefManager.getCustomVal(
- "${media.id}_${media.anime!!.selectedEpisode}_max",
- Long.MAX_VALUE
- )
- .takeIf { it != Long.MAX_VALUE }
- ?.let { if (it <= playbackPosition) playbackPosition = max(0, it - 5) }
- seekTo(playbackPosition)
+ val decoder =
+ if (useExtensionDecoder) {
+ DefaultRenderersFactory.EXTENSION_RENDERER_MODE_PREFER
+ } else {
+ DefaultRenderersFactory.EXTENSION_RENDERER_MODE_OFF
}
+ val renderersFactory =
+ NextRenderersFactory(this)
+ .setEnableDecoderFallback(true)
+ .setExtensionRendererMode(decoder)
+
+ exoPlayer =
+ ExoPlayer
+ .Builder(this, renderersFactory)
+ .setMediaSourceFactory(DefaultMediaSourceFactory(cacheFactory))
+ .setTrackSelector(trackSelector)
+ .setLoadControl(loadControl)
+ .build()
+ .apply {
+ playWhenReady = true
+ this.playbackParameters = this@ExoplayerView.playbackParameters
+ setMediaSource(mediaSource)
+ prepare()
+ PrefManager
+ .getCustomVal(
+ "${media.id}_${media.anime!!.selectedEpisode}_max",
+ Long.MAX_VALUE,
+ ).takeIf { it != Long.MAX_VALUE }
+ ?.let { if (it <= playbackPosition) playbackPosition = max(0, it - 5) }
+ seekTo(playbackPosition)
+ }
playerView.player = exoPlayer
- exoPlayer.addListener(object : Player.Listener {
- var activeSubtitles = ArrayDeque(3)
- var lastSubtitle: String? = null
- var lastPosition: Long = 0
+ exoPlayer.addListener(
+ object : Player.Listener {
+ var activeSubtitles = ArrayDeque(3)
+ var lastSubtitle: String? = null
+ var lastPosition: Long = 0
- override fun onCues(cueGroup: CueGroup) {
- if (PrefManager.getVal(PrefName.TextviewSubtitles)) {
- exoSubtitleView.visibility = View.GONE
- customSubtitleView.visibility = View.VISIBLE
- val newCues = cueGroup.cues.map { it.text.toString() }
+ override fun onCues(cueGroup: CueGroup) {
+ if (PrefManager.getVal(PrefName.TextviewSubtitles)) {
+ exoSubtitleView.visibility = View.GONE
+ customSubtitleView.visibility = View.VISIBLE
+ val newCues = cueGroup.cues.map { it.text.toString() }
- if (newCues.isEmpty()) {
- customSubtitleView.text = ""
- activeSubtitles.clear()
- lastSubtitle = null
- lastPosition = 0
- return
- }
-
- val currentPosition = exoPlayer.currentPosition
-
- if ((lastSubtitle?.length
- ?: 0) < 20 || (lastPosition != 0L && currentPosition - lastPosition > 1500)
- ) {
- activeSubtitles.clear()
- }
-
- for (newCue in newCues) {
- if (newCue !in activeSubtitles) {
- if (activeSubtitles.size >= 2) {
- activeSubtitles.removeLast()
- }
- activeSubtitles.addFirst(newCue)
- lastSubtitle = newCue
- lastPosition = currentPosition
+ if (newCues.isEmpty()) {
+ customSubtitleView.text = ""
+ activeSubtitles.clear()
+ lastSubtitle = null
+ lastPosition = 0
+ return
}
- }
- customSubtitleView.text = activeSubtitles.joinToString("\n")
- } else {
- customSubtitleView.text = ""
- customSubtitleView.visibility = View.GONE
- exoSubtitleView.visibility = View.VISIBLE
+ val currentPosition = exoPlayer.currentPosition
+
+ if ((lastSubtitle?.length
+ ?: 0) < 20 || (lastPosition != 0L && currentPosition - lastPosition > 1500)
+ ) {
+ activeSubtitles.clear()
+ }
+
+ for (newCue in newCues) {
+ if (newCue !in activeSubtitles) {
+ if (activeSubtitles.size >= 2) {
+ activeSubtitles.removeLast()
+ }
+ activeSubtitles.addFirst(newCue)
+ lastSubtitle = newCue
+ lastPosition = currentPosition
+ }
+ }
+
+ customSubtitleView.text = activeSubtitles.joinToString("\n")
+ } else {
+ customSubtitleView.text = ""
+ customSubtitleView.visibility = View.GONE
+ exoSubtitleView.visibility = View.VISIBLE
+ }
}
- }
- })
+ },
+ )
applySubtitleStyles(customSubtitleView)
setupSubFormatting(playerView)
try {
val rightNow = Calendar.getInstance()
- mediaSession = MediaSession.Builder(this, exoPlayer)
- .setId(rightNow.timeInMillis.toString())
- .build()
+ mediaSession =
+ MediaSession
+ .Builder(this, exoPlayer)
+ .setId(rightNow.timeInMillis.toString())
+ .build()
} catch (e: Exception) {
toast(e.toString())
}
@@ -1807,10 +2005,11 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
val isDisabled = (subtitle == null && hasExtSubtitles)
- exoPlayer.trackSelectionParameters = exoPlayer.trackSelectionParameters
- .buildUpon()
- .setTrackTypeDisabled(TRACK_TYPE_TEXT, isDisabled)
- .build()
+ exoPlayer.trackSelectionParameters =
+ exoPlayer.trackSelectionParameters
+ .buildUpon()
+ .setTrackTypeDisabled(TRACK_TYPE_TEXT, isDisabled)
+ .build()
}
private fun releasePlayer() {
@@ -1826,7 +2025,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
DiscordServiceRunningSingleton.running = false
stopService(stopIntent)
}
-
}
override fun onSaveInstanceState(outState: Bundle) {
@@ -1844,18 +2042,22 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
media.selected!!.server = null
PrefManager.setCustomVal(
- "${media.id}_${media.anime!!.selectedEpisode}", exoPlayer.currentPosition
+ "${media.id}_${media.anime!!.selectedEpisode}",
+ exoPlayer.currentPosition,
)
model.saveSelected(media.id, media.selected!!)
model.onEpisodeClick(
- media, episode.number, this.supportFragmentManager,
- launch = false
+ media,
+ episode.number,
+ this.supportFragmentManager,
+ launch = false,
)
}
private fun subClick() {
PrefManager.setCustomVal(
- "${media.id}_${media.anime!!.selectedEpisode}", exoPlayer.currentPosition
+ "${media.id}_${media.anime!!.selectedEpisode}",
+ exoPlayer.currentPosition,
)
model.saveSelected(media.id, media.selected!!)
SubtitleDialogFragment().show(supportFragmentManager, "dialog")
@@ -1871,7 +2073,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
if (exoPlayer.currentPosition > 5000) {
PrefManager.setCustomVal(
"${media.id}_${media.anime!!.selectedEpisode}",
- exoPlayer.currentPosition
+ exoPlayer.currentPosition,
)
}
}
@@ -1895,6 +2097,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
private var wasPlaying = false
+
override fun onWindowFocusChanged(hasFocus: Boolean) {
if (PrefManager.getVal(PrefName.FocusPause) && !epChanging) {
if (isInitialized && !hasFocus) wasPlaying = exoPlayer.isPlaying
@@ -1912,9 +2115,12 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
isPlayerPlaying = isPlaying
playerView.keepScreenOn = isPlaying
(exoPlay.drawable as Animatable?)?.start()
- if (!this.isDestroyed) Glide.with(this)
- .load(if (isPlaying) R.drawable.anim_play_to_pause else R.drawable.anim_pause_to_play)
- .into(exoPlay)
+ if (!this.isDestroyed) {
+ Glide
+ .with(this)
+ .load(if (isPlaying) R.drawable.anim_play_to_pause else R.drawable.anim_pause_to_play)
+ .into(exoPlay)
+ }
}
}
@@ -1922,7 +2128,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
super.onRenderedFirstFrame()
PrefManager.setCustomVal(
"${media.id}_${media.anime!!.selectedEpisode}_max",
- exoPlayer.duration
+ exoPlayer.duration,
)
val height = (exoPlayer.videoFormat ?: return).height
val width = (exoPlayer.videoFormat ?: return).width
@@ -1931,10 +2137,11 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
videoInfo.text = getString(R.string.video_quality, height)
- if (exoPlayer.duration < playbackPosition)
+ if (exoPlayer.duration < playbackPosition) {
exoPlayer.seekTo(0)
+ }
- //if playbackPosition is within 92% of the episode length, reset it to 0
+ // if playbackPosition is within 92% of the episode length, reset it to 0
if (playbackPosition > exoPlayer.duration.toFloat() * 0.92) {
playbackPosition = 0
exoPlayer.seekTo(0)
@@ -1945,20 +2152,25 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
lifecycleScope.launch(Dispatchers.IO) {
model.loadTimeStamps(
media.idMAL,
- media.anime?.selectedEpisode?.trim()?.toIntOrNull(),
+ media.anime
+ ?.selectedEpisode
+ ?.trim()
+ ?.toIntOrNull(),
dur / 1000,
- PrefManager.getVal(PrefName.UseProxyForTimeStamps)
+ PrefManager.getVal(PrefName.UseProxyForTimeStamps),
)
}
}
}
- //Link Preloading
+ // Link Preloading
private var preloading = false
+
private fun updateProgress() {
if (isInitialized) {
- if (exoPlayer.currentPosition.toFloat() / exoPlayer.duration > PrefManager.getVal(
- PrefName.WatchPercentage
+ if (exoPlayer.currentPosition.toFloat() / exoPlayer.duration >
+ PrefManager.getVal(
+ PrefName.WatchPercentage,
)
) {
preloading = true
@@ -1966,134 +2178,150 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
val ep = episodes[episodeArr[currentEpisodeIndex + i]] ?: return@nextEpisode
val selected = media.selected ?: return@nextEpisode
lifecycleScope.launch(Dispatchers.IO) {
- if (media.selected!!.server != null)
+ if (media.selected!!.server != null) {
model.loadEpisodeSingleVideo(ep, selected, false)
- else
+ } else {
model.loadEpisodeVideos(ep, selected.sourceIndex, false)
+ }
}
}
}
}
- if (!preloading) handler.postDelayed({
- updateProgress()
- }, 2500)
+ if (!preloading) {
+ handler.postDelayed({
+ updateProgress()
+ }, 2500)
+ }
}
- //TimeStamp Updating
+ // TimeStamp Updating
private var currentTimeStamp: AniSkip.Stamp? = null
private var skippedTimeStamps: MutableList = mutableListOf()
+
private fun updateTimeStamp() {
if (isInitialized) {
val playerCurrentTime = exoPlayer.currentPosition / 1000
- currentTimeStamp = model.timeStamps.value?.find { timestamp ->
- timestamp.interval.startTime < playerCurrentTime
- && playerCurrentTime < (timestamp.interval.endTime - 1)
- }
+ currentTimeStamp =
+ model.timeStamps.value?.find { timestamp ->
+ timestamp.interval.startTime < playerCurrentTime &&
+ playerCurrentTime < (timestamp.interval.endTime - 1)
+ }
val new = currentTimeStamp
- timeStampText.text = if (new != null) {
- fun disappearSkip() {
- functionstarted = true
- skipTimeButton.visibility = View.VISIBLE
- exoSkip.visibility = View.GONE
- skipTimeText.text = new.skipType.getType()
- skipTimeButton.setOnClickListener {
- exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
- }
- var timer: CountDownTimer? = null
- fun cancelTimer() {
- timer?.cancel()
- timer = null
- return
- }
- timer = object : CountDownTimer(5000, 1000) {
- override fun onTick(millisUntilFinished: Long) {
- if (new == null) {
- skipTimeButton.visibility = View.GONE
- exoSkip.isVisible = PrefManager.getVal(PrefName.SkipTime) > 0
- disappeared = false
- functionstarted = false
- cancelTimer()
- }
- }
-
- override fun onFinish() {
- skipTimeButton.visibility = View.GONE
- exoSkip.isVisible = PrefManager.getVal(PrefName.SkipTime) > 0
- disappeared = true
- functionstarted = false
- cancelTimer()
- }
- }
- timer?.start()
-
- }
- if (PrefManager.getVal(PrefName.ShowTimeStampButton)) {
-
- if (!functionstarted && !disappeared && PrefManager.getVal(PrefName.AutoHideTimeStamps)) {
- disappearSkip()
- } else if (!PrefManager.getVal(PrefName.AutoHideTimeStamps)) {
+ timeStampText.text =
+ if (new != null) {
+ fun disappearSkip() {
+ functionstarted = true
skipTimeButton.visibility = View.VISIBLE
exoSkip.visibility = View.GONE
skipTimeText.text = new.skipType.getType()
skipTimeButton.setOnClickListener {
exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
}
- }
+ var timer: CountDownTimer? = null
+ fun cancelTimer() {
+ timer?.cancel()
+ timer = null
+ return
+ }
+ timer =
+ object : CountDownTimer(5000, 1000) {
+ override fun onTick(millisUntilFinished: Long) {
+ if (new == null) {
+ skipTimeButton.visibility = View.GONE
+ exoSkip.isVisible =
+ PrefManager.getVal(PrefName.SkipTime) > 0
+ disappeared = false
+ functionstarted = false
+ cancelTimer()
+ }
+ }
+
+ override fun onFinish() {
+ skipTimeButton.visibility = View.GONE
+ exoSkip.isVisible =
+ PrefManager.getVal(PrefName.SkipTime) > 0
+ disappeared = true
+ functionstarted = false
+ cancelTimer()
+ }
+ }
+ timer?.start()
+ }
+ if (PrefManager.getVal(PrefName.ShowTimeStampButton)) {
+ if (!functionstarted && !disappeared && PrefManager.getVal(PrefName.AutoHideTimeStamps)) {
+ disappearSkip()
+ } else if (!PrefManager.getVal(PrefName.AutoHideTimeStamps)) {
+ skipTimeButton.visibility = View.VISIBLE
+ exoSkip.visibility = View.GONE
+ skipTimeText.text = new.skipType.getType()
+ skipTimeButton.setOnClickListener {
+ exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
+ }
+ }
+ }
+ if (PrefManager.getVal(PrefName.AutoSkipOPED) &&
+ (new.skipType == "op" || new.skipType == "ed") &&
+ !skippedTimeStamps.contains(new)
+ ) {
+ exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
+ skippedTimeStamps.add(new)
+ }
+ if (PrefManager.getVal(PrefName.AutoSkipRecap) &&
+ new.skipType == "recap" &&
+ !skippedTimeStamps.contains(
+ new,
+ )
+ ) {
+ exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
+ skippedTimeStamps.add(new)
+ }
+ new.skipType.getType()
+ } else {
+ disappeared = false
+ functionstarted = false
+ skipTimeButton.visibility = View.GONE
+ exoSkip.isVisible = PrefManager.getVal(PrefName.SkipTime) > 0
+ ""
}
- if (PrefManager.getVal(PrefName.AutoSkipOPED) && (new.skipType == "op" || new.skipType == "ed")
- && !skippedTimeStamps.contains(new)
- ) {
- exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
- skippedTimeStamps.add(new)
- }
- if (PrefManager.getVal(PrefName.AutoSkipRecap) && new.skipType == "recap" && !skippedTimeStamps.contains(
- new
- )
- ) {
- exoPlayer.seekTo((new.interval.endTime * 1000).toLong())
- skippedTimeStamps.add(new)
- }
- new.skipType.getType()
- } else {
- disappeared = false
- functionstarted = false
- skipTimeButton.visibility = View.GONE
- exoSkip.isVisible = PrefManager.getVal(PrefName.SkipTime) > 0
- ""
- }
}
handler.postDelayed({
updateTimeStamp()
}, 500)
}
- fun onSetTrackGroupOverride(trackGroup: Tracks.Group, type: @C.TrackType Int, index: Int = 0) {
+ fun onSetTrackGroupOverride(
+ trackGroup: Tracks.Group,
+ type: @C.TrackType Int,
+ index: Int = 0,
+ ) {
val isDisabled = trackGroup.getTrackFormat(0).language == "none"
- exoPlayer.trackSelectionParameters = exoPlayer.trackSelectionParameters
- .buildUpon()
- .setTrackTypeDisabled(TRACK_TYPE_TEXT, isDisabled)
- .setOverrideForType(
- TrackSelectionOverride(trackGroup.mediaTrackGroup, index)
- )
- .build()
+ exoPlayer.trackSelectionParameters =
+ exoPlayer.trackSelectionParameters
+ .buildUpon()
+ .setTrackTypeDisabled(TRACK_TYPE_TEXT, isDisabled)
+ .setOverrideForType(
+ TrackSelectionOverride(trackGroup.mediaTrackGroup, index),
+ ).build()
if (type == TRACK_TYPE_TEXT) {
setupSubFormatting(playerView)
applySubtitleStyles(customSubtitleView)
}
- playerView.subtitleView?.alpha = when (isDisabled) {
- false -> PrefManager.getVal(PrefName.SubAlpha)
- true -> 0f
- }
+ playerView.subtitleView?.alpha =
+ when (isDisabled) {
+ false -> PrefManager.getVal(PrefName.SubAlpha)
+ true -> 0f
+ }
}
- private val dummyTrack = Tracks.Group(
- TrackGroup("Dummy Track", Format.Builder().apply { setLanguage("none") }.build()),
- true,
- intArrayOf(1),
- booleanArrayOf(false)
- )
+ private val dummyTrack =
+ Tracks.Group(
+ TrackGroup("Dummy Track", Format.Builder().apply { setLanguage("none") }.build()),
+ true,
+ intArrayOf(1),
+ booleanArrayOf(false),
+ )
override fun onTracksChanged(tracks: Tracks) {
val audioTracks: ArrayList = arrayListOf()
@@ -2101,7 +2329,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
tracks.groups.forEach {
println(
"Track__: $it\nTrack__: ${it.length}\nTrack__: ${it.isSelected}\n" +
- "Track__: ${it.type}\nTrack__: ${it.mediaTrackGroup.id}"
+ "Track__: ${it.type}\nTrack__: ${it.mediaTrackGroup.id}",
)
when (it.type) {
TRACK_TYPE_AUDIO -> {
@@ -2130,31 +2358,33 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
- private val onChangeSettings = registerForActivityResult(
- ActivityResultContracts.StartActivityForResult()
- ) { _: ActivityResult ->
- if (!hasExtSubtitles) {
- exoPlayer.currentTracks.groups.forEach { trackGroup ->
- when (trackGroup.type) {
- TRACK_TYPE_TEXT -> {
- if (PrefManager.getVal(PrefName.Subtitles)) {
- onSetTrackGroupOverride(trackGroup, TRACK_TYPE_TEXT)
- } else {
- onSetTrackGroupOverride(dummyTrack, TRACK_TYPE_TEXT)
+ private val onChangeSettings =
+ registerForActivityResult(
+ ActivityResultContracts.StartActivityForResult(),
+ ) { _: ActivityResult ->
+ if (!hasExtSubtitles) {
+ exoPlayer.currentTracks.groups.forEach { trackGroup ->
+ when (trackGroup.type) {
+ TRACK_TYPE_TEXT -> {
+ if (PrefManager.getVal(PrefName.Subtitles)) {
+ onSetTrackGroupOverride(trackGroup, TRACK_TYPE_TEXT)
+ } else {
+ onSetTrackGroupOverride(dummyTrack, TRACK_TYPE_TEXT)
+ }
}
- }
- else -> {}
+ else -> {}
+ }
}
}
+ if (isInitialized) exoPlayer.play()
}
- if (isInitialized) exoPlayer.play()
- }
override fun onPlayerError(error: PlaybackException) {
when (error.errorCode) {
PlaybackException.ERROR_CODE_IO_BAD_HTTP_STATUS,
- PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED -> {
+ PlaybackException.ERROR_CODE_IO_NETWORK_CONNECTION_FAILED,
+ -> {
toast("Source Exception : ${error.message}")
isPlayerPlaying = true
sourceClick()
@@ -2168,6 +2398,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
private var isBuffering = true
+
override fun onPlaybackStateChanged(playbackState: Int) {
if (playbackState == ExoPlayer.STATE_READY) {
exoPlayer.play()
@@ -2178,24 +2409,30 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
isBuffering = playbackState == Player.STATE_BUFFERING
if (playbackState == Player.STATE_ENDED && PrefManager.getVal(PrefName.AutoPlay)) {
- if (interacted) exoNext.performClick()
- else toast(getString(R.string.autoplay_cancelled))
+ if (interacted) {
+ exoNext.performClick()
+ } else {
+ toast(getString(R.string.autoplay_cancelled))
+ }
}
super.onPlaybackStateChanged(playbackState)
}
private fun updateAniProgress() {
val incognito: Boolean = PrefManager.getVal(PrefName.Incognito)
- val episodeEnd = exoPlayer.currentPosition / episodeLength > PrefManager.getVal(
- PrefName.WatchPercentage
- )
+ val episodeEnd =
+ exoPlayer.currentPosition / episodeLength >
+ PrefManager.getVal(
+ PrefName.WatchPercentage,
+ )
val episode0 = currentEpisodeIndex == 0 && PrefManager.getVal(PrefName.ChapterZeroPlayer)
if (!incognito && (episodeEnd || episode0) && Anilist.userid != null
- )
+ ) {
if (PrefManager.getCustomVal(
"${media.id}_save_progress",
- true
- ) && (if (media.isAdult) PrefManager.getVal(PrefName.UpdateForHPlayer) else true)
+ true,
+ ) &&
+ (if (media.isAdult) PrefManager.getVal(PrefName.UpdateForHPlayer) else true)
) {
if (episode0) {
updateProgress(media, "0")
@@ -2205,21 +2442,30 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
}
}
+ }
}
- private fun nextEpisode(toast: Boolean = true, runnable: ((Int) -> Unit)) {
+ private fun nextEpisode(
+ toast: Boolean = true,
+ runnable: ((Int) -> Unit),
+ ) {
var isFiller = true
var i = 1
while (isFiller) {
if (episodeArr.size > currentEpisodeIndex + i) {
isFiller =
- if (PrefManager.getVal(PrefName.AutoSkipFiller)) episodes[episodeArr[currentEpisodeIndex + i]]?.filler
- ?: false else false
+ if (PrefManager.getVal(PrefName.AutoSkipFiller)) {
+ episodes[episodeArr[currentEpisodeIndex + i]]?.filler
+ ?: false
+ } else {
+ false
+ }
if (!isFiller) runnable.invoke(i)
i++
} else {
- if (toast)
+ if (toast) {
toast(getString(R.string.no_next_episode))
+ }
isFiller = false
}
}
@@ -2262,7 +2508,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
if (subtitle != null) shareVideo.putExtra("subtitle", subtitleUrl)
shareVideo.putExtra(
"title",
- media.userPreferredName + " : Ep " + episodeTitleArr[currentEpisodeIndex]
+ media.userPreferredName + " : Ep " + episodeTitleArr[currentEpisodeIndex],
)
shareVideo.putExtra("poster", episode.thumb?.url ?: media.cover)
val headers = Bundle()
@@ -2295,7 +2541,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
PictureInPictureParams
.Builder()
.setAspectRatio(aspectRatio)
- .build()
+ .build(),
)
} else if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.N) {
enterPictureInPictureMode()
@@ -2316,7 +2562,7 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
if (isInitialized) {
PrefManager.setCustomVal(
"${media.id}_${episode.number}",
- exoPlayer.currentPosition
+ exoPlayer.currentPosition,
)
if (wasPlaying) exoPlayer.play()
}
@@ -2337,59 +2583,65 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
@RequiresApi(Build.VERSION_CODES.O)
override fun onPictureInPictureModeChanged(
isInPictureInPictureMode: Boolean,
- newConfig: Configuration
+ newConfig: Configuration,
) {
onPiPChanged(isInPictureInPictureMode)
super.onPictureInPictureModeChanged(isInPictureInPictureMode, newConfig)
}
- private val keyMap: MutableMap Unit)?> = mutableMapOf(
- KEYCODE_DPAD_RIGHT to null,
- KEYCODE_DPAD_LEFT to null,
- KEYCODE_SPACE to { exoPlay.performClick() },
- KEYCODE_N to { exoNext.performClick() },
- KEYCODE_B to { exoPrev.performClick() }
- )
+ private val keyMap: MutableMap Unit)?> =
+ mutableMapOf(
+ KEYCODE_DPAD_RIGHT to null,
+ KEYCODE_DPAD_LEFT to null,
+ KEYCODE_SPACE to { exoPlay.performClick() },
+ KEYCODE_N to { exoNext.performClick() },
+ KEYCODE_B to { exoPrev.performClick() },
+ )
- override fun dispatchKeyEvent(event: KeyEvent): Boolean {
- return if (keyMap.containsKey(event.keyCode)) {
+ override fun dispatchKeyEvent(event: KeyEvent): Boolean =
+ if (keyMap.containsKey(event.keyCode)) {
(event.action == ACTION_UP).also {
if (isInitialized && it) keyMap[event.keyCode]?.invoke()
}
} else {
super.dispatchKeyEvent(event)
}
- }
-
private fun startCastPlayer() {
if (!isCastApiAvailable) {
snackString("Cast API not available")
return
}
- //make sure mediaItem is initialized and castPlayer is not null
+ // make sure mediaItem is initialized and castPlayer is not null
if (!this::mediaItem.isInitialized || castPlayer == null) return
castPlayer?.setMediaItem(mediaItem)
castPlayer?.prepare()
playerView.player = castPlayer
exoPlayer.stop()
- castPlayer?.addListener(object : Player.Listener {
- //if the player is paused changed, we want to update the UI
- override fun onPlayWhenReadyChanged(playWhenReady: Boolean, reason: Int) {
- super.onPlayWhenReadyChanged(playWhenReady, reason)
- if (playWhenReady) {
- (exoPlay.drawable as Animatable?)?.start()
- Glide.with(this@ExoplayerView)
- .load(R.drawable.anim_play_to_pause)
- .into(exoPlay)
- } else {
- (exoPlay.drawable as Animatable?)?.start()
- Glide.with(this@ExoplayerView)
- .load(R.drawable.anim_pause_to_play)
- .into(exoPlay)
+ castPlayer?.addListener(
+ object : Player.Listener {
+ // if the player is paused changed, we want to update the UI
+ override fun onPlayWhenReadyChanged(
+ playWhenReady: Boolean,
+ reason: Int,
+ ) {
+ super.onPlayWhenReadyChanged(playWhenReady, reason)
+ if (playWhenReady) {
+ (exoPlay.drawable as Animatable?)?.start()
+ Glide
+ .with(this@ExoplayerView)
+ .load(R.drawable.anim_play_to_pause)
+ .into(exoPlay)
+ } else {
+ (exoPlay.drawable as Animatable?)?.start()
+ Glide
+ .with(this@ExoplayerView)
+ .load(R.drawable.anim_pause_to_play)
+ .into(exoPlay)
+ }
}
- }
- })
+ },
+ )
}
private fun startExoPlayer() {
@@ -2409,14 +2661,14 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
startExoPlayer()
}
-
@SuppressLint("ViewConstructor")
class ExtendedTimeBar(
context: Context,
- attrs: AttributeSet?
+ attrs: AttributeSet?,
) : DefaultTimeBar(context, attrs) {
private var enabled = false
private var forceDisabled = false
+
override fun setEnabled(enabled: Boolean) {
this.enabled = enabled
super.setEnabled(!forceDisabled && this.enabled)
@@ -2430,7 +2682,6 @@ class ExoplayerView : AppCompatActivity(), Player.Listener, SessionAvailabilityL
}
class CustomCastButton : MediaRouteButton {
-
private var castCallback: (() -> Unit)? = null
fun setCastCallback(castCallback: () -> Unit) {
@@ -2444,15 +2695,14 @@ class CustomCastButton : MediaRouteButton {
constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(
context,
attrs,
- defStyleAttr
+ defStyleAttr,
)
- override fun performClick(): Boolean {
- return if (PrefManager.getVal(PrefName.UseInternalCast)) {
+ override fun performClick(): Boolean =
+ if (PrefManager.getVal(PrefName.UseInternalCast)) {
super.performClick()
} else {
castCallback?.let { it() }
true
}
- }
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
index a76d44a4..ddf98974 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadAdapter.kt
@@ -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(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)
}
diff --git a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
index 6b7eb6bc..63ef7408 100644
--- a/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
+++ b/app/src/main/java/ani/dantotsu/media/manga/MangaReadFragment.kt
@@ -474,7 +474,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
scanlator = chapter.scanlator ?: "Unknown",
imageData = images,
sourceMedia = media,
- retries = 2,
+ retries = 25,
simultaneousDownloads = 2
)
diff --git a/app/src/main/java/ani/dantotsu/others/CrashActivity.kt b/app/src/main/java/ani/dantotsu/others/CrashActivity.kt
index c410d687..c8a0af8b 100644
--- a/app/src/main/java/ani/dantotsu/others/CrashActivity.kt
+++ b/app/src/main/java/ani/dantotsu/others/CrashActivity.kt
@@ -24,11 +24,11 @@ class CrashActivity : AppCompatActivity() {
super.onCreate(savedInstanceState)
ThemeManager(this).applyTheme()
initActivity(this)
- binding = ActivityCrashBinding.inflate(layoutInflater)
window.setFlags(
WindowManager.LayoutParams.FLAG_SECURE,
WindowManager.LayoutParams.FLAG_SECURE
)
+ binding = ActivityCrashBinding.inflate(layoutInflater)
setContentView(binding.root)
binding.root.updateLayoutParams {
topMargin = statusBarHeight
diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsAnimeActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsAnimeActivity.kt
index 2a5c6aee..73f90595 100644
--- a/app/src/main/java/ani/dantotsu/settings/SettingsAnimeActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SettingsAnimeActivity.kt
@@ -19,6 +19,7 @@ import ani.dantotsu.settings.saving.PrefManager
import ani.dantotsu.settings.saving.PrefName
import ani.dantotsu.statusBarHeight
import ani.dantotsu.themes.ThemeManager
+import ani.dantotsu.util.customAlertDialog
import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
@@ -57,23 +58,16 @@ class SettingsAnimeActivity : AppCompatActivity() {
desc = getString(R.string.purge_anime_downloads_desc),
icon = R.drawable.ic_round_delete_24,
onClick = {
- val dialog = AlertDialog.Builder(context, R.style.MyPopup)
- .setTitle(R.string.purge_anime_downloads)
- .setMessage(
- getString(
- R.string.purge_confirm,
- getString(R.string.anime)
- )
- )
- .setPositiveButton(R.string.yes) { dialog, _ ->
+ context.customAlertDialog().apply {
+ setTitle(R.string.purge_anime_downloads)
+ setMessage(R.string.purge_confirm, getString(R.string.anime))
+ setPosButton(R.string.yes, onClick = {
val downloadsManager = Injekt.get()
downloadsManager.purgeDownloads(MediaType.ANIME)
- dialog.dismiss()
- }.setNegativeButton(R.string.no) { dialog, _ ->
- dialog.dismiss()
- }.create()
- dialog.window?.setDimAmount(0.8f)
- dialog.show()
+ })
+ setNegButton(R.string.no)
+ show()
+ }
}
),
@@ -143,4 +137,4 @@ class SettingsAnimeActivity : AppCompatActivity() {
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt
index 8cfa7160..f0bf91d0 100644
--- a/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SettingsCommonActivity.kt
@@ -45,7 +45,6 @@ import uy.kohesive.injekt.Injekt
import uy.kohesive.injekt.api.get
import java.util.UUID
-
class SettingsCommonActivity : AppCompatActivity() {
private lateinit var binding: ActivitySettingsCommonBinding
private lateinit var launcher: LauncherWrapper
@@ -62,23 +61,27 @@ class SettingsCommonActivity : AppCompatActivity() {
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
if (uri != null) {
try {
- val jsonString = contentResolver.openInputStream(uri)?.readBytes()
- ?: throw Exception("Error reading file")
+ val jsonString =
+ contentResolver.openInputStream(uri)?.readBytes()
+ ?: throw Exception("Error reading file")
val name = DocumentFile.fromSingleUri(this, uri)?.name ?: "settings"
- //.sani is encrypted, .ani is not
+ // .sani is encrypted, .ani is not
if (name.endsWith(".sani")) {
passwordAlertDialog(false) { password ->
if (password != null) {
val salt = jsonString.copyOfRange(0, 16)
val encrypted = jsonString.copyOfRange(16, jsonString.size)
- val decryptedJson = try {
- PreferenceKeystore.decryptWithPassword(
- password, encrypted, salt
- )
- } catch (e: Exception) {
- toast(getString(R.string.incorrect_password))
- return@passwordAlertDialog
- }
+ val decryptedJson =
+ try {
+ PreferenceKeystore.decryptWithPassword(
+ password,
+ encrypted,
+ salt,
+ )
+ } catch (e: Exception) {
+ toast(getString(R.string.incorrect_password))
+ return@passwordAlertDialog
+ }
if (PreferencePackager.unpack(decryptedJson)) restartApp()
} else {
toast(getString(R.string.password_cannot_be_empty))
@@ -100,7 +103,6 @@ class SettingsCommonActivity : AppCompatActivity() {
launcher = LauncherWrapper(this, contract)
binding.apply {
-
settingsCommonLayout.updateLayoutParams {
topMargin = statusBarHeight
bottomMargin = navBarHeight
@@ -108,27 +110,30 @@ class SettingsCommonActivity : AppCompatActivity() {
commonSettingsBack.setOnClickListener {
onBackPressedDispatcher.onBackPressed()
}
- val exDns = listOf(
- "None",
- "Cloudflare",
- "Google",
- "AdGuard",
- "Quad9",
- "AliDNS",
- "DNSPod",
- "360",
- "Quad101",
- "Mullvad",
- "Controld",
- "Njalla",
- "Shecan",
- "Libre"
- )
+ val exDns =
+ listOf(
+ "None",
+ "Cloudflare",
+ "Google",
+ "AdGuard",
+ "Quad9",
+ "AliDNS",
+ "DNSPod",
+ "360",
+ "Quad101",
+ "Mullvad",
+ "Controld",
+ "Njalla",
+ "Shecan",
+ "Libre",
+ )
settingsExtensionDns.setText(exDns[PrefManager.getVal(PrefName.DohProvider)])
settingsExtensionDns.setAdapter(
ArrayAdapter(
- context, R.layout.item_dropdown, exDns
- )
+ context,
+ R.layout.item_dropdown,
+ exDns,
+ ),
)
settingsExtensionDns.setOnItemClickListener { _, _, i, _ ->
PrefManager.setVal(PrefName.DohProvider, i)
@@ -136,283 +141,287 @@ class SettingsCommonActivity : AppCompatActivity() {
restartApp()
}
- settingsRecyclerView.adapter = SettingsAdapter(
- arrayListOf(
- Settings(
- type = 1,
- name = getString(R.string.ui_settings),
- desc = getString(R.string.ui_settings_desc),
- icon = R.drawable.ic_round_auto_awesome_24,
- onClick = {
- startActivity(
- Intent(
- context,
- UserInterfaceSettingsActivity::class.java
+ settingsRecyclerView.adapter =
+ SettingsAdapter(
+ arrayListOf(
+ Settings(
+ type = 1,
+ name = getString(R.string.ui_settings),
+ desc = getString(R.string.ui_settings_desc),
+ icon = R.drawable.ic_round_auto_awesome_24,
+ onClick = {
+ startActivity(
+ Intent(
+ context,
+ UserInterfaceSettingsActivity::class.java,
+ ),
)
- )
- },
- isActivity = true
- ),
- Settings(
- type = 2,
- name = getString(R.string.open_animanga_directly),
- desc = getString(R.string.open_animanga_directly_info),
- icon = R.drawable.ic_round_search_24,
- isChecked = PrefManager.getVal(PrefName.AniMangaSearchDirect),
- switch = { isChecked, _ ->
- PrefManager.setVal(PrefName.AniMangaSearchDirect, isChecked)
- }
- ),
- Settings(
- type = 1,
- name = getString(R.string.download_manager_select),
- desc = getString(R.string.download_manager_select_desc),
- icon = R.drawable.ic_download_24,
- onClick = {
- val managers = arrayOf("Default", "1DM", "ADM")
- customAlertDialog().apply {
- setTitle(getString(R.string.download_manager))
- singleChoiceItems(
- managers,
- PrefManager.getVal(PrefName.DownloadManager)
- ) { count ->
- PrefManager.setVal(PrefName.DownloadManager, count)
- }
- show()
- }
- }
- ),
- Settings(
- type = 1,
- name = getString(R.string.app_lock),
- desc = getString(R.string.app_lock_desc),
- icon = R.drawable.ic_round_lock_open_24,
- onClick = {
- customAlertDialog().apply {
- val view = DialogSetPasswordBinding.inflate(layoutInflater)
- setTitle(R.string.app_lock)
- setCustomView(view.root)
- setPosButton(R.string.ok) {
- if (view.forgotPasswordCheckbox.isChecked) {
- PrefManager.setVal(PrefName.OverridePassword, true)
+ },
+ isActivity = true,
+ ),
+ Settings(
+ type = 1,
+ name = getString(R.string.download_manager_select),
+ desc = getString(R.string.download_manager_select_desc),
+ icon = R.drawable.ic_download_24,
+ onClick = {
+ val managers = arrayOf("Default", "1DM", "ADM")
+ customAlertDialog().apply {
+ setTitle(getString(R.string.download_manager))
+ singleChoiceItems(
+ managers,
+ PrefManager.getVal(PrefName.DownloadManager),
+ ) { count ->
+ PrefManager.setVal(PrefName.DownloadManager, count)
}
- val password = view.passwordInput.text.toString()
- val confirmPassword = view.confirmPasswordInput.text.toString()
- if (password == confirmPassword && password.isNotEmpty()) {
- PrefManager.setVal(PrefName.AppPassword, password)
- if (view.biometricCheckbox.isChecked) {
- val canBiometricPrompt =
- BiometricManager.from(applicationContext)
- .canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
-
- if (canBiometricPrompt) {
- val biometricPrompt =
- BiometricPromptUtils.createBiometricPrompt(this@SettingsCommonActivity) { _ ->
- val token = UUID.randomUUID().toString()
- PrefManager.setVal(
- PrefName.BiometricToken,
- token
- )
- toast(R.string.success)
- }
- val promptInfo =
- BiometricPromptUtils.createPromptInfo(this@SettingsCommonActivity)
- biometricPrompt.authenticate(promptInfo)
- }
-
- } else {
- PrefManager.setVal(PrefName.BiometricToken, "")
- toast(R.string.success)
+ show()
+ }
+ },
+ ),
+ Settings(
+ type = 1,
+ name = getString(R.string.app_lock),
+ desc = getString(R.string.app_lock_desc),
+ icon = R.drawable.ic_round_lock_open_24,
+ onClick = {
+ customAlertDialog().apply {
+ val view = DialogSetPasswordBinding.inflate(layoutInflater)
+ setTitle(R.string.app_lock)
+ setCustomView(view.root)
+ setPosButton(R.string.ok) {
+ if (view.forgotPasswordCheckbox.isChecked) {
+ PrefManager.setVal(PrefName.OverridePassword, true)
}
- } else {
- toast(R.string.password_mismatch)
- }
- }
- setNegButton(R.string.cancel)
- setNeutralButton(R.string.remove) {
- PrefManager.setVal(PrefName.AppPassword, "")
- PrefManager.setVal(PrefName.BiometricToken, "")
- PrefManager.setVal(PrefName.OverridePassword, false)
- toast(R.string.success)
- }
- setOnShowListener {
- view.passwordInput.requestFocus()
- val canAuthenticate =
- BiometricManager.from(applicationContext).canAuthenticate(
- BiometricManager.Authenticators.BIOMETRIC_WEAK
- ) == BiometricManager.BIOMETRIC_SUCCESS
- view.biometricCheckbox.isVisible = canAuthenticate
- view.biometricCheckbox.isChecked =
- PrefManager.getVal(PrefName.BiometricToken, "").isNotEmpty()
- view.forgotPasswordCheckbox.isChecked =
- PrefManager.getVal(PrefName.OverridePassword)
- }
- show()
- }
- }
+ val password = view.passwordInput.text.toString()
+ val confirmPassword =
+ view.confirmPasswordInput.text.toString()
+ if (password == confirmPassword && password.isNotEmpty()) {
+ PrefManager.setVal(PrefName.AppPassword, password)
+ if (view.biometricCheckbox.isChecked) {
+ val canBiometricPrompt =
+ BiometricManager
+ .from(applicationContext)
+ .canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) ==
+ BiometricManager.BIOMETRIC_SUCCESS
- ),
- Settings(
- type = 1,
- name = getString(R.string.backup_restore),
- desc = getString(R.string.backup_restore_desc),
- icon = R.drawable.backup_restore,
- onClick = {
- StoragePermissions.downloadsPermission(context)
- val selectedArray = mutableListOf(false)
- val filteredLocations = Location.entries.filter { it.exportable }
- selectedArray.addAll(List(filteredLocations.size - 1) { false })
- val dialog = AlertDialog.Builder(context, R.style.MyPopup)
- .setTitle(R.string.backup_restore).setMultiChoiceItems(
- filteredLocations.map { it.name }.toTypedArray(),
- selectedArray.toBooleanArray()
- ) { _, which, isChecked ->
- selectedArray[which] = isChecked
- }.setPositiveButton(R.string.button_restore) { dialog, _ ->
- openDocumentLauncher.launch(arrayOf("*/*"))
- dialog.dismiss()
- }.setNegativeButton(R.string.button_backup) { dialog, _ ->
- if (!selectedArray.contains(true)) {
- toast(R.string.no_location_selected)
- return@setNegativeButton
- }
- dialog.dismiss()
- val selected =
- filteredLocations.filterIndexed { index, _ -> selectedArray[index] }
- if (selected.contains(Location.Protected)) {
- passwordAlertDialog(true) { password ->
- if (password != null) {
- savePrefsToDownloads(
- "DantotsuSettings",
- PrefManager.exportAllPrefs(selected),
- context,
- password
- )
+ if (canBiometricPrompt) {
+ val biometricPrompt =
+ BiometricPromptUtils.createBiometricPrompt(
+ this@SettingsCommonActivity
+ ) { _ ->
+ val token = UUID.randomUUID().toString()
+ PrefManager.setVal(
+ PrefName.BiometricToken,
+ token,
+ )
+ toast(R.string.success)
+ }
+ val promptInfo =
+ BiometricPromptUtils.createPromptInfo(this@SettingsCommonActivity)
+ biometricPrompt.authenticate(promptInfo)
+ }
} else {
- toast(R.string.password_cannot_be_empty)
+ PrefManager.setVal(PrefName.BiometricToken, "")
+ toast(R.string.success)
}
+ } else {
+ toast(R.string.password_mismatch)
}
- } else {
- savePrefsToDownloads(
- "DantotsuSettings",
- PrefManager.exportAllPrefs(selected),
- context,
- null
- )
}
- }.setNeutralButton(R.string.cancel) { dialog, _ ->
- dialog.dismiss()
- }.create()
- dialog.window?.setDimAmount(0.8f)
- dialog.show()
- },
- ),
- Settings(
- type = 1,
- name = getString(R.string.change_download_location),
- desc = getString(R.string.change_download_location_desc),
- icon = R.drawable.ic_round_source_24,
- onClick = {
- context.customAlertDialog().apply {
- setTitle(R.string.change_download_location)
- setMessage(R.string.download_location_msg)
- setPosButton(R.string.ok) {
- val oldUri = PrefManager.getVal(PrefName.DownloadsDir)
- launcher.registerForCallback { success ->
- if (success) {
- toast(getString(R.string.please_wait))
- val newUri =
- PrefManager.getVal(PrefName.DownloadsDir)
- GlobalScope.launch(Dispatchers.IO) {
- Injekt.get().moveDownloadsDir(
- context, Uri.parse(oldUri), Uri.parse(newUri)
- ) { finished, message ->
- if (finished) {
- toast(getString(R.string.success))
- } else {
- toast(message)
- }
+ setNegButton(R.string.cancel)
+ setNeutralButton(R.string.remove) {
+ PrefManager.setVal(PrefName.AppPassword, "")
+ PrefManager.setVal(PrefName.BiometricToken, "")
+ PrefManager.setVal(PrefName.OverridePassword, false)
+ toast(R.string.success)
+ }
+ setOnShowListener {
+ view.passwordInput.requestFocus()
+ val canAuthenticate =
+ BiometricManager.from(applicationContext)
+ .canAuthenticate(
+ BiometricManager.Authenticators.BIOMETRIC_WEAK,
+ ) == BiometricManager.BIOMETRIC_SUCCESS
+ view.biometricCheckbox.isVisible = canAuthenticate
+ view.biometricCheckbox.isChecked =
+ PrefManager.getVal(PrefName.BiometricToken, "")
+ .isNotEmpty()
+ view.forgotPasswordCheckbox.isChecked =
+ PrefManager.getVal(PrefName.OverridePassword)
+ }
+ show()
+ }
+ },
+ ),
+ Settings(
+ type = 1,
+ name = getString(R.string.backup_restore),
+ desc = getString(R.string.backup_restore_desc),
+ icon = R.drawable.backup_restore,
+ onClick = {
+ StoragePermissions.downloadsPermission(context)
+ val filteredLocations = Location.entries.filter { it.exportable }
+ val selectedArray = BooleanArray(filteredLocations.size) { false }
+ context.customAlertDialog().apply {
+ setTitle(R.string.backup_restore)
+ multiChoiceItems(
+ filteredLocations.map { it.name }.toTypedArray(),
+ selectedArray,
+ ) { updatedSelection ->
+ for (i in updatedSelection.indices) {
+ selectedArray[i] = updatedSelection[i]
+ }
+ }
+ setPosButton(R.string.button_restore) {
+ openDocumentLauncher.launch(arrayOf("*/*"))
+ }
+ setNegButton(R.string.button_backup) {
+ if (!selectedArray.contains(true)) {
+ toast(R.string.no_location_selected)
+ return@setNegButton
+ }
+ val selected =
+ filteredLocations.filterIndexed { index, _ -> selectedArray[index] }
+ if (selected.contains(Location.Protected)) {
+ passwordAlertDialog(true) { password ->
+ if (password != null) {
+ savePrefsToDownloads(
+ "DantotsuSettings",
+ PrefManager.exportAllPrefs(selected),
+ context,
+ password,
+ )
+ } else {
+ toast(R.string.password_cannot_be_empty)
}
}
} else {
- toast(getString(R.string.error))
+ savePrefsToDownloads(
+ "DantotsuSettings",
+ PrefManager.exportAllPrefs(selected),
+ context,
+ null,
+ )
}
}
- launcher.launch()
+ setNeutralButton(R.string.cancel) {}
+ show()
}
- setNegButton(R.string.cancel)
- show()
- }
- }
- ),
- Settings(
- type = 2,
- name = getString(R.string.always_continue_content),
- desc = getString(R.string.always_continue_content_desc),
- icon = R.drawable.ic_round_delete_24,
- isChecked = PrefManager.getVal(PrefName.ContinueMedia),
- switch = { isChecked, _ ->
- PrefManager.setVal(PrefName.ContinueMedia, isChecked)
- }
- ),
- Settings(
- type = 2,
- name = getString(R.string.hide_private),
- desc = getString(R.string.hide_private_desc),
- icon = R.drawable.ic_round_remove_red_eye_24,
- isChecked = PrefManager.getVal(PrefName.HidePrivate),
- switch = { isChecked, _ ->
- PrefManager.setVal(PrefName.HidePrivate, isChecked)
- restartApp()
- }
- ),
- Settings(
- type = 2,
- name = getString(R.string.search_source_list),
- desc = getString(R.string.search_source_list_desc),
- icon = R.drawable.ic_round_search_sources_24,
- isChecked = PrefManager.getVal(PrefName.SearchSources),
- switch = { isChecked, _ ->
- PrefManager.setVal(PrefName.SearchSources, isChecked)
- }
- ),
- Settings(
- type = 2,
- name = getString(R.string.recentlyListOnly),
- desc = getString(R.string.recentlyListOnly_desc),
- icon = R.drawable.ic_round_new_releases_24,
- isChecked = PrefManager.getVal(PrefName.RecentlyListOnly),
- switch = { isChecked, _ ->
- PrefManager.setVal(PrefName.RecentlyListOnly, isChecked)
- }
- ),
- Settings(
- type = 2,
- name = getString(R.string.adult_only_content),
- desc = getString(R.string.adult_only_content_desc),
- icon = R.drawable.ic_round_nsfw_24,
- isChecked = PrefManager.getVal(PrefName.AdultOnly),
- switch = { isChecked, _ ->
- PrefManager.setVal(PrefName.AdultOnly, isChecked)
- restartApp()
- },
- isVisible = Anilist.adult
-
+ },
+ ),
+ Settings(
+ type = 1,
+ name = getString(R.string.change_download_location),
+ desc = getString(R.string.change_download_location_desc),
+ icon = R.drawable.ic_round_source_24,
+ onClick = {
+ context.customAlertDialog().apply {
+ setTitle(R.string.change_download_location)
+ setMessage(R.string.download_location_msg)
+ setPosButton(R.string.ok) {
+ val oldUri =
+ PrefManager.getVal(PrefName.DownloadsDir)
+ launcher.registerForCallback { success ->
+ if (success) {
+ toast(getString(R.string.please_wait))
+ val newUri =
+ PrefManager.getVal(PrefName.DownloadsDir)
+ GlobalScope.launch(Dispatchers.IO) {
+ Injekt.get().moveDownloadsDir(
+ context,
+ Uri.parse(oldUri),
+ Uri.parse(newUri),
+ ) { finished, message ->
+ if (finished) {
+ toast(getString(R.string.success))
+ } else {
+ toast(message)
+ }
+ }
+ }
+ } else {
+ toast(getString(R.string.error))
+ }
+ }
+ launcher.launch()
+ }
+ setNegButton(R.string.cancel)
+ show()
+ }
+ },
+ ),
+ Settings(
+ type = 2,
+ name = getString(R.string.always_continue_content),
+ desc = getString(R.string.always_continue_content_desc),
+ icon = R.drawable.ic_round_delete_24,
+ isChecked = PrefManager.getVal(PrefName.ContinueMedia),
+ switch = { isChecked, _ ->
+ PrefManager.setVal(PrefName.ContinueMedia, isChecked)
+ },
+ ),
+ Settings(
+ type = 2,
+ name = getString(R.string.hide_private),
+ desc = getString(R.string.hide_private_desc),
+ icon = R.drawable.ic_round_remove_red_eye_24,
+ isChecked = PrefManager.getVal(PrefName.HidePrivate),
+ switch = { isChecked, _ ->
+ PrefManager.setVal(PrefName.HidePrivate, isChecked)
+ restartApp()
+ },
+ ),
+ Settings(
+ type = 2,
+ name = getString(R.string.search_source_list),
+ desc = getString(R.string.search_source_list_desc),
+ icon = R.drawable.ic_round_search_sources_24,
+ isChecked = PrefManager.getVal(PrefName.SearchSources),
+ switch = { isChecked, _ ->
+ PrefManager.setVal(PrefName.SearchSources, isChecked)
+ },
+ ),
+ Settings(
+ type = 2,
+ name = getString(R.string.recentlyListOnly),
+ desc = getString(R.string.recentlyListOnly_desc),
+ icon = R.drawable.ic_round_new_releases_24,
+ isChecked = PrefManager.getVal(PrefName.RecentlyListOnly),
+ switch = { isChecked, _ ->
+ PrefManager.setVal(PrefName.RecentlyListOnly, isChecked)
+ },
+ ),
+ Settings(
+ type = 2,
+ name = getString(R.string.adult_only_content),
+ desc = getString(R.string.adult_only_content_desc),
+ icon = R.drawable.ic_round_nsfw_24,
+ isChecked = PrefManager.getVal(PrefName.AdultOnly),
+ switch = { isChecked, _ ->
+ PrefManager.setVal(PrefName.AdultOnly, isChecked)
+ restartApp()
+ },
+ isVisible = Anilist.adult,
+ ),
),
)
- )
settingsRecyclerView.apply {
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
setHasFixedSize(true)
}
- var previousStart: View = when (PrefManager.getVal(PrefName.DefaultStartUpTab)) {
- 0 -> uiSettingsAnime
- 1 -> uiSettingsHome
- 2 -> uiSettingsManga
- else -> uiSettingsHome
- }
+ var previousStart: View =
+ when (PrefManager.getVal(PrefName.DefaultStartUpTab)) {
+ 0 -> uiSettingsAnime
+ 1 -> uiSettingsHome
+ 2 -> uiSettingsManga
+ else -> uiSettingsHome
+ }
previousStart.alpha = 1f
- fun uiDefault(mode: Int, current: View) {
+
+ fun uiDefault(
+ mode: Int,
+ current: View,
+ ) {
previousStart.alpha = 0.33f
previousStart = current
current.alpha = 1f
@@ -431,11 +440,13 @@ class SettingsCommonActivity : AppCompatActivity() {
uiSettingsManga.setOnClickListener {
uiDefault(2, it)
}
-
}
}
- private fun passwordAlertDialog(isExporting: Boolean, callback: (CharArray?) -> Unit) {
+ private fun passwordAlertDialog(
+ isExporting: Boolean,
+ callback: (CharArray?) -> Unit,
+ ) {
val password = CharArray(16).apply { fill('0') }
// Inflate the dialog layout
@@ -445,7 +456,9 @@ class SettingsCommonActivity : AppCompatActivity() {
box.setSingleLine()
val dialog =
- AlertDialog.Builder(this, R.style.MyPopup).setTitle(getString(R.string.enter_password))
+ AlertDialog
+ .Builder(this, R.style.MyPopup)
+ .setTitle(getString(R.string.enter_password))
.setView(dialogView.root)
.setPositiveButton(R.string.ok, null)
.setNegativeButton(R.string.cancel) { dialog, _ ->
@@ -457,7 +470,10 @@ class SettingsCommonActivity : AppCompatActivity() {
fun handleOkAction() {
val editText = dialogView.userAgentTextBox
if (editText.text?.isNotBlank() == true) {
- editText.text?.toString()?.trim()?.toCharArray(password)
+ editText.text
+ ?.toString()
+ ?.trim()
+ ?.toCharArray(password)
dialog.dismiss()
callback(password)
} else {
@@ -473,18 +489,20 @@ class SettingsCommonActivity : AppCompatActivity() {
}
}
dialogView.subtitle.visibility = View.VISIBLE
- if (!isExporting) dialogView.subtitle.text =
- getString(R.string.enter_password_to_decrypt_file)
+ if (!isExporting) {
+ dialogView.subtitle.text =
+ getString(R.string.enter_password_to_decrypt_file)
+ }
-
- dialog.window?.setDimAmount(0.8f)
+ dialog.window?.apply {
+ setDimAmount(0.8f)
+ attributes.windowAnimations = android.R.style.Animation_Dialog
+ }
dialog.show()
// Override the positive button here
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
handleOkAction()
}
-
}
-
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt
index ae3f08f5..f1225ed4 100644
--- a/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SettingsNotificationActivity.kt
@@ -128,26 +128,27 @@ class SettingsNotificationActivity : AppCompatActivity() {
PrefManager.getVal>(PrefName.AnilistFilteredTypes)
.toMutableSet()
val selected = types.map { filteredTypes.contains(it) }.toBooleanArray()
- val dialog = AlertDialog.Builder(context, R.style.MyPopup)
- .setTitle(R.string.anilist_notification_filters)
- .setMultiChoiceItems(
+ context.customAlertDialog().apply {
+ setTitle(R.string.anilist_notification_filters)
+ multiChoiceItems(
types.map { name ->
name.replace("_", " ").lowercase().replaceFirstChar {
if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString()
}
}.toTypedArray(),
selected
- ) { _, which, isChecked ->
- val type = types[which]
- if (isChecked) {
- filteredTypes.add(type)
- } else {
- filteredTypes.remove(type)
+ ) { updatedSelected ->
+ types.forEachIndexed { index, type ->
+ if (updatedSelected[index]) {
+ filteredTypes.add(type)
+ } else {
+ filteredTypes.remove(type)
+ }
}
PrefManager.setVal(PrefName.AnilistFilteredTypes, filteredTypes)
- }.create()
- dialog.window?.setDimAmount(0.8f)
- dialog.show()
+ }
+ show()
+ }
}
),
@@ -160,27 +161,24 @@ class SettingsNotificationActivity : AppCompatActivity() {
desc = getString(R.string.anilist_notifications_checking_time_desc),
icon = R.drawable.ic_round_notifications_none_24,
onClick = {
- val selected =
- PrefManager.getVal(PrefName.AnilistNotificationInterval)
- val dialog = AlertDialog.Builder(context, R.style.MyPopup)
- .setTitle(R.string.subscriptions_checking_time)
- .setSingleChoiceItems(
+ context.customAlertDialog().apply {
+ setTitle(R.string.subscriptions_checking_time)
+ singleChoiceItems(
aItems.toTypedArray(),
- selected
- ) { dialog, i ->
+ PrefManager.getVal(PrefName.AnilistNotificationInterval)
+ ) { i ->
PrefManager.setVal(PrefName.AnilistNotificationInterval, i)
it.settingsTitle.text =
getString(
R.string.anilist_notifications_checking_time,
aItems[i]
)
- dialog.dismiss()
TaskScheduler.create(
context, PrefManager.getVal(PrefName.UseAlarmManager)
).scheduleAllTasks(context)
- }.create()
- dialog.window?.setDimAmount(0.8f)
- dialog.show()
+ }
+ show()
+ }
}
),
Settings(
@@ -192,27 +190,24 @@ class SettingsNotificationActivity : AppCompatActivity() {
desc = getString(R.string.comment_notification_checking_time_desc),
icon = R.drawable.ic_round_notifications_none_24,
onClick = {
- val selected =
- PrefManager.getVal(PrefName.CommentNotificationInterval)
- val dialog = AlertDialog.Builder(context, R.style.MyPopup)
- .setTitle(R.string.subscriptions_checking_time)
- .setSingleChoiceItems(
+ context.customAlertDialog().apply {
+ setTitle(R.string.subscriptions_checking_time)
+ singleChoiceItems(
cItems.toTypedArray(),
- selected
- ) { dialog, i ->
+ PrefManager.getVal(PrefName.CommentNotificationInterval)
+ ) { i ->
PrefManager.setVal(PrefName.CommentNotificationInterval, i)
it.settingsTitle.text =
getString(
R.string.comment_notification_checking_time,
cItems[i]
)
- dialog.dismiss()
TaskScheduler.create(
context, PrefManager.getVal(PrefName.UseAlarmManager)
).scheduleAllTasks(context)
- }.create()
- dialog.window?.setDimAmount(0.8f)
- dialog.show()
+ }
+ show()
+ }
}
),
Settings(
@@ -239,10 +234,10 @@ class SettingsNotificationActivity : AppCompatActivity() {
isChecked = PrefManager.getVal(PrefName.UseAlarmManager),
switch = { isChecked, view ->
if (isChecked) {
- val alertDialog = AlertDialog.Builder(context, R.style.MyPopup)
- .setTitle(R.string.use_alarm_manager)
- .setMessage(R.string.use_alarm_manager_confirm)
- .setPositiveButton(R.string.use) { dialog, _ ->
+ context.customAlertDialog().apply {
+ setTitle(R.string.use_alarm_manager)
+ setMessage(R.string.use_alarm_manager_confirm)
+ setPosButton(R.string.use) {
PrefManager.setVal(PrefName.UseAlarmManager, true)
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
if (!(getSystemService(Context.ALARM_SERVICE) as AlarmManager).canScheduleExactAlarms()) {
@@ -252,15 +247,13 @@ class SettingsNotificationActivity : AppCompatActivity() {
view.settingsButton.isChecked = true
}
}
- dialog.dismiss()
- }.setNegativeButton(R.string.cancel) { dialog, _ ->
+ }
+ setNegButton(R.string.cancel) {
view.settingsButton.isChecked = false
PrefManager.setVal(PrefName.UseAlarmManager, false)
-
- dialog.dismiss()
- }.create()
- alertDialog.window?.setDimAmount(0.8f)
- alertDialog.show()
+ }
+ show()
+ }
} else {
PrefManager.setVal(PrefName.UseAlarmManager, false)
TaskScheduler.create(context, true).cancelAllTasks()
@@ -277,4 +270,4 @@ class SettingsNotificationActivity : AppCompatActivity() {
}
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/ani/dantotsu/settings/SettingsThemeActivity.kt b/app/src/main/java/ani/dantotsu/settings/SettingsThemeActivity.kt
index 8133640a..648c26bd 100644
--- a/app/src/main/java/ani/dantotsu/settings/SettingsThemeActivity.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SettingsThemeActivity.kt
@@ -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(
diff --git a/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt b/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt
index 9fa7552e..188da9d3 100644
--- a/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt
+++ b/app/src/main/java/ani/dantotsu/settings/SubscriptionsBottomDialog.kt
@@ -52,14 +52,15 @@ class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
}
groupedSubscriptions.forEach { (parserName, mediaList) ->
- adapter.add(SubscriptionSource(
- parserName,
- mediaList.toMutableList(),
- adapter,
- getParserIcon(parserName)
- ) { group ->
- adapter.remove(group)
- })
+ adapter.add(
+ SubscriptionSource(
+ parserName,
+ mediaList.toMutableList(),
+ adapter,
+ getParserIcon(parserName)
+ ) { group ->
+ adapter.remove(group)
+ })
}
}
diff --git a/app/src/main/res/layout/dialog_layout.xml b/app/src/main/res/layout/dialog_layout.xml
index 3db06395..3c9df3b8 100644
--- a/app/src/main/res/layout/dialog_layout.xml
+++ b/app/src/main/res/layout/dialog_layout.xml
@@ -8,7 +8,7 @@
android:padding="16dp">
@@ -160,8 +160,8 @@
android:orientation="horizontal">
-
+ tools:text="Number" />
+
+
+
+
+
+
+
+