Compare commits
8 Commits
feature/st
...
v6.7.5
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
8c2bff78f7 | ||
|
|
4f2c38d4ee | ||
|
|
3c54fe4217 | ||
|
|
750bf11fdc | ||
|
|
b2c5ec5082 | ||
|
|
f97d4d452f | ||
|
|
640fe272c8 | ||
|
|
f730e80bb7 |
@@ -16,8 +16,8 @@ android {
|
|||||||
applicationId 'org.koitharu.kotatsu'
|
applicationId 'org.koitharu.kotatsu'
|
||||||
minSdk = 21
|
minSdk = 21
|
||||||
targetSdk = 34
|
targetSdk = 34
|
||||||
versionCode = 626
|
versionCode = 627
|
||||||
versionName = '6.7.4'
|
versionName = '6.7.5'
|
||||||
generatedDensities = []
|
generatedDensities = []
|
||||||
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
|
testInstrumentationRunner 'org.koitharu.kotatsu.HiltTestRunner'
|
||||||
ksp {
|
ksp {
|
||||||
@@ -82,7 +82,7 @@ afterEvaluate {
|
|||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
//noinspection GradleDependency
|
//noinspection GradleDependency
|
||||||
implementation('com.github.KotatsuApp:kotatsu-parsers:103f578c61') {
|
implementation('com.github.KotatsuApp:kotatsu-parsers:b7613606c0') {
|
||||||
exclude group: 'org.json', module: 'json'
|
exclude group: 'org.json', module: 'json'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ import androidx.core.view.isVisible
|
|||||||
import androidx.core.view.updatePadding
|
import androidx.core.view.updatePadding
|
||||||
import org.koitharu.kotatsu.R
|
import org.koitharu.kotatsu.R
|
||||||
import org.koitharu.kotatsu.core.ui.BaseActivity
|
import org.koitharu.kotatsu.core.ui.BaseActivity
|
||||||
|
import org.koitharu.kotatsu.core.util.ext.toUriOrNull
|
||||||
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
|
import org.koitharu.kotatsu.databinding.ActivityBrowserBinding
|
||||||
import org.koitharu.kotatsu.parsers.network.UserAgents
|
import org.koitharu.kotatsu.parsers.network.UserAgents
|
||||||
import com.google.android.material.R as materialR
|
import com.google.android.material.R as materialR
|
||||||
@@ -80,12 +81,15 @@ class BrowserActivity : BaseActivity<ActivityBrowserBinding>(), BrowserCallback
|
|||||||
}
|
}
|
||||||
|
|
||||||
R.id.action_browser -> {
|
R.id.action_browser -> {
|
||||||
|
val url = viewBinding.webView.url?.toUriOrNull()
|
||||||
|
if (url != null) {
|
||||||
val intent = Intent(Intent.ACTION_VIEW)
|
val intent = Intent(Intent.ACTION_VIEW)
|
||||||
intent.data = Uri.parse(viewBinding.webView.url)
|
intent.data = url
|
||||||
try {
|
try {
|
||||||
startActivity(Intent.createChooser(intent, item.title))
|
startActivity(Intent.createChooser(intent, item.title))
|
||||||
} catch (_: ActivityNotFoundException) {
|
} catch (_: ActivityNotFoundException) {
|
||||||
}
|
}
|
||||||
|
}
|
||||||
true
|
true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ import org.koitharu.kotatsu.explore.data.SourcesSortOrder
|
|||||||
import org.koitharu.kotatsu.list.domain.ListSortOrder
|
import org.koitharu.kotatsu.list.domain.ListSortOrder
|
||||||
import org.koitharu.kotatsu.parsers.model.SortOrder
|
import org.koitharu.kotatsu.parsers.model.SortOrder
|
||||||
import org.koitharu.kotatsu.parsers.util.find
|
import org.koitharu.kotatsu.parsers.util.find
|
||||||
|
import org.koitharu.kotatsu.parsers.util.isNumeric
|
||||||
import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
|
import org.koitharu.kotatsu.parsers.util.mapNotNullToSet
|
||||||
import org.koitharu.kotatsu.parsers.util.mapToSet
|
import org.koitharu.kotatsu.parsers.util.mapToSet
|
||||||
import org.koitharu.kotatsu.reader.domain.ReaderColorFilter
|
import org.koitharu.kotatsu.reader.domain.ReaderColorFilter
|
||||||
@@ -191,11 +192,13 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
|||||||
var appPassword: String?
|
var appPassword: String?
|
||||||
get() = prefs.getString(KEY_APP_PASSWORD, null)
|
get() = prefs.getString(KEY_APP_PASSWORD, null)
|
||||||
set(value) = prefs.edit {
|
set(value) = prefs.edit {
|
||||||
if (value != null) putString(KEY_APP_PASSWORD, value) else remove(
|
if (value != null) putString(KEY_APP_PASSWORD, value) else remove(KEY_APP_PASSWORD)
|
||||||
KEY_APP_PASSWORD,
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var isAppPasswordNumeric: Boolean
|
||||||
|
get() = prefs.getBoolean(KEY_APP_PASSWORD_NUMERIC, false)
|
||||||
|
set(value) = prefs.edit { putBoolean(KEY_APP_PASSWORD_NUMERIC, value) }
|
||||||
|
|
||||||
val isLoggingEnabled: Boolean
|
val isLoggingEnabled: Boolean
|
||||||
get() = prefs.getBoolean(KEY_LOGGING_ENABLED, false)
|
get() = prefs.getBoolean(KEY_LOGGING_ENABLED, false)
|
||||||
|
|
||||||
@@ -528,6 +531,7 @@ class AppSettings @Inject constructor(@ApplicationContext context: Context) {
|
|||||||
const val KEY_READER_MODE = "reader_mode"
|
const val KEY_READER_MODE = "reader_mode"
|
||||||
const val KEY_READER_MODE_DETECT = "reader_mode_detect"
|
const val KEY_READER_MODE_DETECT = "reader_mode_detect"
|
||||||
const val KEY_APP_PASSWORD = "app_password"
|
const val KEY_APP_PASSWORD = "app_password"
|
||||||
|
const val KEY_APP_PASSWORD_NUMERIC = "app_password_num"
|
||||||
const val KEY_PROTECT_APP = "protect_app"
|
const val KEY_PROTECT_APP = "protect_app"
|
||||||
const val KEY_PROTECT_APP_BIOMETRIC = "protect_app_bio"
|
const val KEY_PROTECT_APP_BIOMETRIC = "protect_app_bio"
|
||||||
const val KEY_APP_VERSION = "app_version"
|
const val KEY_APP_VERSION = "app_version"
|
||||||
|
|||||||
@@ -60,11 +60,11 @@ abstract class BaseActivity<B : ViewBinding> :
|
|||||||
if (isAmoledTheme) {
|
if (isAmoledTheme) {
|
||||||
setTheme(R.style.ThemeOverlay_Kotatsu_Amoled)
|
setTheme(R.style.ThemeOverlay_Kotatsu_Amoled)
|
||||||
}
|
}
|
||||||
|
putDataToExtras(intent)
|
||||||
super.onCreate(savedInstanceState)
|
super.onCreate(savedInstanceState)
|
||||||
WindowCompat.setDecorFitsSystemWindows(window, false)
|
WindowCompat.setDecorFitsSystemWindows(window, false)
|
||||||
insetsDelegate.handleImeInsets = true
|
insetsDelegate.handleImeInsets = true
|
||||||
insetsDelegate.addInsetsListener(this)
|
insetsDelegate.addInsetsListener(this)
|
||||||
putDataToExtras(intent)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun onPostCreate(savedInstanceState: Bundle?) {
|
override fun onPostCreate(savedInstanceState: Bundle?) {
|
||||||
|
|||||||
@@ -44,6 +44,12 @@ class ProtectActivity :
|
|||||||
viewBinding.buttonNext.setOnClickListener(this)
|
viewBinding.buttonNext.setOnClickListener(this)
|
||||||
viewBinding.buttonCancel.setOnClickListener(this)
|
viewBinding.buttonCancel.setOnClickListener(this)
|
||||||
|
|
||||||
|
viewBinding.editPassword.inputType = if (viewModel.isNumericPassword) {
|
||||||
|
EditorInfo.TYPE_CLASS_NUMBER or EditorInfo.TYPE_NUMBER_VARIATION_PASSWORD
|
||||||
|
} else {
|
||||||
|
EditorInfo.TYPE_CLASS_TEXT or EditorInfo.TYPE_TEXT_VARIATION_PASSWORD
|
||||||
|
}
|
||||||
|
|
||||||
viewModel.onError.observeEvent(this, this::onError)
|
viewModel.onError.observeEvent(this, this::onError)
|
||||||
viewModel.isLoading.observe(this, this::onLoadingStateChanged)
|
viewModel.isLoading.observe(this, this::onLoadingStateChanged)
|
||||||
viewModel.onUnlockSuccess.observeEvent(this) {
|
viewModel.onUnlockSuccess.observeEvent(this) {
|
||||||
|
|||||||
@@ -8,6 +8,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
|
|||||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||||
import org.koitharu.kotatsu.core.util.ext.call
|
import org.koitharu.kotatsu.core.util.ext.call
|
||||||
|
import org.koitharu.kotatsu.parsers.util.isNumeric
|
||||||
import org.koitharu.kotatsu.parsers.util.md5
|
import org.koitharu.kotatsu.parsers.util.md5
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -26,6 +27,9 @@ class ProtectViewModel @Inject constructor(
|
|||||||
val isBiometricEnabled
|
val isBiometricEnabled
|
||||||
get() = settings.isBiometricProtectionEnabled
|
get() = settings.isBiometricProtectionEnabled
|
||||||
|
|
||||||
|
val isNumericPassword
|
||||||
|
get() = settings.isAppPasswordNumeric
|
||||||
|
|
||||||
fun tryUnlock(password: String) {
|
fun tryUnlock(password: String) {
|
||||||
if (job?.isActive == true) {
|
if (job?.isActive == true) {
|
||||||
return
|
return
|
||||||
|
|||||||
@@ -14,6 +14,7 @@ class ChapterPages private constructor(private val pages: ArrayDeque<ReaderPage>
|
|||||||
val chaptersSize: Int
|
val chaptersSize: Int
|
||||||
get() = indices.size()
|
get() = indices.size()
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
fun removeFirst() {
|
fun removeFirst() {
|
||||||
val chapterId = pages.first().chapterId
|
val chapterId = pages.first().chapterId
|
||||||
indices.remove(chapterId)
|
indices.remove(chapterId)
|
||||||
@@ -25,6 +26,7 @@ class ChapterPages private constructor(private val pages: ArrayDeque<ReaderPage>
|
|||||||
shiftIndices(delta)
|
shiftIndices(delta)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
fun removeLast() {
|
fun removeLast() {
|
||||||
val chapterId = pages.last().chapterId
|
val chapterId = pages.last().chapterId
|
||||||
indices.remove(chapterId)
|
indices.remove(chapterId)
|
||||||
@@ -33,17 +35,28 @@ class ChapterPages private constructor(private val pages: ArrayDeque<ReaderPage>
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addLast(id: Long, newPages: List<ReaderPage>) {
|
@Synchronized
|
||||||
|
fun addLast(id: Long, newPages: List<ReaderPage>): Boolean {
|
||||||
|
if (id in indices) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
indices.put(id, pages.size until (pages.size + newPages.size))
|
indices.put(id, pages.size until (pages.size + newPages.size))
|
||||||
pages.addAll(newPages)
|
pages.addAll(newPages)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
fun addFirst(id: Long, newPages: List<ReaderPage>) {
|
@Synchronized
|
||||||
|
fun addFirst(id: Long, newPages: List<ReaderPage>): Boolean {
|
||||||
|
if (id in indices) {
|
||||||
|
return false
|
||||||
|
}
|
||||||
shiftIndices(newPages.size)
|
shiftIndices(newPages.size)
|
||||||
indices.put(id, newPages.indices)
|
indices.put(id, newPages.indices)
|
||||||
pages.addAll(0, newPages)
|
pages.addAll(0, newPages)
|
||||||
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Synchronized
|
||||||
fun clear() {
|
fun clear() {
|
||||||
indices.clear()
|
indices.clear()
|
||||||
pages.clear()
|
pages.clear()
|
||||||
@@ -58,7 +71,7 @@ class ChapterPages private constructor(private val pages: ArrayDeque<ReaderPage>
|
|||||||
return pages.subList(range.first, range.last + 1)
|
return pages.subList(range.first, range.last + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
operator fun contains(chapterId: Long) = indices.contains(chapterId)
|
operator fun contains(chapterId: Long) = chapterId in indices
|
||||||
|
|
||||||
private fun shiftIndices(delta: Int) {
|
private fun shiftIndices(delta: Int) {
|
||||||
for (i in 0 until indices.size()) {
|
for (i in 0 until indices.size()) {
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import org.koitharu.kotatsu.core.prefs.AppSettings
|
|||||||
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
import org.koitharu.kotatsu.core.ui.BaseViewModel
|
||||||
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
import org.koitharu.kotatsu.core.util.ext.MutableEventFlow
|
||||||
import org.koitharu.kotatsu.core.util.ext.call
|
import org.koitharu.kotatsu.core.util.ext.call
|
||||||
|
import org.koitharu.kotatsu.parsers.util.isNumeric
|
||||||
import org.koitharu.kotatsu.parsers.util.md5
|
import org.koitharu.kotatsu.parsers.util.md5
|
||||||
import javax.inject.Inject
|
import javax.inject.Inject
|
||||||
|
|
||||||
@@ -39,6 +40,7 @@ class ProtectSetupViewModel @Inject constructor(
|
|||||||
} else {
|
} else {
|
||||||
if (firstPassword.value == password) {
|
if (firstPassword.value == password) {
|
||||||
settings.appPassword = password.md5()
|
settings.appPassword = password.md5()
|
||||||
|
settings.isAppPasswordNumeric = password.isNumeric()
|
||||||
onPasswordSet.call(Unit)
|
onPasswordSet.call(Unit)
|
||||||
} else {
|
} else {
|
||||||
onPasswordMismatch.call(Unit)
|
onPasswordMismatch.call(Unit)
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<item android:alpha="0.2" android:color="?attr/colorPrimaryInverse" />
|
<item android:alpha="0.27" android:color="?attr/colorOnSurface" />
|
||||||
</selector>
|
</selector>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
<selector xmlns:android="http://schemas.android.com/apk/res/android">
|
||||||
<!-- https://stackoverflow.com/questions/54685474/theme-attributes-in-color-selector-for-api-22 -->
|
<!-- https://stackoverflow.com/questions/54685474/theme-attributes-in-color-selector-for-api-22 -->
|
||||||
<item android:alpha="0.2" android:color="@color/kotatsu_inversePrimary" />
|
<item android:alpha="0.27" android:color="@color/kotatsu_onSurface" />
|
||||||
</selector>
|
</selector>
|
||||||
|
|||||||
@@ -112,7 +112,7 @@
|
|||||||
app:barrierMargin="8dp"
|
app:barrierMargin="8dp"
|
||||||
app:constraint_referenced_ids="imageView_cover,spinner_status" />
|
app:constraint_referenced_ids="imageView_cover,spinner_status" />
|
||||||
|
|
||||||
<TextView
|
<org.koitharu.kotatsu.core.ui.widgets.SelectableTextView
|
||||||
android:id="@+id/textView_description"
|
android:id="@+id/textView_description"
|
||||||
android:layout_width="0dp"
|
android:layout_width="0dp"
|
||||||
android:layout_height="wrap_content"
|
android:layout_height="wrap_content"
|
||||||
|
|||||||
@@ -4,7 +4,7 @@ buildscript {
|
|||||||
mavenCentral()
|
mavenCentral()
|
||||||
}
|
}
|
||||||
dependencies {
|
dependencies {
|
||||||
classpath 'com.android.tools.build:gradle:8.2.2'
|
classpath 'com.android.tools.build:gradle:8.3.0'
|
||||||
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22'
|
classpath 'org.jetbrains.kotlin:kotlin-gradle-plugin:1.9.22'
|
||||||
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.51'
|
classpath 'com.google.dagger:hilt-android-gradle-plugin:2.51'
|
||||||
classpath 'com.google.devtools.ksp:symbol-processing-gradle-plugin:1.9.22-1.0.17'
|
classpath 'com.google.devtools.ksp:symbol-processing-gradle-plugin:1.9.22-1.0.17'
|
||||||
|
|||||||
2
gradle/wrapper/gradle-wrapper.properties
vendored
2
gradle/wrapper/gradle-wrapper.properties
vendored
@@ -2,6 +2,6 @@
|
|||||||
distributionBase=GRADLE_USER_HOME
|
distributionBase=GRADLE_USER_HOME
|
||||||
distributionPath=wrapper/dists
|
distributionPath=wrapper/dists
|
||||||
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
|
distributionSha256Sum=3e1af3ae886920c3ac87f7a91f816c0c7c436f276a6eefdb3da152100fef72ae
|
||||||
distributionUrl=https\://services.gradle.org/distributions/gradle-8.4-bin.zip
|
distributionUrl=https\://services.gradle.org/distributions/gradle-8.6-bin.zip
|
||||||
zipStoreBase=GRADLE_USER_HOME
|
zipStoreBase=GRADLE_USER_HOME
|
||||||
zipStorePath=wrapper/dists
|
zipStorePath=wrapper/dists
|
||||||
|
|||||||
Reference in New Issue
Block a user