Compare commits
No commits in common. "a93b4f5b1102f781fe2b1b1e51adacf2d83fdb71" and "f606bef2a5199d0af8ae0d26b6299defe8946091" have entirely different histories.
a93b4f5b11
...
f606bef2a5
19 changed files with 1324 additions and 1553 deletions
|
@ -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!
|
> **Dantotsu (断トツ; Dan-totsu)** literally means "the best of the best" in Japanese. Try it out for yourself and be the judge!
|
||||||
|
|
||||||
<a href="https://www.buymeacoffee.com/rebelonion"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=rebelonion&button_colour=FFDD00&font_colour=000000&font_family=Cookie&outline_colour=000000&coffee_colour=ffffff" /></a>
|
<a href="https://www.buymeacoffee.com/rebelonion"><img src="https://img.buymeacoffee.com/button-api/?text=Buy me a coffee&emoji=&slug=rebelonion&button_colour=FFDD00&font_colour=030201&font_family=Poppins&outline_colour=000000&coffee_colour=ffffff" /></a>
|
||||||
|
|
||||||
## Terms of Use
|
## Terms of Use
|
||||||
By downloading, installing, or using this application, you agree to:
|
By downloading, installing, or using this application, you agree to:
|
||||||
|
|
|
@ -17,8 +17,9 @@ android {
|
||||||
applicationId "ani.dantotsu"
|
applicationId "ani.dantotsu"
|
||||||
minSdk 21
|
minSdk 21
|
||||||
targetSdk 35
|
targetSdk 35
|
||||||
versionName "3.2.2"
|
versionCode((System.currentTimeMillis() / 60000).toInteger())
|
||||||
versionCode 300200200
|
versionName "3.2.0"
|
||||||
|
versionCode 300200000
|
||||||
signingConfig signingConfigs.debug
|
signingConfig signingConfigs.debug
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
<?xml version="1.0" encoding="utf-8"?>
|
`<?xml version="1.0" encoding="utf-8"?>
|
||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
xmlns:tools="http://schemas.android.com/tools">
|
xmlns:tools="http://schemas.android.com/tools">
|
||||||
|
|
||||||
|
|
|
@ -253,7 +253,7 @@ data class MediaStreamingEpisode(
|
||||||
|
|
||||||
// The site location of the streaming episode
|
// The site location of the streaming episode
|
||||||
@SerialName("site") var site: String?,
|
@SerialName("site") var site: String?,
|
||||||
) : java.io.Serializable
|
)
|
||||||
|
|
||||||
@Serializable
|
@Serializable
|
||||||
data class MediaCoverImage(
|
data class MediaCoverImage(
|
||||||
|
|
|
@ -50,9 +50,8 @@ open class RPC(val token: String, val coroutineContext: CoroutineContext) {
|
||||||
val assetApi = RPCExternalAsset(data.applicationId, token!!, client, json)
|
val assetApi = RPCExternalAsset(data.applicationId, token!!, client, json)
|
||||||
suspend fun String.discordUrl() = assetApi.getDiscordUri(this)
|
suspend fun String.discordUrl() = assetApi.getDiscordUri(this)
|
||||||
|
|
||||||
return json.encodeToString(
|
return json.encodeToString(Presence.Response(
|
||||||
Presence.Response(
|
3,
|
||||||
3,
|
|
||||||
Presence(
|
Presence(
|
||||||
activities = listOf(
|
activities = listOf(
|
||||||
Activity(
|
Activity(
|
||||||
|
|
|
@ -232,18 +232,12 @@ class MangaDownloaderService : Service() {
|
||||||
image.page,
|
image.page,
|
||||||
image.source
|
image.source
|
||||||
)
|
)
|
||||||
if (bitmap == null) {
|
|
||||||
snackString("${task.chapter} - Retrying to download page ${index.ofLength(3)}, attempt ${retryCount + 1}.")
|
|
||||||
}
|
|
||||||
retryCount++
|
retryCount++
|
||||||
}
|
}
|
||||||
|
|
||||||
if (bitmap == null) {
|
if (bitmap != null) {
|
||||||
outputDir.deleteRecursively(this@MangaDownloaderService, false)
|
saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
|
||||||
throw Exception("${task.chapter} - Unable to download all pages after $retryCount attempts. Try again.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
saveToDisk("${index.ofLength(3)}.jpg", outputDir, bitmap)
|
|
||||||
farthest++
|
farthest++
|
||||||
|
|
||||||
builder.setProgress(task.imageData.size, farthest, false)
|
builder.setProgress(task.imageData.size, farthest, false)
|
||||||
|
|
File diff suppressed because it is too large
Load diff
|
@ -7,11 +7,9 @@ import android.view.View
|
||||||
import android.view.ViewGroup
|
import android.view.ViewGroup
|
||||||
import android.widget.ArrayAdapter
|
import android.widget.ArrayAdapter
|
||||||
import android.widget.CheckBox
|
import android.widget.CheckBox
|
||||||
import android.widget.EditText
|
|
||||||
import android.widget.ImageButton
|
import android.widget.ImageButton
|
||||||
import android.widget.LinearLayout
|
import android.widget.LinearLayout
|
||||||
import android.widget.NumberPicker
|
import android.widget.NumberPicker
|
||||||
import android.widget.TextView
|
|
||||||
import androidx.core.content.ContextCompat
|
import androidx.core.content.ContextCompat
|
||||||
import androidx.core.content.ContextCompat.getString
|
import androidx.core.content.ContextCompat.getString
|
||||||
import androidx.core.content.ContextCompat.startActivity
|
import androidx.core.content.ContextCompat.startActivity
|
||||||
|
@ -267,22 +265,19 @@ class MangaReadAdapter(
|
||||||
}
|
}
|
||||||
|
|
||||||
// Multi download
|
// Multi download
|
||||||
//downloadNo.text = "0"
|
downloadNo.text = "0"
|
||||||
mediaDownloadTop.setOnClickListener {
|
mediaDownloadTop.setOnClickListener {
|
||||||
|
// Alert dialog asking for the number of chapters to download
|
||||||
fragment.requireContext().customAlertDialog().apply {
|
fragment.requireContext().customAlertDialog().apply {
|
||||||
setTitle("Multi Chapter Downloader")
|
setTitle("Multi Chapter Downloader")
|
||||||
setMessage("Enter the number of chapters to download")
|
setMessage("Enter the number of chapters to download")
|
||||||
val input = View.inflate(currContext(), R.layout.dialog_layout, null)
|
val input = NumberPicker(currContext())
|
||||||
val editText = input.findViewById<EditText>(R.id.downloadNo)
|
input.minValue = 1
|
||||||
|
input.maxValue = 20
|
||||||
|
input.value = 1
|
||||||
setCustomView(input)
|
setCustomView(input)
|
||||||
setPosButton(R.string.ok) {
|
setPosButton(R.string.ok) {
|
||||||
val value = editText.text.toString().toIntOrNull()
|
downloadNo.text = "${input.value}"
|
||||||
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)
|
setNegButton(R.string.cancel)
|
||||||
show()
|
show()
|
||||||
|
@ -387,9 +382,8 @@ class MangaReadAdapter(
|
||||||
setCustomView(root)
|
setCustomView(root)
|
||||||
setPosButton("OK") {
|
setPosButton("OK") {
|
||||||
if (run) fragment.onIconPressed(style, reversed)
|
if (run) fragment.onIconPressed(style, reversed)
|
||||||
val value = downloadNo.text.toString().toIntOrNull()
|
if (downloadNo.text != "0") {
|
||||||
if (value != null && value > 0) {
|
fragment.multiDownload(downloadNo.text.toString().toInt())
|
||||||
fragment.multiDownload(value)
|
|
||||||
}
|
}
|
||||||
if (refresh) fragment.loadChapters(source, true)
|
if (refresh) fragment.loadChapters(source, true)
|
||||||
}
|
}
|
||||||
|
|
|
@ -474,7 +474,7 @@ open class MangaReadFragment : Fragment(), ScanlatorSelectionListener {
|
||||||
scanlator = chapter.scanlator ?: "Unknown",
|
scanlator = chapter.scanlator ?: "Unknown",
|
||||||
imageData = images,
|
imageData = images,
|
||||||
sourceMedia = media,
|
sourceMedia = media,
|
||||||
retries = 25,
|
retries = 2,
|
||||||
simultaneousDownloads = 2
|
simultaneousDownloads = 2
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
|
@ -24,11 +24,11 @@ class CrashActivity : AppCompatActivity() {
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
ThemeManager(this).applyTheme()
|
ThemeManager(this).applyTheme()
|
||||||
initActivity(this)
|
initActivity(this)
|
||||||
|
binding = ActivityCrashBinding.inflate(layoutInflater)
|
||||||
window.setFlags(
|
window.setFlags(
|
||||||
WindowManager.LayoutParams.FLAG_SECURE,
|
WindowManager.LayoutParams.FLAG_SECURE,
|
||||||
WindowManager.LayoutParams.FLAG_SECURE
|
WindowManager.LayoutParams.FLAG_SECURE
|
||||||
)
|
)
|
||||||
binding = ActivityCrashBinding.inflate(layoutInflater)
|
|
||||||
setContentView(binding.root)
|
setContentView(binding.root)
|
||||||
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
binding.root.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
topMargin = statusBarHeight
|
topMargin = statusBarHeight
|
||||||
|
|
|
@ -19,7 +19,6 @@ import ani.dantotsu.settings.saving.PrefManager
|
||||||
import ani.dantotsu.settings.saving.PrefName
|
import ani.dantotsu.settings.saving.PrefName
|
||||||
import ani.dantotsu.statusBarHeight
|
import ani.dantotsu.statusBarHeight
|
||||||
import ani.dantotsu.themes.ThemeManager
|
import ani.dantotsu.themes.ThemeManager
|
||||||
import ani.dantotsu.util.customAlertDialog
|
|
||||||
import uy.kohesive.injekt.Injekt
|
import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
|
|
||||||
|
@ -58,16 +57,23 @@ class SettingsAnimeActivity : AppCompatActivity() {
|
||||||
desc = getString(R.string.purge_anime_downloads_desc),
|
desc = getString(R.string.purge_anime_downloads_desc),
|
||||||
icon = R.drawable.ic_round_delete_24,
|
icon = R.drawable.ic_round_delete_24,
|
||||||
onClick = {
|
onClick = {
|
||||||
context.customAlertDialog().apply {
|
val dialog = AlertDialog.Builder(context, R.style.MyPopup)
|
||||||
setTitle(R.string.purge_anime_downloads)
|
.setTitle(R.string.purge_anime_downloads)
|
||||||
setMessage(R.string.purge_confirm, getString(R.string.anime))
|
.setMessage(
|
||||||
setPosButton(R.string.yes, onClick = {
|
getString(
|
||||||
|
R.string.purge_confirm,
|
||||||
|
getString(R.string.anime)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.setPositiveButton(R.string.yes) { dialog, _ ->
|
||||||
val downloadsManager = Injekt.get<DownloadsManager>()
|
val downloadsManager = Injekt.get<DownloadsManager>()
|
||||||
downloadsManager.purgeDownloads(MediaType.ANIME)
|
downloadsManager.purgeDownloads(MediaType.ANIME)
|
||||||
})
|
dialog.dismiss()
|
||||||
setNegButton(R.string.no)
|
}.setNegativeButton(R.string.no) { dialog, _ ->
|
||||||
show()
|
dialog.dismiss()
|
||||||
}
|
}.create()
|
||||||
|
dialog.window?.setDimAmount(0.8f)
|
||||||
|
dialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
),
|
),
|
||||||
|
@ -137,4 +143,4 @@ class SettingsAnimeActivity : AppCompatActivity() {
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -45,6 +45,7 @@ import uy.kohesive.injekt.Injekt
|
||||||
import uy.kohesive.injekt.api.get
|
import uy.kohesive.injekt.api.get
|
||||||
import java.util.UUID
|
import java.util.UUID
|
||||||
|
|
||||||
|
|
||||||
class SettingsCommonActivity : AppCompatActivity() {
|
class SettingsCommonActivity : AppCompatActivity() {
|
||||||
private lateinit var binding: ActivitySettingsCommonBinding
|
private lateinit var binding: ActivitySettingsCommonBinding
|
||||||
private lateinit var launcher: LauncherWrapper
|
private lateinit var launcher: LauncherWrapper
|
||||||
|
@ -61,27 +62,23 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||||
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
|
registerForActivityResult(ActivityResultContracts.OpenDocument()) { uri ->
|
||||||
if (uri != null) {
|
if (uri != null) {
|
||||||
try {
|
try {
|
||||||
val jsonString =
|
val jsonString = contentResolver.openInputStream(uri)?.readBytes()
|
||||||
contentResolver.openInputStream(uri)?.readBytes()
|
?: throw Exception("Error reading file")
|
||||||
?: throw Exception("Error reading file")
|
|
||||||
val name = DocumentFile.fromSingleUri(this, uri)?.name ?: "settings"
|
val name = DocumentFile.fromSingleUri(this, uri)?.name ?: "settings"
|
||||||
// .sani is encrypted, .ani is not
|
//.sani is encrypted, .ani is not
|
||||||
if (name.endsWith(".sani")) {
|
if (name.endsWith(".sani")) {
|
||||||
passwordAlertDialog(false) { password ->
|
passwordAlertDialog(false) { password ->
|
||||||
if (password != null) {
|
if (password != null) {
|
||||||
val salt = jsonString.copyOfRange(0, 16)
|
val salt = jsonString.copyOfRange(0, 16)
|
||||||
val encrypted = jsonString.copyOfRange(16, jsonString.size)
|
val encrypted = jsonString.copyOfRange(16, jsonString.size)
|
||||||
val decryptedJson =
|
val decryptedJson = try {
|
||||||
try {
|
PreferenceKeystore.decryptWithPassword(
|
||||||
PreferenceKeystore.decryptWithPassword(
|
password, encrypted, salt
|
||||||
password,
|
)
|
||||||
encrypted,
|
} catch (e: Exception) {
|
||||||
salt,
|
toast(getString(R.string.incorrect_password))
|
||||||
)
|
return@passwordAlertDialog
|
||||||
} catch (e: Exception) {
|
}
|
||||||
toast(getString(R.string.incorrect_password))
|
|
||||||
return@passwordAlertDialog
|
|
||||||
}
|
|
||||||
if (PreferencePackager.unpack(decryptedJson)) restartApp()
|
if (PreferencePackager.unpack(decryptedJson)) restartApp()
|
||||||
} else {
|
} else {
|
||||||
toast(getString(R.string.password_cannot_be_empty))
|
toast(getString(R.string.password_cannot_be_empty))
|
||||||
|
@ -103,6 +100,7 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||||
launcher = LauncherWrapper(this, contract)
|
launcher = LauncherWrapper(this, contract)
|
||||||
|
|
||||||
binding.apply {
|
binding.apply {
|
||||||
|
|
||||||
settingsCommonLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
settingsCommonLayout.updateLayoutParams<ViewGroup.MarginLayoutParams> {
|
||||||
topMargin = statusBarHeight
|
topMargin = statusBarHeight
|
||||||
bottomMargin = navBarHeight
|
bottomMargin = navBarHeight
|
||||||
|
@ -110,30 +108,27 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||||
commonSettingsBack.setOnClickListener {
|
commonSettingsBack.setOnClickListener {
|
||||||
onBackPressedDispatcher.onBackPressed()
|
onBackPressedDispatcher.onBackPressed()
|
||||||
}
|
}
|
||||||
val exDns =
|
val exDns = listOf(
|
||||||
listOf(
|
"None",
|
||||||
"None",
|
"Cloudflare",
|
||||||
"Cloudflare",
|
"Google",
|
||||||
"Google",
|
"AdGuard",
|
||||||
"AdGuard",
|
"Quad9",
|
||||||
"Quad9",
|
"AliDNS",
|
||||||
"AliDNS",
|
"DNSPod",
|
||||||
"DNSPod",
|
"360",
|
||||||
"360",
|
"Quad101",
|
||||||
"Quad101",
|
"Mullvad",
|
||||||
"Mullvad",
|
"Controld",
|
||||||
"Controld",
|
"Njalla",
|
||||||
"Njalla",
|
"Shecan",
|
||||||
"Shecan",
|
"Libre"
|
||||||
"Libre",
|
)
|
||||||
)
|
|
||||||
settingsExtensionDns.setText(exDns[PrefManager.getVal(PrefName.DohProvider)])
|
settingsExtensionDns.setText(exDns[PrefManager.getVal(PrefName.DohProvider)])
|
||||||
settingsExtensionDns.setAdapter(
|
settingsExtensionDns.setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(
|
||||||
context,
|
context, R.layout.item_dropdown, exDns
|
||||||
R.layout.item_dropdown,
|
)
|
||||||
exDns,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
settingsExtensionDns.setOnItemClickListener { _, _, i, _ ->
|
settingsExtensionDns.setOnItemClickListener { _, _, i, _ ->
|
||||||
PrefManager.setVal(PrefName.DohProvider, i)
|
PrefManager.setVal(PrefName.DohProvider, i)
|
||||||
|
@ -141,287 +136,283 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||||
restartApp()
|
restartApp()
|
||||||
}
|
}
|
||||||
|
|
||||||
settingsRecyclerView.adapter =
|
settingsRecyclerView.adapter = SettingsAdapter(
|
||||||
SettingsAdapter(
|
arrayListOf(
|
||||||
arrayListOf(
|
Settings(
|
||||||
Settings(
|
type = 1,
|
||||||
type = 1,
|
name = getString(R.string.ui_settings),
|
||||||
name = getString(R.string.ui_settings),
|
desc = getString(R.string.ui_settings_desc),
|
||||||
desc = getString(R.string.ui_settings_desc),
|
icon = R.drawable.ic_round_auto_awesome_24,
|
||||||
icon = R.drawable.ic_round_auto_awesome_24,
|
onClick = {
|
||||||
onClick = {
|
startActivity(
|
||||||
startActivity(
|
Intent(
|
||||||
Intent(
|
context,
|
||||||
context,
|
UserInterfaceSettingsActivity::class.java
|
||||||
UserInterfaceSettingsActivity::class.java,
|
|
||||||
),
|
|
||||||
)
|
)
|
||||||
},
|
)
|
||||||
isActivity = true,
|
},
|
||||||
),
|
isActivity = true
|
||||||
Settings(
|
),
|
||||||
type = 1,
|
Settings(
|
||||||
name = getString(R.string.download_manager_select),
|
type = 2,
|
||||||
desc = getString(R.string.download_manager_select_desc),
|
name = getString(R.string.open_animanga_directly),
|
||||||
icon = R.drawable.ic_download_24,
|
desc = getString(R.string.open_animanga_directly_info),
|
||||||
onClick = {
|
icon = R.drawable.ic_round_search_24,
|
||||||
val managers = arrayOf("Default", "1DM", "ADM")
|
isChecked = PrefManager.getVal(PrefName.AniMangaSearchDirect),
|
||||||
customAlertDialog().apply {
|
switch = { isChecked, _ ->
|
||||||
setTitle(getString(R.string.download_manager))
|
PrefManager.setVal(PrefName.AniMangaSearchDirect, isChecked)
|
||||||
singleChoiceItems(
|
}
|
||||||
managers,
|
),
|
||||||
PrefManager.getVal(PrefName.DownloadManager),
|
Settings(
|
||||||
) { count ->
|
type = 1,
|
||||||
PrefManager.setVal(PrefName.DownloadManager, count)
|
name = getString(R.string.download_manager_select),
|
||||||
}
|
desc = getString(R.string.download_manager_select_desc),
|
||||||
show()
|
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),
|
Settings(
|
||||||
desc = getString(R.string.app_lock_desc),
|
type = 1,
|
||||||
icon = R.drawable.ic_round_lock_open_24,
|
name = getString(R.string.app_lock),
|
||||||
onClick = {
|
desc = getString(R.string.app_lock_desc),
|
||||||
customAlertDialog().apply {
|
icon = R.drawable.ic_round_lock_open_24,
|
||||||
val view = DialogSetPasswordBinding.inflate(layoutInflater)
|
onClick = {
|
||||||
setTitle(R.string.app_lock)
|
customAlertDialog().apply {
|
||||||
setCustomView(view.root)
|
val view = DialogSetPasswordBinding.inflate(layoutInflater)
|
||||||
setPosButton(R.string.ok) {
|
setTitle(R.string.app_lock)
|
||||||
if (view.forgotPasswordCheckbox.isChecked) {
|
setCustomView(view.root)
|
||||||
PrefManager.setVal(PrefName.OverridePassword, true)
|
setPosButton(R.string.ok) {
|
||||||
}
|
if (view.forgotPasswordCheckbox.isChecked) {
|
||||||
val password = view.passwordInput.text.toString()
|
PrefManager.setVal(PrefName.OverridePassword, true)
|
||||||
val confirmPassword =
|
}
|
||||||
view.confirmPasswordInput.text.toString()
|
val password = view.passwordInput.text.toString()
|
||||||
if (password == confirmPassword && password.isNotEmpty()) {
|
val confirmPassword = view.confirmPasswordInput.text.toString()
|
||||||
PrefManager.setVal(PrefName.AppPassword, password)
|
if (password == confirmPassword && password.isNotEmpty()) {
|
||||||
if (view.biometricCheckbox.isChecked) {
|
PrefManager.setVal(PrefName.AppPassword, password)
|
||||||
val canBiometricPrompt =
|
if (view.biometricCheckbox.isChecked) {
|
||||||
BiometricManager
|
val canBiometricPrompt =
|
||||||
.from(applicationContext)
|
BiometricManager.from(applicationContext)
|
||||||
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) ==
|
.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK) == BiometricManager.BIOMETRIC_SUCCESS
|
||||||
BiometricManager.BIOMETRIC_SUCCESS
|
|
||||||
|
|
||||||
if (canBiometricPrompt) {
|
if (canBiometricPrompt) {
|
||||||
val biometricPrompt =
|
val biometricPrompt =
|
||||||
BiometricPromptUtils.createBiometricPrompt(
|
BiometricPromptUtils.createBiometricPrompt(this@SettingsCommonActivity) { _ ->
|
||||||
this@SettingsCommonActivity
|
val token = UUID.randomUUID().toString()
|
||||||
) { _ ->
|
PrefManager.setVal(
|
||||||
val token = UUID.randomUUID().toString()
|
PrefName.BiometricToken,
|
||||||
PrefManager.setVal(
|
token
|
||||||
PrefName.BiometricToken,
|
)
|
||||||
token,
|
toast(R.string.success)
|
||||||
)
|
}
|
||||||
toast(R.string.success)
|
val promptInfo =
|
||||||
}
|
BiometricPromptUtils.createPromptInfo(this@SettingsCommonActivity)
|
||||||
val promptInfo =
|
biometricPrompt.authenticate(promptInfo)
|
||||||
BiometricPromptUtils.createPromptInfo(this@SettingsCommonActivity)
|
}
|
||||||
biometricPrompt.authenticate(promptInfo)
|
|
||||||
}
|
} else {
|
||||||
|
PrefManager.setVal(PrefName.BiometricToken, "")
|
||||||
|
toast(R.string.success)
|
||||||
|
}
|
||||||
|
} 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()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
),
|
||||||
|
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
|
||||||
|
)
|
||||||
} else {
|
} else {
|
||||||
PrefManager.setVal(PrefName.BiometricToken, "")
|
toast(R.string.password_cannot_be_empty)
|
||||||
toast(R.string.success)
|
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
toast(R.string.password_mismatch)
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
savePrefsToDownloads(
|
||||||
|
"DantotsuSettings",
|
||||||
|
PrefManager.exportAllPrefs(selected),
|
||||||
|
context,
|
||||||
|
null
|
||||||
|
)
|
||||||
}
|
}
|
||||||
setNegButton(R.string.cancel)
|
}.setNeutralButton(R.string.cancel) { dialog, _ ->
|
||||||
setNeutralButton(R.string.remove) {
|
dialog.dismiss()
|
||||||
PrefManager.setVal(PrefName.AppPassword, "")
|
}.create()
|
||||||
PrefManager.setVal(PrefName.BiometricToken, "")
|
dialog.window?.setDimAmount(0.8f)
|
||||||
PrefManager.setVal(PrefName.OverridePassword, false)
|
dialog.show()
|
||||||
toast(R.string.success)
|
},
|
||||||
}
|
),
|
||||||
setOnShowListener {
|
Settings(
|
||||||
view.passwordInput.requestFocus()
|
type = 1,
|
||||||
val canAuthenticate =
|
name = getString(R.string.change_download_location),
|
||||||
BiometricManager.from(applicationContext)
|
desc = getString(R.string.change_download_location_desc),
|
||||||
.canAuthenticate(
|
icon = R.drawable.ic_round_source_24,
|
||||||
BiometricManager.Authenticators.BIOMETRIC_WEAK,
|
onClick = {
|
||||||
) == BiometricManager.BIOMETRIC_SUCCESS
|
context.customAlertDialog().apply {
|
||||||
view.biometricCheckbox.isVisible = canAuthenticate
|
setTitle(R.string.change_download_location)
|
||||||
view.biometricCheckbox.isChecked =
|
setMessage(R.string.download_location_msg)
|
||||||
PrefManager.getVal(PrefName.BiometricToken, "")
|
setPosButton(R.string.ok) {
|
||||||
.isNotEmpty()
|
val oldUri = PrefManager.getVal<String>(PrefName.DownloadsDir)
|
||||||
view.forgotPasswordCheckbox.isChecked =
|
launcher.registerForCallback { success ->
|
||||||
PrefManager.getVal(PrefName.OverridePassword)
|
if (success) {
|
||||||
}
|
toast(getString(R.string.please_wait))
|
||||||
show()
|
val newUri =
|
||||||
}
|
PrefManager.getVal<String>(PrefName.DownloadsDir)
|
||||||
},
|
GlobalScope.launch(Dispatchers.IO) {
|
||||||
),
|
Injekt.get<DownloadsManager>().moveDownloadsDir(
|
||||||
Settings(
|
context, Uri.parse(oldUri), Uri.parse(newUri)
|
||||||
type = 1,
|
) { finished, message ->
|
||||||
name = getString(R.string.backup_restore),
|
if (finished) {
|
||||||
desc = getString(R.string.backup_restore_desc),
|
toast(getString(R.string.success))
|
||||||
icon = R.drawable.backup_restore,
|
} else {
|
||||||
onClick = {
|
toast(message)
|
||||||
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 {
|
|
||||||
savePrefsToDownloads(
|
|
||||||
"DantotsuSettings",
|
|
||||||
PrefManager.exportAllPrefs(selected),
|
|
||||||
context,
|
|
||||||
null,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setNeutralButton(R.string.cancel) {}
|
|
||||||
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<String>(PrefName.DownloadsDir)
|
|
||||||
launcher.registerForCallback { success ->
|
|
||||||
if (success) {
|
|
||||||
toast(getString(R.string.please_wait))
|
|
||||||
val newUri =
|
|
||||||
PrefManager.getVal<String>(PrefName.DownloadsDir)
|
|
||||||
GlobalScope.launch(Dispatchers.IO) {
|
|
||||||
Injekt.get<DownloadsManager>().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))
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
toast(getString(R.string.error))
|
||||||
}
|
}
|
||||||
launcher.launch()
|
|
||||||
}
|
}
|
||||||
setNegButton(R.string.cancel)
|
launcher.launch()
|
||||||
show()
|
|
||||||
}
|
}
|
||||||
},
|
setNegButton(R.string.cancel)
|
||||||
),
|
show()
|
||||||
Settings(
|
}
|
||||||
type = 2,
|
}
|
||||||
name = getString(R.string.always_continue_content),
|
),
|
||||||
desc = getString(R.string.always_continue_content_desc),
|
Settings(
|
||||||
icon = R.drawable.ic_round_delete_24,
|
type = 2,
|
||||||
isChecked = PrefManager.getVal(PrefName.ContinueMedia),
|
name = getString(R.string.always_continue_content),
|
||||||
switch = { isChecked, _ ->
|
desc = getString(R.string.always_continue_content_desc),
|
||||||
PrefManager.setVal(PrefName.ContinueMedia, isChecked)
|
icon = R.drawable.ic_round_delete_24,
|
||||||
},
|
isChecked = PrefManager.getVal(PrefName.ContinueMedia),
|
||||||
),
|
switch = { isChecked, _ ->
|
||||||
Settings(
|
PrefManager.setVal(PrefName.ContinueMedia, isChecked)
|
||||||
type = 2,
|
}
|
||||||
name = getString(R.string.hide_private),
|
),
|
||||||
desc = getString(R.string.hide_private_desc),
|
Settings(
|
||||||
icon = R.drawable.ic_round_remove_red_eye_24,
|
type = 2,
|
||||||
isChecked = PrefManager.getVal(PrefName.HidePrivate),
|
name = getString(R.string.hide_private),
|
||||||
switch = { isChecked, _ ->
|
desc = getString(R.string.hide_private_desc),
|
||||||
PrefManager.setVal(PrefName.HidePrivate, isChecked)
|
icon = R.drawable.ic_round_remove_red_eye_24,
|
||||||
restartApp()
|
isChecked = PrefManager.getVal(PrefName.HidePrivate),
|
||||||
},
|
switch = { isChecked, _ ->
|
||||||
),
|
PrefManager.setVal(PrefName.HidePrivate, isChecked)
|
||||||
Settings(
|
restartApp()
|
||||||
type = 2,
|
}
|
||||||
name = getString(R.string.search_source_list),
|
),
|
||||||
desc = getString(R.string.search_source_list_desc),
|
Settings(
|
||||||
icon = R.drawable.ic_round_search_sources_24,
|
type = 2,
|
||||||
isChecked = PrefManager.getVal(PrefName.SearchSources),
|
name = getString(R.string.search_source_list),
|
||||||
switch = { isChecked, _ ->
|
desc = getString(R.string.search_source_list_desc),
|
||||||
PrefManager.setVal(PrefName.SearchSources, isChecked)
|
icon = R.drawable.ic_round_search_sources_24,
|
||||||
},
|
isChecked = PrefManager.getVal(PrefName.SearchSources),
|
||||||
),
|
switch = { isChecked, _ ->
|
||||||
Settings(
|
PrefManager.setVal(PrefName.SearchSources, isChecked)
|
||||||
type = 2,
|
}
|
||||||
name = getString(R.string.recentlyListOnly),
|
),
|
||||||
desc = getString(R.string.recentlyListOnly_desc),
|
Settings(
|
||||||
icon = R.drawable.ic_round_new_releases_24,
|
type = 2,
|
||||||
isChecked = PrefManager.getVal(PrefName.RecentlyListOnly),
|
name = getString(R.string.recentlyListOnly),
|
||||||
switch = { isChecked, _ ->
|
desc = getString(R.string.recentlyListOnly_desc),
|
||||||
PrefManager.setVal(PrefName.RecentlyListOnly, isChecked)
|
icon = R.drawable.ic_round_new_releases_24,
|
||||||
},
|
isChecked = PrefManager.getVal(PrefName.RecentlyListOnly),
|
||||||
),
|
switch = { isChecked, _ ->
|
||||||
Settings(
|
PrefManager.setVal(PrefName.RecentlyListOnly, isChecked)
|
||||||
type = 2,
|
}
|
||||||
name = getString(R.string.adult_only_content),
|
),
|
||||||
desc = getString(R.string.adult_only_content_desc),
|
Settings(
|
||||||
icon = R.drawable.ic_round_nsfw_24,
|
type = 2,
|
||||||
isChecked = PrefManager.getVal(PrefName.AdultOnly),
|
name = getString(R.string.adult_only_content),
|
||||||
switch = { isChecked, _ ->
|
desc = getString(R.string.adult_only_content_desc),
|
||||||
PrefManager.setVal(PrefName.AdultOnly, isChecked)
|
icon = R.drawable.ic_round_nsfw_24,
|
||||||
restartApp()
|
isChecked = PrefManager.getVal(PrefName.AdultOnly),
|
||||||
},
|
switch = { isChecked, _ ->
|
||||||
isVisible = Anilist.adult,
|
PrefManager.setVal(PrefName.AdultOnly, isChecked)
|
||||||
),
|
restartApp()
|
||||||
|
},
|
||||||
|
isVisible = Anilist.adult
|
||||||
|
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
)
|
||||||
settingsRecyclerView.apply {
|
settingsRecyclerView.apply {
|
||||||
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
layoutManager = LinearLayoutManager(context, LinearLayoutManager.VERTICAL, false)
|
||||||
setHasFixedSize(true)
|
setHasFixedSize(true)
|
||||||
}
|
}
|
||||||
var previousStart: View =
|
var previousStart: View = when (PrefManager.getVal<Int>(PrefName.DefaultStartUpTab)) {
|
||||||
when (PrefManager.getVal<Int>(PrefName.DefaultStartUpTab)) {
|
0 -> uiSettingsAnime
|
||||||
0 -> uiSettingsAnime
|
1 -> uiSettingsHome
|
||||||
1 -> uiSettingsHome
|
2 -> uiSettingsManga
|
||||||
2 -> uiSettingsManga
|
else -> uiSettingsHome
|
||||||
else -> uiSettingsHome
|
}
|
||||||
}
|
|
||||||
previousStart.alpha = 1f
|
previousStart.alpha = 1f
|
||||||
|
fun uiDefault(mode: Int, current: View) {
|
||||||
fun uiDefault(
|
|
||||||
mode: Int,
|
|
||||||
current: View,
|
|
||||||
) {
|
|
||||||
previousStart.alpha = 0.33f
|
previousStart.alpha = 0.33f
|
||||||
previousStart = current
|
previousStart = current
|
||||||
current.alpha = 1f
|
current.alpha = 1f
|
||||||
|
@ -440,13 +431,11 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||||
uiSettingsManga.setOnClickListener {
|
uiSettingsManga.setOnClickListener {
|
||||||
uiDefault(2, it)
|
uiDefault(2, it)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun passwordAlertDialog(
|
private fun passwordAlertDialog(isExporting: Boolean, callback: (CharArray?) -> Unit) {
|
||||||
isExporting: Boolean,
|
|
||||||
callback: (CharArray?) -> Unit,
|
|
||||||
) {
|
|
||||||
val password = CharArray(16).apply { fill('0') }
|
val password = CharArray(16).apply { fill('0') }
|
||||||
|
|
||||||
// Inflate the dialog layout
|
// Inflate the dialog layout
|
||||||
|
@ -456,9 +445,7 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||||
box.setSingleLine()
|
box.setSingleLine()
|
||||||
|
|
||||||
val dialog =
|
val dialog =
|
||||||
AlertDialog
|
AlertDialog.Builder(this, R.style.MyPopup).setTitle(getString(R.string.enter_password))
|
||||||
.Builder(this, R.style.MyPopup)
|
|
||||||
.setTitle(getString(R.string.enter_password))
|
|
||||||
.setView(dialogView.root)
|
.setView(dialogView.root)
|
||||||
.setPositiveButton(R.string.ok, null)
|
.setPositiveButton(R.string.ok, null)
|
||||||
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
.setNegativeButton(R.string.cancel) { dialog, _ ->
|
||||||
|
@ -470,10 +457,7 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||||
fun handleOkAction() {
|
fun handleOkAction() {
|
||||||
val editText = dialogView.userAgentTextBox
|
val editText = dialogView.userAgentTextBox
|
||||||
if (editText.text?.isNotBlank() == true) {
|
if (editText.text?.isNotBlank() == true) {
|
||||||
editText.text
|
editText.text?.toString()?.trim()?.toCharArray(password)
|
||||||
?.toString()
|
|
||||||
?.trim()
|
|
||||||
?.toCharArray(password)
|
|
||||||
dialog.dismiss()
|
dialog.dismiss()
|
||||||
callback(password)
|
callback(password)
|
||||||
} else {
|
} else {
|
||||||
|
@ -489,20 +473,18 @@ class SettingsCommonActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
dialogView.subtitle.visibility = View.VISIBLE
|
dialogView.subtitle.visibility = View.VISIBLE
|
||||||
if (!isExporting) {
|
if (!isExporting) dialogView.subtitle.text =
|
||||||
dialogView.subtitle.text =
|
getString(R.string.enter_password_to_decrypt_file)
|
||||||
getString(R.string.enter_password_to_decrypt_file)
|
|
||||||
}
|
|
||||||
|
|
||||||
dialog.window?.apply {
|
|
||||||
setDimAmount(0.8f)
|
dialog.window?.setDimAmount(0.8f)
|
||||||
attributes.windowAnimations = android.R.style.Animation_Dialog
|
|
||||||
}
|
|
||||||
dialog.show()
|
dialog.show()
|
||||||
|
|
||||||
// Override the positive button here
|
// Override the positive button here
|
||||||
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
dialog.getButton(AlertDialog.BUTTON_POSITIVE).setOnClickListener {
|
||||||
handleOkAction()
|
handleOkAction()
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
}
|
|
@ -128,27 +128,26 @@ class SettingsNotificationActivity : AppCompatActivity() {
|
||||||
PrefManager.getVal<Set<String>>(PrefName.AnilistFilteredTypes)
|
PrefManager.getVal<Set<String>>(PrefName.AnilistFilteredTypes)
|
||||||
.toMutableSet()
|
.toMutableSet()
|
||||||
val selected = types.map { filteredTypes.contains(it) }.toBooleanArray()
|
val selected = types.map { filteredTypes.contains(it) }.toBooleanArray()
|
||||||
context.customAlertDialog().apply {
|
val dialog = AlertDialog.Builder(context, R.style.MyPopup)
|
||||||
setTitle(R.string.anilist_notification_filters)
|
.setTitle(R.string.anilist_notification_filters)
|
||||||
multiChoiceItems(
|
.setMultiChoiceItems(
|
||||||
types.map { name ->
|
types.map { name ->
|
||||||
name.replace("_", " ").lowercase().replaceFirstChar {
|
name.replace("_", " ").lowercase().replaceFirstChar {
|
||||||
if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString()
|
if (it.isLowerCase()) it.titlecase(Locale.ROOT) else it.toString()
|
||||||
}
|
}
|
||||||
}.toTypedArray(),
|
}.toTypedArray(),
|
||||||
selected
|
selected
|
||||||
) { updatedSelected ->
|
) { _, which, isChecked ->
|
||||||
types.forEachIndexed { index, type ->
|
val type = types[which]
|
||||||
if (updatedSelected[index]) {
|
if (isChecked) {
|
||||||
filteredTypes.add(type)
|
filteredTypes.add(type)
|
||||||
} else {
|
} else {
|
||||||
filteredTypes.remove(type)
|
filteredTypes.remove(type)
|
||||||
}
|
|
||||||
}
|
}
|
||||||
PrefManager.setVal(PrefName.AnilistFilteredTypes, filteredTypes)
|
PrefManager.setVal(PrefName.AnilistFilteredTypes, filteredTypes)
|
||||||
}
|
}.create()
|
||||||
show()
|
dialog.window?.setDimAmount(0.8f)
|
||||||
}
|
dialog.show()
|
||||||
}
|
}
|
||||||
|
|
||||||
),
|
),
|
||||||
|
@ -161,24 +160,27 @@ class SettingsNotificationActivity : AppCompatActivity() {
|
||||||
desc = getString(R.string.anilist_notifications_checking_time_desc),
|
desc = getString(R.string.anilist_notifications_checking_time_desc),
|
||||||
icon = R.drawable.ic_round_notifications_none_24,
|
icon = R.drawable.ic_round_notifications_none_24,
|
||||||
onClick = {
|
onClick = {
|
||||||
context.customAlertDialog().apply {
|
val selected =
|
||||||
setTitle(R.string.subscriptions_checking_time)
|
PrefManager.getVal<Int>(PrefName.AnilistNotificationInterval)
|
||||||
singleChoiceItems(
|
val dialog = AlertDialog.Builder(context, R.style.MyPopup)
|
||||||
|
.setTitle(R.string.subscriptions_checking_time)
|
||||||
|
.setSingleChoiceItems(
|
||||||
aItems.toTypedArray(),
|
aItems.toTypedArray(),
|
||||||
PrefManager.getVal<Int>(PrefName.AnilistNotificationInterval)
|
selected
|
||||||
) { i ->
|
) { dialog, i ->
|
||||||
PrefManager.setVal(PrefName.AnilistNotificationInterval, i)
|
PrefManager.setVal(PrefName.AnilistNotificationInterval, i)
|
||||||
it.settingsTitle.text =
|
it.settingsTitle.text =
|
||||||
getString(
|
getString(
|
||||||
R.string.anilist_notifications_checking_time,
|
R.string.anilist_notifications_checking_time,
|
||||||
aItems[i]
|
aItems[i]
|
||||||
)
|
)
|
||||||
|
dialog.dismiss()
|
||||||
TaskScheduler.create(
|
TaskScheduler.create(
|
||||||
context, PrefManager.getVal(PrefName.UseAlarmManager)
|
context, PrefManager.getVal(PrefName.UseAlarmManager)
|
||||||
).scheduleAllTasks(context)
|
).scheduleAllTasks(context)
|
||||||
}
|
}.create()
|
||||||
show()
|
dialog.window?.setDimAmount(0.8f)
|
||||||
}
|
dialog.show()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
Settings(
|
Settings(
|
||||||
|
@ -190,24 +192,27 @@ class SettingsNotificationActivity : AppCompatActivity() {
|
||||||
desc = getString(R.string.comment_notification_checking_time_desc),
|
desc = getString(R.string.comment_notification_checking_time_desc),
|
||||||
icon = R.drawable.ic_round_notifications_none_24,
|
icon = R.drawable.ic_round_notifications_none_24,
|
||||||
onClick = {
|
onClick = {
|
||||||
context.customAlertDialog().apply {
|
val selected =
|
||||||
setTitle(R.string.subscriptions_checking_time)
|
PrefManager.getVal<Int>(PrefName.CommentNotificationInterval)
|
||||||
singleChoiceItems(
|
val dialog = AlertDialog.Builder(context, R.style.MyPopup)
|
||||||
|
.setTitle(R.string.subscriptions_checking_time)
|
||||||
|
.setSingleChoiceItems(
|
||||||
cItems.toTypedArray(),
|
cItems.toTypedArray(),
|
||||||
PrefManager.getVal<Int>(PrefName.CommentNotificationInterval)
|
selected
|
||||||
) { i ->
|
) { dialog, i ->
|
||||||
PrefManager.setVal(PrefName.CommentNotificationInterval, i)
|
PrefManager.setVal(PrefName.CommentNotificationInterval, i)
|
||||||
it.settingsTitle.text =
|
it.settingsTitle.text =
|
||||||
getString(
|
getString(
|
||||||
R.string.comment_notification_checking_time,
|
R.string.comment_notification_checking_time,
|
||||||
cItems[i]
|
cItems[i]
|
||||||
)
|
)
|
||||||
|
dialog.dismiss()
|
||||||
TaskScheduler.create(
|
TaskScheduler.create(
|
||||||
context, PrefManager.getVal(PrefName.UseAlarmManager)
|
context, PrefManager.getVal(PrefName.UseAlarmManager)
|
||||||
).scheduleAllTasks(context)
|
).scheduleAllTasks(context)
|
||||||
}
|
}.create()
|
||||||
show()
|
dialog.window?.setDimAmount(0.8f)
|
||||||
}
|
dialog.show()
|
||||||
}
|
}
|
||||||
),
|
),
|
||||||
Settings(
|
Settings(
|
||||||
|
@ -234,10 +239,10 @@ class SettingsNotificationActivity : AppCompatActivity() {
|
||||||
isChecked = PrefManager.getVal(PrefName.UseAlarmManager),
|
isChecked = PrefManager.getVal(PrefName.UseAlarmManager),
|
||||||
switch = { isChecked, view ->
|
switch = { isChecked, view ->
|
||||||
if (isChecked) {
|
if (isChecked) {
|
||||||
context.customAlertDialog().apply {
|
val alertDialog = AlertDialog.Builder(context, R.style.MyPopup)
|
||||||
setTitle(R.string.use_alarm_manager)
|
.setTitle(R.string.use_alarm_manager)
|
||||||
setMessage(R.string.use_alarm_manager_confirm)
|
.setMessage(R.string.use_alarm_manager_confirm)
|
||||||
setPosButton(R.string.use) {
|
.setPositiveButton(R.string.use) { dialog, _ ->
|
||||||
PrefManager.setVal(PrefName.UseAlarmManager, true)
|
PrefManager.setVal(PrefName.UseAlarmManager, true)
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||||
if (!(getSystemService(Context.ALARM_SERVICE) as AlarmManager).canScheduleExactAlarms()) {
|
if (!(getSystemService(Context.ALARM_SERVICE) as AlarmManager).canScheduleExactAlarms()) {
|
||||||
|
@ -247,13 +252,15 @@ class SettingsNotificationActivity : AppCompatActivity() {
|
||||||
view.settingsButton.isChecked = true
|
view.settingsButton.isChecked = true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
dialog.dismiss()
|
||||||
setNegButton(R.string.cancel) {
|
}.setNegativeButton(R.string.cancel) { dialog, _ ->
|
||||||
view.settingsButton.isChecked = false
|
view.settingsButton.isChecked = false
|
||||||
PrefManager.setVal(PrefName.UseAlarmManager, false)
|
PrefManager.setVal(PrefName.UseAlarmManager, false)
|
||||||
}
|
|
||||||
show()
|
dialog.dismiss()
|
||||||
}
|
}.create()
|
||||||
|
alertDialog.window?.setDimAmount(0.8f)
|
||||||
|
alertDialog.show()
|
||||||
} else {
|
} else {
|
||||||
PrefManager.setVal(PrefName.UseAlarmManager, false)
|
PrefManager.setVal(PrefName.UseAlarmManager, false)
|
||||||
TaskScheduler.create(context, true).cancelAllTasks()
|
TaskScheduler.create(context, true).cancelAllTasks()
|
||||||
|
@ -270,4 +277,4 @@ class SettingsNotificationActivity : AppCompatActivity() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -96,8 +96,7 @@ class SettingsThemeActivity : AppCompatActivity(), SimpleDialog.OnDialogResultLi
|
||||||
themeSwitcher.apply {
|
themeSwitcher.apply {
|
||||||
setText(themeText)
|
setText(themeText)
|
||||||
setAdapter(
|
setAdapter(
|
||||||
ArrayAdapter(
|
ArrayAdapter(context,
|
||||||
context,
|
|
||||||
R.layout.item_dropdown,
|
R.layout.item_dropdown,
|
||||||
ThemeManager.Companion.Theme.entries.map {
|
ThemeManager.Companion.Theme.entries.map {
|
||||||
it.theme.substring(
|
it.theme.substring(
|
||||||
|
|
|
@ -52,15 +52,14 @@ class SubscriptionsBottomDialog : BottomSheetDialogFragment() {
|
||||||
}
|
}
|
||||||
|
|
||||||
groupedSubscriptions.forEach { (parserName, mediaList) ->
|
groupedSubscriptions.forEach { (parserName, mediaList) ->
|
||||||
adapter.add(
|
adapter.add(SubscriptionSource(
|
||||||
SubscriptionSource(
|
parserName,
|
||||||
parserName,
|
mediaList.toMutableList(),
|
||||||
mediaList.toMutableList(),
|
adapter,
|
||||||
adapter,
|
getParserIcon(parserName)
|
||||||
getParserIcon(parserName)
|
) { group ->
|
||||||
) { group ->
|
adapter.remove(group)
|
||||||
adapter.remove(group)
|
})
|
||||||
})
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
android:padding="16dp">
|
android:padding="16dp">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="326dp"
|
android:layout_width="wrap_content"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
|
@ -160,8 +160,8 @@
|
||||||
android:orientation="horizontal">
|
android:orientation="horizontal">
|
||||||
|
|
||||||
<LinearLayout
|
<LinearLayout
|
||||||
android:layout_width="263dp"
|
android:layout_width="265dp"
|
||||||
android:layout_height="60dp"
|
android:layout_height="match_parent"
|
||||||
android:orientation="vertical">
|
android:orientation="vertical">
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
|
@ -171,23 +171,14 @@
|
||||||
android:fontFamily="@font/poppins_bold"
|
android:fontFamily="@font/poppins_bold"
|
||||||
android:text="@string/download" />
|
android:text="@string/download" />
|
||||||
|
|
||||||
<EditText
|
<TextView
|
||||||
android:id="@+id/downloadNo"
|
android:id="@+id/downloadNo"
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
android:fontFamily="@font/poppins_bold"
|
android:fontFamily="@font/poppins_bold"
|
||||||
android:textColor="?attr/colorSecondary"
|
android:textColor="?attr/colorSecondary"
|
||||||
android:textSize="12dp"
|
|
||||||
tools:ignore="TextContrastCheck"
|
tools:ignore="TextContrastCheck"
|
||||||
tools:text="Number" />
|
tools:text="number" />
|
||||||
<!-- <TextView-->
|
|
||||||
<!-- android:id="@+id/downloadNo"-->
|
|
||||||
<!-- android:layout_width="match_parent"-->
|
|
||||||
<!-- android:layout_height="wrap_content"-->
|
|
||||||
<!-- android:fontFamily="@font/poppins_bold"-->
|
|
||||||
<!-- android:textColor="?attr/colorSecondary"-->
|
|
||||||
<!-- tools:ignore="TextContrastCheck"-->
|
|
||||||
<!-- tools:text="number" />-->
|
|
||||||
</LinearLayout>
|
</LinearLayout>
|
||||||
|
|
||||||
<androidx.cardview.widget.CardView
|
<androidx.cardview.widget.CardView
|
||||||
|
@ -200,7 +191,7 @@
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/mediaDownloadTop"
|
android:id="@+id/mediaDownloadTop"
|
||||||
android:layout_width="48dp"
|
android:layout_width="48dp"
|
||||||
android:layout_height="60dp"
|
android:layout_height="48dp"
|
||||||
android:background="?android:attr/selectableItemBackground"
|
android:background="?android:attr/selectableItemBackground"
|
||||||
app:srcCompat="@drawable/ic_download_24"
|
app:srcCompat="@drawable/ic_download_24"
|
||||||
app:tint="?attr/colorOnBackground"
|
app:tint="?attr/colorOnBackground"
|
||||||
|
@ -322,9 +313,9 @@
|
||||||
android:text="@string/reset" />
|
android:text="@string/reset" />
|
||||||
|
|
||||||
<TextView
|
<TextView
|
||||||
android:id="@+id/reset_progress_def"
|
|
||||||
android:layout_width="match_parent"
|
android:layout_width="match_parent"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
android:id="@+id/reset_progress_def"
|
||||||
android:fontFamily="@font/poppins_bold"
|
android:fontFamily="@font/poppins_bold"
|
||||||
android:text=""
|
android:text=""
|
||||||
android:textColor="?attr/colorSecondary"
|
android:textColor="?attr/colorSecondary"
|
||||||
|
|
|
@ -12,7 +12,7 @@ buildscript {
|
||||||
}
|
}
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.9.0'
|
classpath 'com.android.tools.build:gradle:8.7.3'
|
||||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||||
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
classpath "org.jetbrains.kotlin:kotlin-serialization:$kotlin_version"
|
||||||
classpath "com.google.devtools.ksp:symbol-processing-api:$ksp_version"
|
classpath "com.google.devtools.ksp:symbol-processing-api:$ksp_version"
|
||||||
|
|
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
|
@ -1,6 +1,6 @@
|
||||||
#Wed Aug 30 19:57:04 IST 2023
|
#Wed Aug 30 19:57:04 IST 2023
|
||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.11.1-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.9-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|
53
stable.md
53
stable.md
|
@ -1,4 +1,53 @@
|
||||||
# 3.2.1
|
# 3.1.0
|
||||||
|
|
||||||
|
- **New Features:**
|
||||||
|
- Addons
|
||||||
|
- Torrent support addon
|
||||||
|
- Anime downloading addon (mkv files pog)
|
||||||
|
- Available in app settings
|
||||||
|
- Anilist reviews in app
|
||||||
|
- Media subscriptions added to notification tab
|
||||||
|
- Notification filtering
|
||||||
|
- Ability to post activitys
|
||||||
|
- Ability to reply to activities
|
||||||
|
- Extension tester
|
||||||
|
- Media subscription Viewer
|
||||||
|
- Instagram-style stories
|
||||||
|
- More audio options for some extensions
|
||||||
|
- Ability to hide items on the home screen
|
||||||
|
- Ability to set a downloads directory
|
||||||
|
- 2 functioning widgets
|
||||||
|
- App lock ( ͡° ͜ʖ ͡°)
|
||||||
|
- More manga and anime feeds on the home page
|
||||||
|
- Settings page redesign
|
||||||
|
- New app crash notifier
|
||||||
|
- Voice actors
|
||||||
|
- Additional repo support
|
||||||
|
- Various UI uplifts
|
||||||
|
|
||||||
- **Bugfixes:**
|
- **Bugfixes:**
|
||||||
- Fix a crash after watching a video
|
- Scanlator/language not saving after leaving app
|
||||||
|
- notification red dot not hiding on home pages
|
||||||
|
- comment/activity scrolling not working on some parts of the screen
|
||||||
|
- comment notifications falling to the bottom of the list
|
||||||
|
- Fixed some sources without audio
|
||||||
|
- Initial app loading time reduced
|
||||||
|
- activity text more visible
|
||||||
|
- novel extensions not installing
|
||||||
|
- Many sources not working
|
||||||
|
- Subscription notifications not using the correct source
|
||||||
|
- Notification red dot showing with no new notifications
|
||||||
|
- Various bug/crash fixes
|
||||||
|
- General theme tweaks
|
||||||
|
- Fixed some network-related crashes
|
||||||
|
- Subscription notifications not working for some people
|
||||||
|
- Fix for file permissions on older Android versions
|
||||||
|
- Search list view not working
|
||||||
|
- Media page opening twice on notification click
|
||||||
|
|
||||||
|
- A Special Thanks to all those who contributed :heart:
|
||||||
|
|
||||||
|
- **Like what you see?**
|
||||||
|
- Consider supporting me on [Github](https://github.com/sponsors/rebelonion) or [Buy Me a Coffee](https://www.buymeacoffee.com/rebelonion)!
|
||||||
|
|
||||||
|

|
||||||
|
|
Loading…
Add table
Add a link
Reference in a new issue