[DO NOT USE] Drop androidx preference, drop rikkax preference
This commit is contained in:
1 parent
8c6d28d6e2
commit
3664ca846c
37 files changed
+9
-3630
No files matched your search
+4
-10
@@ -1,4 +1,6 @@
|
||||
import com.google.devtools.ksp.gradle.KspTaskJvm
|
||||
import com.mikepenz.aboutlibraries.plugin.DuplicateMode.MERGE
|
||||
import com.mikepenz.aboutlibraries.plugin.DuplicateRule.GROUP
|
||||
import org.jetbrains.kotlin.gradle.dsl.JvmTarget.JVM_17
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.time.Instant
|
||||
@@ -191,7 +193,6 @@ dependencies {
|
||||
// https://developer.android.com/jetpack/androidx/releases/paging
|
||||
implementation("androidx.paging:paging-common-ktx:3.2.0-alpha06")
|
||||
implementation("androidx.paging:paging-compose:1.0.0-alpha20")
|
||||
implementation("androidx.preference:preference-ktx:1.2.0")
|
||||
implementation("androidx.recyclerview:recyclerview:1.3.0")
|
||||
|
||||
// https://developer.android.com/jetpack/androidx/releases/room
|
||||
@@ -229,8 +230,6 @@ dependencies {
|
||||
implementation("dev.rikka.rikkax.core:core-ktx:1.4.1")
|
||||
implementation("dev.rikka.rikkax.insets:insets:1.3.0")
|
||||
implementation("dev.rikka.rikkax.layoutinflater:layoutinflater:1.3.0")
|
||||
implementation("dev.rikka.rikkax.preference:simplemenu-preference:1.0.3")
|
||||
implementation("dev.rikka.rikkax.material:material-preference:2.0.0")
|
||||
|
||||
implementation(platform("io.arrow-kt:arrow-stack:1.2.0-RC"))
|
||||
implementation("io.arrow-kt:arrow-fx-coroutines")
|
||||
@@ -245,11 +244,6 @@ dependencies {
|
||||
implementation("org.jsoup:jsoup:1.16.1")
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
exclude("dev.rikka.rikkax.appcompat", "appcompat")
|
||||
exclude("dev.rikka.rikkax.material", "material")
|
||||
}
|
||||
|
||||
ksp {
|
||||
arg("room.schemaLocation", "$projectDir/schemas")
|
||||
arg("room.generateKotlin", "true")
|
||||
@@ -260,6 +254,6 @@ tasks.withType<KspTaskJvm>().configureEach {
|
||||
}
|
||||
|
||||
aboutLibraries {
|
||||
duplicationMode = com.mikepenz.aboutlibraries.plugin.DuplicateMode.MERGE
|
||||
duplicationRule = com.mikepenz.aboutlibraries.plugin.DuplicateRule.GROUP
|
||||
duplicationMode = MERGE
|
||||
duplicationRule = GROUP
|
||||
}
|
||||
@@ -126,10 +126,6 @@
|
||||
<data android:mimeType="application/x-xz-compressed-tar" />
|
||||
</intent-filter>
|
||||
</activity>
|
||||
<activity
|
||||
android:name=".ui.SettingsActivity"
|
||||
android:configChanges="screenSize"
|
||||
android:label="@string/settings" />
|
||||
<activity
|
||||
android:name=".ui.settings.SettingsActivity"
|
||||
android:configChanges="screenSize"
|
||||
|
||||
@@ -3,7 +3,6 @@ package com.hippo.ehviewer.client
|
||||
import android.os.Build
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.builtInHosts
|
||||
import com.hippo.ehviewer.preference.EhDoH
|
||||
import okhttp3.AsyncDns
|
||||
import okhttp3.Dns
|
||||
import okhttp3.android.AndroidAsyncDns
|
||||
@@ -30,5 +29,5 @@ fun HostsMap.hosts(vararg hosts: String, builder: HostMapBuilder.() -> Unit) = a
|
||||
val systemDns = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) AsyncDns.toDns(AndroidAsyncDns.IPv4, AndroidAsyncDns.IPv6) else Dns.SYSTEM
|
||||
|
||||
object EhDns : Dns {
|
||||
override fun lookup(hostname: String): List<InetAddress> = builtInHosts[hostname].takeIf { Settings.builtInHosts } ?: EhDoH.lookup(hostname) ?: systemDns.lookup(hostname)
|
||||
override fun lookup(hostname: String): List<InetAddress> = builtInHosts[hostname].takeIf { Settings.builtInHosts } ?: systemDns.lookup(hostname)
|
||||
}
|
||||
@@ -1,96 +0,0 @@
|
||||
/*
|
||||
* Copyright 2018 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.text.Html
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.ui.SettingsActivity
|
||||
import com.hippo.ehviewer.ui.legacy.preference.MessagePreference
|
||||
import com.hippo.ehviewer.ui.scene.BaseScene
|
||||
import com.hippo.ehviewer.util.addTextToClipboard
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import java.util.LinkedList
|
||||
|
||||
class AccountPreference @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : MessagePreference(context, attrs) {
|
||||
private val mActivity = context as SettingsActivity
|
||||
private var message: String? = null
|
||||
|
||||
init {
|
||||
val store = EhCookieStore
|
||||
val eCookies = store.getCookies(EhUrl.HOST_E.toHttpUrl())
|
||||
val exCookies = store.getCookies(EhUrl.HOST_EX.toHttpUrl())
|
||||
val cookies: MutableList<Cookie> = LinkedList(eCookies)
|
||||
cookies.addAll(exCookies)
|
||||
var ipbMemberId: String? = null
|
||||
var ipbPassHash: String? = null
|
||||
var igneous: String? = null
|
||||
var i = 0
|
||||
val n = cookies.size
|
||||
while (i < n) {
|
||||
val cookie = cookies[i]
|
||||
when (cookie.name) {
|
||||
EhCookieStore.KEY_IPB_MEMBER_ID -> ipbMemberId = cookie.value
|
||||
EhCookieStore.KEY_IPB_PASS_HASH -> ipbPassHash = cookie.value
|
||||
EhCookieStore.KEY_IGNEOUS -> igneous = cookie.value
|
||||
}
|
||||
i++
|
||||
}
|
||||
if (ipbMemberId != null || ipbPassHash != null || igneous != null) {
|
||||
message = (
|
||||
EhCookieStore.KEY_IPB_MEMBER_ID + ": " + ipbMemberId + "<br>" +
|
||||
EhCookieStore.KEY_IPB_PASS_HASH + ": " + ipbPassHash + "<br>" +
|
||||
EhCookieStore.KEY_IGNEOUS + ": " + igneous
|
||||
)
|
||||
setDialogMessage(
|
||||
Html.fromHtml(
|
||||
context.getString(
|
||||
R.string.settings_eh_identity_cookies_signed,
|
||||
message,
|
||||
),
|
||||
Html.FROM_HTML_MODE_LEGACY,
|
||||
),
|
||||
)
|
||||
message = message!!.replace("<br>", "\n")
|
||||
} else {
|
||||
setDialogMessage(context.getString(R.string.settings_eh_identity_cookies_tourist))
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
|
||||
super.onPrepareDialogBuilder(builder)
|
||||
if (message != null) {
|
||||
builder.setNeutralButton(R.string.settings_eh_identity_cookies_copy) { dialog: DialogInterface?, which: Int ->
|
||||
mActivity.addTextToClipboard(message, true)
|
||||
this@AccountPreference.onClick(dialog, which)
|
||||
}
|
||||
}
|
||||
builder.setPositiveButton(R.string.settings_eh_sign_out) { _: DialogInterface?, _: Int ->
|
||||
EhUtils.signOut()
|
||||
mActivity.showTip(R.string.settings_eh_sign_out_tip, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,75 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.download.downloadLocation
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import splitties.init.appCtx
|
||||
import com.hippo.ehviewer.download.DownloadManager as downloadManager
|
||||
|
||||
private val NO_REDUNDANCY = appCtx.getString(R.string.settings_download_clean_redundancy_no_redundancy)
|
||||
private val CLEAR_REDUNDANCY_DONE = { cnt: Int -> appCtx.getString(R.string.settings_download_clean_redundancy_done, cnt) }
|
||||
private val FINAL_CLEAR_REDUNDANCY_MSG = { cnt: Int -> if (cnt == 0) NO_REDUNDANCY else CLEAR_REDUNDANCY_DONE(cnt) }
|
||||
|
||||
class CleanRedundancyPreference @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : TaskPreference(context, attrs) {
|
||||
private fun clearFile(file: UniFile): Boolean {
|
||||
var name = file.name ?: return false
|
||||
val index = name.indexOf('-')
|
||||
if (index >= 0) {
|
||||
name = name.substring(0, index)
|
||||
}
|
||||
val gid = name.toLongOrNull() ?: return false
|
||||
if (downloadManager.containDownloadInfo(gid)) {
|
||||
return false
|
||||
}
|
||||
file.delete()
|
||||
return true
|
||||
}
|
||||
|
||||
private fun doRealWork(): Int {
|
||||
return downloadLocation.listFiles()?.sumOf { clearFile(it).compareTo(false) } ?: 0
|
||||
}
|
||||
|
||||
override fun launchJob() {
|
||||
if (singletonJob?.isActive == true) {
|
||||
singletonJob?.invokeOnCompletion {
|
||||
launchUI {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
singletonJob = launch {
|
||||
val cnt = doRealWork()
|
||||
withUIContext {
|
||||
showTip(FINAL_CLEAR_REDUNDANCY_MSG(cnt))
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var singletonJob: Job? = null
|
||||
@@ -1,56 +0,0 @@
|
||||
package com.hippo.ehviewer.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.util.AttributeSet
|
||||
import androidx.preference.Preference
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.systemDns
|
||||
import com.hippo.ehviewer.ui.legacy.EditTextDialogBuilder
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.dnsoverhttps.DnsOverHttps
|
||||
import java.net.InetAddress
|
||||
|
||||
class DohPreference @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : Preference(context, attrs) {
|
||||
override fun onClick() {
|
||||
val builder = EditTextDialogBuilder(context, Settings.dohUrl, context.getString(R.string.settings_advanced_dns_over_http_hint))
|
||||
builder.setTitle(R.string.settings_advanced_dns_over_http_title)
|
||||
builder.setPositiveButton(android.R.string.ok, null)
|
||||
val dialog = builder.create()
|
||||
dialog.show()
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
|
||||
val text = builder.text.trim()
|
||||
runCatching {
|
||||
doh = if (text.isNotBlank()) buildDoHDNS(text) else null
|
||||
}.onFailure {
|
||||
builder.setError("Invalid URL!")
|
||||
}.onSuccess {
|
||||
Settings.dohUrl = text
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDoHDNS(url: String): DnsOverHttps {
|
||||
return DnsOverHttps.Builder().apply {
|
||||
client(EhApplication.okHttpClient)
|
||||
url(url.toHttpUrl())
|
||||
post(true)
|
||||
systemDns(systemDns)
|
||||
}.build()
|
||||
}
|
||||
|
||||
private var doh: DnsOverHttps? = Settings.dohUrl.runCatching { buildDoHDNS(this) }.getOrNull()
|
||||
|
||||
object EhDoH {
|
||||
fun lookup(hostname: String): List<InetAddress>? = doh
|
||||
?.runCatching { lookup(hostname).takeIf { it.isNotEmpty() } }
|
||||
?.onFailure { it.printStackTrace() }
|
||||
?.getOrNull()
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
package com.hippo.ehviewer.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.Button
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.ui.SettingsActivity
|
||||
import com.hippo.ehviewer.ui.legacy.preference.DialogPreference
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
|
||||
class ImageLimitsPreference(context: Context, attrs: AttributeSet) :
|
||||
DialogPreference(context, attrs), View.OnClickListener {
|
||||
private val mActivity = context as SettingsActivity
|
||||
private val placeholder = context.getString(R.string.please_wait)
|
||||
private lateinit var resetButton: Button
|
||||
private lateinit var mDialog: AlertDialog
|
||||
|
||||
init {
|
||||
updateSummary()
|
||||
}
|
||||
|
||||
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
|
||||
super.onPrepareDialogBuilder(builder)
|
||||
builder.setMessage(placeholder)
|
||||
}
|
||||
|
||||
override fun onDialogCreated(dialog: AlertDialog) {
|
||||
super.onDialogCreated(dialog)
|
||||
mDialog = dialog
|
||||
resetButton = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
|
||||
resetButton.setOnClickListener(this)
|
||||
resetButton.isEnabled = false
|
||||
launchIOCatching {
|
||||
mActivity.getImageLimits()
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
super.onDialogClosed(positiveResult)
|
||||
updateSummary()
|
||||
}
|
||||
|
||||
private fun updateSummary() {
|
||||
summary = mActivity.mLimits?.run {
|
||||
mActivity.getString(R.string.image_limits_summary, current, maximum)
|
||||
} ?: mActivity.getString(R.string.image_limits_summary, 0, 0)
|
||||
}
|
||||
|
||||
private fun launchIOCatching(block: suspend () -> Unit) = mActivity.lifecycleScope.launchIO {
|
||||
runCatching {
|
||||
block()
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
withUIContext {
|
||||
mDialog.setMessage(it.message)
|
||||
}
|
||||
}.onSuccess {
|
||||
withUIContext {
|
||||
bind()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun bind() {
|
||||
val (current, maximum, resetCost) = mActivity.mLimits!!
|
||||
val (fundsGP, fundsC) = mActivity.mFunds!!
|
||||
val cost = if (fundsGP >= resetCost) "$resetCost GP" else "$resetCost Credits"
|
||||
val message = mActivity.getString(R.string.current_limits, "$current / $maximum", cost) +
|
||||
"\n" + mActivity.getString(R.string.current_funds, "$fundsGP+", fundsC)
|
||||
mDialog.setMessage(message)
|
||||
resetButton.isEnabled = resetCost in 1..maxOf(fundsGP, fundsC)
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
resetButton.isEnabled = false
|
||||
mDialog.setMessage(placeholder)
|
||||
launchIOCatching {
|
||||
mActivity.resetImageLimits()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,170 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.preference
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Context
|
||||
import android.content.DialogInterface
|
||||
import android.text.TextUtils
|
||||
import android.util.AttributeSet
|
||||
import android.view.View
|
||||
import android.widget.AutoCompleteTextView
|
||||
import android.widget.EditText
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.hippo.ehviewer.EhProxySelector
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.network.InetValidator
|
||||
import com.hippo.ehviewer.ui.legacy.preference.DialogPreference
|
||||
import com.hippo.ehviewer.yorozuya.MathUtils
|
||||
import com.hippo.ehviewer.yorozuya.ViewUtils
|
||||
|
||||
class ProxyPreference @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : DialogPreference(context, attrs), View.OnClickListener {
|
||||
private var mType: TextInputLayout? = null
|
||||
private var mIpInputLayout: TextInputLayout? = null
|
||||
private var mIp: EditText? = null
|
||||
private var mPortInputLayout: TextInputLayout? = null
|
||||
private var mPort: EditText? = null
|
||||
private val mArray: Array<String>
|
||||
|
||||
init {
|
||||
mArray = context.resources.getStringArray(R.array.proxy_types)
|
||||
dialogLayoutResource = R.layout.preference_dialog_proxy
|
||||
updateSummary(Settings.proxyType, Settings.proxyIp, Settings.proxyPort)
|
||||
}
|
||||
|
||||
private fun getProxyTypeText(type: Int): String {
|
||||
return mArray[MathUtils.clamp(type, 0, mArray.size - 1)]
|
||||
}
|
||||
|
||||
private fun updateSummary(type: Int, ip: String?, port: Int) {
|
||||
var type1 = type
|
||||
if ((type1 == EhProxySelector.TYPE_HTTP || type1 == EhProxySelector.TYPE_SOCKS) &&
|
||||
(TextUtils.isEmpty(ip) || !InetValidator.isValidInetPort(port))
|
||||
) {
|
||||
type1 = EhProxySelector.TYPE_SYSTEM
|
||||
}
|
||||
summary = if (type1 == EhProxySelector.TYPE_HTTP || type1 == EhProxySelector.TYPE_SOCKS) {
|
||||
val context = context
|
||||
context.getString(
|
||||
R.string.settings_advanced_proxy_summary_1,
|
||||
getProxyTypeText(type1),
|
||||
ip,
|
||||
port,
|
||||
)
|
||||
} else {
|
||||
val context = context
|
||||
context.getString(
|
||||
R.string.settings_advanced_proxy_summary_2,
|
||||
getProxyTypeText(type1),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
|
||||
super.onPrepareDialogBuilder(builder)
|
||||
builder.setPositiveButton(android.R.string.ok, null)
|
||||
}
|
||||
|
||||
@SuppressLint("SetTextI18n")
|
||||
override fun onDialogCreated(dialog: AlertDialog) {
|
||||
super.onDialogCreated(dialog)
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener(this)
|
||||
mType = ViewUtils.`$$`(dialog, R.id.type) as TextInputLayout
|
||||
val array = context.resources.getStringArray(R.array.proxy_types)
|
||||
mIpInputLayout = ViewUtils.`$$`(dialog, R.id.ip_input_layout) as TextInputLayout
|
||||
mIp = ViewUtils.`$$`(dialog, R.id.ip) as EditText
|
||||
mPortInputLayout = ViewUtils.`$$`(dialog, R.id.port_input_layout) as TextInputLayout
|
||||
mPort = ViewUtils.`$$`(dialog, R.id.port) as EditText
|
||||
val type = Settings.proxyType
|
||||
(mType!!.editText as AutoCompleteTextView).setText(
|
||||
array[
|
||||
MathUtils.clamp(
|
||||
type,
|
||||
0,
|
||||
array.size,
|
||||
),
|
||||
],
|
||||
false,
|
||||
)
|
||||
mIp!!.setText(Settings.proxyIp)
|
||||
val portString: String?
|
||||
val port = Settings.proxyPort
|
||||
portString = if (!InetValidator.isValidInetPort(port)) {
|
||||
null
|
||||
} else {
|
||||
port.toString()
|
||||
}
|
||||
mPort!!.setText(portString)
|
||||
}
|
||||
|
||||
override fun onDialogClosed(positiveResult: Boolean) {
|
||||
super.onDialogClosed(positiveResult)
|
||||
mType = null
|
||||
mIpInputLayout = null
|
||||
mIp = null
|
||||
mPortInputLayout = null
|
||||
mPort = null
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
val dialog = dialog
|
||||
val context: Context = context
|
||||
if (null == dialog || null == mType || null == mIpInputLayout || null == mIp || null == mPortInputLayout || null == mPort) {
|
||||
return
|
||||
}
|
||||
val type = mArray.indexOf(mType!!.editText!!.text.toString())
|
||||
val ip = mIp!!.text.toString().trim { it <= ' ' }
|
||||
if (ip.isEmpty()) {
|
||||
if (type == EhProxySelector.TYPE_HTTP || type == EhProxySelector.TYPE_SOCKS) {
|
||||
mIpInputLayout!!.error = context.getString(R.string.text_is_empty)
|
||||
return
|
||||
}
|
||||
}
|
||||
mIpInputLayout!!.error = null
|
||||
val port: Int
|
||||
val portString = mPort!!.text.toString().trim { it <= ' ' }
|
||||
if (portString.isEmpty()) {
|
||||
if (type == EhProxySelector.TYPE_HTTP || type == EhProxySelector.TYPE_SOCKS) {
|
||||
mPortInputLayout!!.error = context.getString(R.string.text_is_empty)
|
||||
return
|
||||
} else {
|
||||
port = -1
|
||||
}
|
||||
} else {
|
||||
port = try {
|
||||
portString.toInt()
|
||||
} catch (e: NumberFormatException) {
|
||||
-1
|
||||
}
|
||||
if (!InetValidator.isValidInetPort(port)) {
|
||||
mPortInputLayout!!.error = context.getString(R.string.proxy_invalid_port)
|
||||
return
|
||||
}
|
||||
}
|
||||
mPortInputLayout!!.error = null
|
||||
Settings.proxyType = type
|
||||
Settings.proxyIp = ip
|
||||
Settings.proxyPort = port
|
||||
updateSummary(type, ip, port)
|
||||
EhProxySelector.updateProxy()
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
@@ -1,131 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhEngine.fillGalleryListByApi
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.client.data.BaseGalleryInfo
|
||||
import com.hippo.ehviewer.client.data.GalleryInfo
|
||||
import com.hippo.ehviewer.download.DownloadManager
|
||||
import com.hippo.ehviewer.download.downloadLocation
|
||||
import com.hippo.ehviewer.spider.SpiderQueen
|
||||
import com.hippo.ehviewer.spider.readCompatFromUniFile
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.util.lang.launchUI
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import splitties.init.appCtx
|
||||
import com.hippo.ehviewer.download.DownloadManager as downloadManager
|
||||
|
||||
private val RESTORE_NOT_FOUND = appCtx.getString(R.string.settings_download_restore_not_found)
|
||||
private val RESTORE_FAILED = appCtx.getString(R.string.settings_download_restore_failed)
|
||||
private val RESTORE_COUNT_MSG = { cnt: Int -> if (cnt == 0) RESTORE_NOT_FOUND else appCtx.getString(R.string.settings_download_restore_successfully, cnt) }
|
||||
|
||||
class RestoreDownloadPreference @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : TaskPreference(context, attrs) {
|
||||
private val mManager: DownloadManager = downloadManager
|
||||
private var restoreDirCount = 0
|
||||
private fun getRestoreItem(file: UniFile?): RestoreItem? {
|
||||
if (null == file || !file.isDirectory) return null
|
||||
val siFile = file.findFile(SpiderQueen.SPIDER_INFO_FILENAME) ?: return null
|
||||
return runCatching {
|
||||
val spiderInfo = readCompatFromUniFile(siFile) ?: return null
|
||||
val gid = spiderInfo.gid
|
||||
val dirname = file.name ?: return null
|
||||
if (mManager.containDownloadInfo(gid)) {
|
||||
// Restore download dir to avoid redownload
|
||||
val dbdirname = EhDB.getDownloadDirname(gid)
|
||||
if (null == dbdirname || dirname != dbdirname) {
|
||||
EhDB.putDownloadDirname(gid, dirname)
|
||||
restoreDirCount++
|
||||
}
|
||||
return null
|
||||
}
|
||||
RestoreItem(dirname).also {
|
||||
it.gid = spiderInfo.gid
|
||||
it.token = spiderInfo.token
|
||||
}
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}.getOrNull()
|
||||
}
|
||||
|
||||
private suspend fun doRealWork(): List<GalleryInfo>? {
|
||||
val files = downloadLocation.listFiles() ?: return null
|
||||
return files.mapNotNull { getRestoreItem(it) }.apply {
|
||||
runSuspendCatching {
|
||||
fillGalleryListByApi(this, EhUrl.referer)
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private class RestoreItem(
|
||||
val dirname: String,
|
||||
) : BaseGalleryInfo()
|
||||
|
||||
override fun launchJob() {
|
||||
if (singletonJob?.isActive == true) {
|
||||
singletonJob?.invokeOnCompletion {
|
||||
launchUI {
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
} else {
|
||||
singletonJob = launch {
|
||||
val result = doRealWork()
|
||||
withUIContext {
|
||||
if (result == null) {
|
||||
showTip(RESTORE_FAILED)
|
||||
} else {
|
||||
if (result.isEmpty()) {
|
||||
showTip(RESTORE_COUNT_MSG(restoreDirCount))
|
||||
} else {
|
||||
var count = 0
|
||||
var i = 0
|
||||
val n = result.size
|
||||
while (i < n) {
|
||||
val item = result[i]
|
||||
// Avoid failed gallery info
|
||||
if (null != item.title) {
|
||||
// Put to download
|
||||
mManager.addDownload(item, null)
|
||||
// Put download dir to DB
|
||||
EhDB.putDownloadDirname(item.gid, (item as RestoreItem).dirname)
|
||||
count++
|
||||
}
|
||||
i++
|
||||
}
|
||||
showTip(RESTORE_COUNT_MSG(count + restoreDirCount))
|
||||
}
|
||||
}
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var singletonJob: Job? = null
|
||||
@@ -1,54 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.ui.SettingsActivity
|
||||
import com.hippo.ehviewer.ui.legacy.preference.DialogPreference
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.Job
|
||||
|
||||
abstract class TaskPreference @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : DialogPreference(context, attrs), CoroutineScope {
|
||||
override val coroutineContext = Dispatchers.IO + Job()
|
||||
protected lateinit var dialog: AlertDialog
|
||||
override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) {
|
||||
builder.setTitle(null)
|
||||
builder.setView(R.layout.preference_dialog_task)
|
||||
builder.setCancelable(false)
|
||||
}
|
||||
|
||||
abstract fun launchJob()
|
||||
|
||||
override fun onDialogCreated(dialog: AlertDialog) {
|
||||
this.dialog = dialog
|
||||
launchJob()
|
||||
}
|
||||
|
||||
protected fun showTip(msg: String) {
|
||||
(context as SettingsActivity).showTip(
|
||||
msg,
|
||||
Snackbar.LENGTH_SHORT,
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.preference.Preference
|
||||
import com.hippo.ehviewer.BuildConfig
|
||||
import com.hippo.ehviewer.R
|
||||
|
||||
class VersionPreference @JvmOverloads constructor(
|
||||
context: Context,
|
||||
attrs: AttributeSet? = null,
|
||||
) : Preference(context, attrs) {
|
||||
init {
|
||||
setTitle(R.string.settings_about_version)
|
||||
summary = "${BuildConfig.VERSION_NAME} (${BuildConfig.COMMIT_SHA})\n" +
|
||||
context.getString(R.string.settings_about_build_time, BuildConfig.BUILD_TIME)
|
||||
}
|
||||
}
|
||||
@@ -1,114 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.MenuItem
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.google.android.material.appbar.AppBarLayout
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.google.android.material.snackbar.Snackbar
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.parser.HomeParser
|
||||
import com.hippo.ehviewer.ui.fragment.SettingsFragment
|
||||
import com.hippo.ehviewer.ui.scene.BaseScene
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
|
||||
class SettingsActivity : EhActivity() {
|
||||
private var mFab: FloatingActionButton? = null
|
||||
private var mContentText: TextView? = null
|
||||
private var mAppbarLayout: AppBarLayout? = null
|
||||
var mLimits: HomeParser.Limits? = null
|
||||
private set
|
||||
var mFunds: HomeParser.Funds? = null
|
||||
private set
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
lifecycleScope.launchIO {
|
||||
if (EhCookieStore.hasSignedIn()) {
|
||||
runCatching {
|
||||
getImageLimits()
|
||||
}.exceptionOrNull()?.printStackTrace()
|
||||
}
|
||||
}
|
||||
setContentView(R.layout.activity_preference)
|
||||
setSupportActionBar(findViewById(R.id.toolbar))
|
||||
mContentText = findViewById(R.id.tip)
|
||||
mFab = findViewById(R.id.fab)
|
||||
mAppbarLayout = findViewById(R.id.appbar)
|
||||
supportActionBar?.setDisplayHomeAsUpEnabled(true)
|
||||
if (savedInstanceState == null) {
|
||||
supportFragmentManager
|
||||
.beginTransaction()
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_MATCH_ACTIVITY_OPEN)
|
||||
.replace(R.id.fragment, SettingsFragment())
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun getImageLimits() = EhEngine.getImageLimits().run {
|
||||
mLimits = limits
|
||||
mFunds = funds
|
||||
}
|
||||
|
||||
suspend fun resetImageLimits() {
|
||||
mLimits =
|
||||
EhEngine.resetImageLimits() ?: HomeParser.Limits(maximum = mLimits!!.maximum).also {
|
||||
withUIContext {
|
||||
showTip(R.string.reset_limits_succeed, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showTip(@StringRes id: Int, length: Int) {
|
||||
showTip(getString(id), length)
|
||||
}
|
||||
|
||||
fun showTip(message: CharSequence?, length: Int) {
|
||||
Snackbar.make(
|
||||
findViewById(R.id.snackbar),
|
||||
message!!,
|
||||
if (length == BaseScene.LENGTH_LONG) Snackbar.LENGTH_LONG else Snackbar.LENGTH_SHORT,
|
||||
).show()
|
||||
}
|
||||
|
||||
override fun onOptionsItemSelected(item: MenuItem): Boolean {
|
||||
if (item.itemId == android.R.id.home) {
|
||||
onBackPressedDispatcher.onBackPressed()
|
||||
return true
|
||||
}
|
||||
return super.onOptionsItemSelected(item)
|
||||
}
|
||||
|
||||
fun getMFab(): FloatingActionButton {
|
||||
return mFab!!
|
||||
}
|
||||
|
||||
fun getMContentText(): TextView {
|
||||
return mContentText!!
|
||||
}
|
||||
|
||||
fun resetAppbarLiftStatus() {
|
||||
mAppbarLayout?.setExpanded(true)
|
||||
}
|
||||
}
|
||||
@@ -1,40 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.text.Html
|
||||
import androidx.preference.Preference
|
||||
import com.hippo.ehviewer.R
|
||||
|
||||
class AboutFragment : BasePreferenceFragment() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.about_settings)
|
||||
val author = findPreference<Preference>(KEY_AUTHOR)
|
||||
author!!.summary = Html.fromHtml(
|
||||
getString(R.string.settings_about_author_summary).replace('$', '@'),
|
||||
Html.FROM_HTML_MODE_LEGACY,
|
||||
)
|
||||
}
|
||||
|
||||
override val fragmentTitle: Int
|
||||
get() = R.string.settings_about
|
||||
|
||||
companion object {
|
||||
private const val KEY_AUTHOR = "author"
|
||||
private const val KEY_CHECK_FOR_UPDATES = "check_for_updates"
|
||||
}
|
||||
}
|
||||
@@ -1,369 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.provider.Settings
|
||||
import android.util.Log
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.core.os.LocaleListCompat
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.Preference
|
||||
import com.hippo.ehviewer.AppConfig
|
||||
import com.hippo.ehviewer.BuildConfig
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder
|
||||
import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder
|
||||
import com.hippo.ehviewer.ui.scene.BaseScene
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import com.hippo.ehviewer.util.LogCat
|
||||
import com.hippo.ehviewer.util.ReadableTime
|
||||
import com.hippo.ehviewer.yorozuya.IOUtils
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.delay
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import java.io.BufferedInputStream
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import java.io.FileInputStream
|
||||
import java.io.FileOutputStream
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
import com.hippo.ehviewer.Settings as AppSettings
|
||||
|
||||
class AdvancedFragment : BasePreferenceFragment() {
|
||||
private var exportLauncher = registerForActivityResult<String, Uri>(
|
||||
ActivityResultContracts.CreateDocument("application/vnd.sqlite3"),
|
||||
) { uri: Uri? ->
|
||||
if (uri != null) {
|
||||
try {
|
||||
// grantUriPermission might throw RemoteException on MIUI
|
||||
requireActivity().grantUriPermission(
|
||||
BuildConfig.APPLICATION_ID,
|
||||
uri,
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
e.printStackTrace()
|
||||
}
|
||||
try {
|
||||
val alertDialog = BaseDialogBuilder(requireActivity())
|
||||
.setCancelable(false)
|
||||
.setView(R.layout.preference_dialog_task)
|
||||
.show()
|
||||
lifecycleScope.launchIO {
|
||||
val e = runSuspendCatching {
|
||||
EhDB.exportDB(requireActivity(), uri)
|
||||
}.exceptionOrNull()?.run {
|
||||
printStackTrace()
|
||||
getString(R.string.settings_advanced_export_data_failed)
|
||||
}
|
||||
withUIContext {
|
||||
if (alertDialog.isShowing) {
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
showTip(
|
||||
e ?: getString(R.string.settings_advanced_export_data_to, uri.toString()),
|
||||
BaseScene.LENGTH_SHORT,
|
||||
)
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showTip(R.string.settings_advanced_export_data_failed, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
private var dumpLogcatLauncher = registerForActivityResult<String, Uri>(
|
||||
ActivityResultContracts.CreateDocument("application/zip"),
|
||||
) { uri: Uri? ->
|
||||
if (uri != null) {
|
||||
try {
|
||||
// grantUriPermission might throw RemoteException on MIUI
|
||||
requireActivity().grantUriPermission(
|
||||
BuildConfig.APPLICATION_ID,
|
||||
uri,
|
||||
Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
e.printStackTrace()
|
||||
}
|
||||
try {
|
||||
val zipFile = File(AppConfig.externalTempDir, "logs.zip")
|
||||
if (zipFile.exists()) {
|
||||
zipFile.delete()
|
||||
}
|
||||
val files = ArrayList<File>()
|
||||
AppConfig.externalParseErrorDir?.listFiles()?.let { files.addAll(it) }
|
||||
AppConfig.externalCrashDir?.listFiles()?.let { files.addAll(it) }
|
||||
var finished = false
|
||||
var origin: BufferedInputStream? = null
|
||||
var out: ZipOutputStream? = null
|
||||
try {
|
||||
val dest = FileOutputStream(zipFile)
|
||||
out = ZipOutputStream(BufferedOutputStream(dest))
|
||||
val bytes = ByteArray(1024 * 64)
|
||||
for (file in files) {
|
||||
if (!file.isFile) {
|
||||
continue
|
||||
}
|
||||
try {
|
||||
val fi = FileInputStream(file)
|
||||
origin = BufferedInputStream(fi, bytes.size)
|
||||
val entry = ZipEntry(file.name)
|
||||
out.putNextEntry(entry)
|
||||
var count: Int
|
||||
while (origin.read(bytes, 0, bytes.size).also { count = it } != -1) {
|
||||
out.write(bytes, 0, count)
|
||||
}
|
||||
origin.close()
|
||||
origin = null
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
val entry =
|
||||
ZipEntry("logcat-" + ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".txt")
|
||||
out.putNextEntry(entry)
|
||||
LogCat.save(out)
|
||||
out.closeEntry()
|
||||
out.close()
|
||||
IOUtils.copy(
|
||||
FileInputStream(zipFile),
|
||||
requireActivity().contentResolver.openOutputStream(uri),
|
||||
)
|
||||
finished = true
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
} finally {
|
||||
origin?.close()
|
||||
out?.close()
|
||||
}
|
||||
if (!finished) {
|
||||
finished = LogCat.save(requireActivity().contentResolver.openOutputStream(uri))
|
||||
}
|
||||
showTip(
|
||||
if (finished) {
|
||||
getString(
|
||||
R.string.settings_advanced_dump_logcat_to,
|
||||
uri.toString(),
|
||||
)
|
||||
} else {
|
||||
getString(R.string.settings_advanced_dump_logcat_failed)
|
||||
},
|
||||
BaseScene.LENGTH_SHORT,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
showTip(
|
||||
getString(R.string.settings_advanced_dump_logcat_failed),
|
||||
BaseScene.LENGTH_SHORT,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
private var importDataLauncher = registerForActivityResult<Array<String>, Uri>(
|
||||
ActivityResultContracts.OpenDocument(),
|
||||
) { uri: Uri? ->
|
||||
if (uri != null) {
|
||||
try {
|
||||
// grantUriPermission might throw RemoteException on MIUI
|
||||
requireActivity().grantUriPermission(
|
||||
BuildConfig.APPLICATION_ID,
|
||||
uri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION,
|
||||
)
|
||||
} catch (e: Exception) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
e.printStackTrace()
|
||||
}
|
||||
try {
|
||||
val alertDialog = BaseDialogBuilder(requireActivity())
|
||||
.setCancelable(false)
|
||||
.setView(R.layout.preference_dialog_task)
|
||||
.show()
|
||||
lifecycleScope.launchIO {
|
||||
val error = runSuspendCatching {
|
||||
EhDB.importDB(requireActivity(), uri)
|
||||
}.exceptionOrNull()?.run {
|
||||
printStackTrace()
|
||||
getString(R.string.cant_read_the_file)
|
||||
}
|
||||
withUIContext {
|
||||
if (alertDialog.isShowing) {
|
||||
alertDialog.dismiss()
|
||||
}
|
||||
if (null == error) {
|
||||
showTip(getString(R.string.settings_advanced_import_data_successfully), BaseScene.LENGTH_SHORT)
|
||||
} else {
|
||||
showTip(error, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
showTip(e.localizedMessage, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.advanced_settings)
|
||||
val dumpLogcat = findPreference<Preference>(KEY_DUMP_LOGCAT)
|
||||
val appLanguage = findPreference<Preference>(KEY_APP_LANGUAGE)
|
||||
val importData = findPreference<Preference>(KEY_IMPORT_DATA)
|
||||
val exportData = findPreference<Preference>(KEY_EXPORT_DATA)
|
||||
val backupFavorite = findPreference<Preference>(KEY_BACKUP_FAVORITE)
|
||||
val openByDefault = findPreference<Preference>(KEY_OPEN_BY_DEFAULT)
|
||||
val domainFronting = findPreference<Preference>(AppSettings.KEY_DOMAIN_FRONTING)
|
||||
val bypassVpn = findPreference<Preference>(AppSettings.KEY_BYPASS_VPN)
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.S) {
|
||||
openByDefault!!.isVisible = false
|
||||
} else {
|
||||
openByDefault!!.onPreferenceClickListener = this
|
||||
}
|
||||
dumpLogcat!!.onPreferenceClickListener = this
|
||||
importData!!.onPreferenceClickListener = this
|
||||
exportData!!.onPreferenceClickListener = this
|
||||
backupFavorite!!.onPreferenceClickListener = this
|
||||
appLanguage!!.onPreferenceChangeListener = this
|
||||
bypassVpn!!.isVisible = AppSettings.dF
|
||||
domainFronting!!.setOnPreferenceChangeListener { _, newValue ->
|
||||
bypassVpn.isVisible = newValue as Boolean
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreferenceClick(preference: Preference): Boolean {
|
||||
val key = preference.key
|
||||
if (KEY_DUMP_LOGCAT == key) {
|
||||
try {
|
||||
dumpLogcatLauncher.launch("log-" + ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".zip")
|
||||
} catch (e: Throwable) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
showTip(R.string.error_cant_find_activity, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
return true
|
||||
} else if (KEY_IMPORT_DATA == key) {
|
||||
try {
|
||||
importDataLauncher.launch(arrayOf("*/*"))
|
||||
} catch (e: Throwable) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
showTip(R.string.error_cant_find_activity, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
return true
|
||||
} else if (KEY_EXPORT_DATA == key) {
|
||||
try {
|
||||
exportLauncher.launch(ReadableTime.getFilenamableTime(System.currentTimeMillis()) + ".db")
|
||||
} catch (e: Throwable) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
showTip(R.string.error_cant_find_activity, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
return true
|
||||
} else if (KEY_BACKUP_FAVORITE == key) {
|
||||
try {
|
||||
backupFavorite()
|
||||
} catch (e: Exception) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
showTip(R.string.settings_advanced_backup_favorite_failed, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
return true
|
||||
} else if (KEY_OPEN_BY_DEFAULT == key) {
|
||||
try {
|
||||
@SuppressLint("InlinedApi")
|
||||
val intent = Intent(
|
||||
Settings.ACTION_APP_OPEN_BY_DEFAULT_SETTINGS,
|
||||
Uri.parse("package:" + requireContext().packageName),
|
||||
)
|
||||
startActivity(intent)
|
||||
} catch (t: Throwable) {
|
||||
val intent = Intent(
|
||||
Settings.ACTION_APPLICATION_DETAILS_SETTINGS,
|
||||
Uri.parse("package:" + requireContext().packageName),
|
||||
)
|
||||
startActivity(intent)
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun backupFavorite() {
|
||||
val favListUrlBuilder = FavListUrlBuilder()
|
||||
var favTotal = 0
|
||||
var favIndex = 0
|
||||
tailrec suspend fun doBackup() {
|
||||
val result = EhEngine.getFavorites(favListUrlBuilder.build())
|
||||
if (result.galleryInfoList.isEmpty()) {
|
||||
showTip(R.string.settings_advanced_backup_favorite_nothing, BaseScene.LENGTH_SHORT)
|
||||
} else {
|
||||
if (favTotal == 0) favTotal = result.countArray.sum()
|
||||
favIndex += result.galleryInfoList.size
|
||||
val status = "($favIndex/$favTotal)"
|
||||
Log.d("LocalFavorites", "now backup page $status")
|
||||
EhDB.putLocalFavorites(result.galleryInfoList)
|
||||
showTip(getString(R.string.settings_advanced_backup_favorite_start, status), BaseScene.LENGTH_SHORT)
|
||||
if (result.next != null) {
|
||||
delay(AppSettings.downloadDelay.toLong())
|
||||
favListUrlBuilder.setIndex(result.next, true)
|
||||
doBackup()
|
||||
}
|
||||
}
|
||||
}
|
||||
viewLifecycleOwner.lifecycleScope.launchIO {
|
||||
runSuspendCatching {
|
||||
doBackup()
|
||||
}.onSuccess {
|
||||
showTip(R.string.settings_advanced_backup_favorite_success, BaseScene.LENGTH_SHORT)
|
||||
}.onFailure {
|
||||
showTip(R.string.settings_advanced_backup_favorite_failed, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
|
||||
val key = preference.key
|
||||
if (KEY_APP_LANGUAGE == key) {
|
||||
if ("system" == newValue) {
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.getEmptyLocaleList())
|
||||
} else {
|
||||
AppCompatDelegate.setApplicationLocales(LocaleListCompat.forLanguageTags(newValue as String))
|
||||
}
|
||||
requireActivity().recreate()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override val fragmentTitle: Int
|
||||
get() = R.string.settings_advanced
|
||||
|
||||
companion object {
|
||||
private const val KEY_DUMP_LOGCAT = "dump_logcat"
|
||||
private const val KEY_APP_LANGUAGE = "app_language"
|
||||
private const val KEY_IMPORT_DATA = "import_data"
|
||||
private const val KEY_EXPORT_DATA = "export_data"
|
||||
private const val KEY_OPEN_BY_DEFAULT = "open_by_default"
|
||||
private const val KEY_BACKUP_FAVORITE = "backup_favorite"
|
||||
}
|
||||
}
|
||||
@@ -1,73 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Tarsin Norbin
|
||||
*
|
||||
* This file is part of EhViewer
|
||||
*
|
||||
* EhViewer is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* EhViewer is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with EhViewer.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.view.View
|
||||
import android.widget.TextView
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.Fragment
|
||||
import com.google.android.material.floatingactionbutton.FloatingActionButton
|
||||
import com.hippo.ehviewer.ui.SettingsActivity
|
||||
|
||||
abstract class BaseFragment : Fragment() {
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
setTitle(getFragmentTitle())
|
||||
(requireActivity() as SettingsActivity).resetAppbarLiftStatus()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
hideFabAndClearTip()
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
abstract fun getFragmentTitle(): Int
|
||||
|
||||
private fun setTitle(@StringRes string: Int) {
|
||||
requireActivity().setTitle(string)
|
||||
}
|
||||
|
||||
fun showTip(@StringRes id: Int, length: Int) {
|
||||
(requireActivity() as SettingsActivity).showTip(getString(id), length)
|
||||
}
|
||||
|
||||
fun showTip(message: CharSequence?, length: Int) {
|
||||
(requireActivity() as SettingsActivity).showTip(message, length)
|
||||
}
|
||||
|
||||
fun getTipView(@StringRes res: Int? = null): TextView {
|
||||
return (requireActivity() as SettingsActivity).getMContentText().apply {
|
||||
visibility = View.VISIBLE
|
||||
res?.let { text = getString(it) }
|
||||
}
|
||||
}
|
||||
|
||||
fun getFabViewAndShow(): FloatingActionButton {
|
||||
return (requireActivity() as SettingsActivity).getMFab().apply { visibility = View.VISIBLE }
|
||||
}
|
||||
|
||||
private fun hideFabAndClearTip() {
|
||||
getFabViewAndShow().apply {
|
||||
visibility = View.GONE
|
||||
setOnClickListener(null)
|
||||
}
|
||||
getTipView().apply {
|
||||
visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Tarsin Norbin
|
||||
*
|
||||
* This file is part of EhViewer
|
||||
*
|
||||
* EhViewer is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* EhViewer is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with EhViewer.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.fragment.app.FragmentTransaction
|
||||
import androidx.preference.Preference
|
||||
import androidx.preference.PreferenceFragmentCompat
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.ui.SettingsActivity
|
||||
|
||||
open class BasePreferenceFragment :
|
||||
PreferenceFragmentCompat(),
|
||||
Preference.OnPreferenceClickListener,
|
||||
Preference.OnPreferenceChangeListener {
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
setTitle(fragmentTitle)
|
||||
}
|
||||
|
||||
@get:StringRes
|
||||
open val fragmentTitle: Int
|
||||
get() = -1
|
||||
|
||||
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onPreferenceClick(preference: Preference): Boolean {
|
||||
return false
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {}
|
||||
|
||||
override fun onPreferenceTreeClick(preference: Preference): Boolean {
|
||||
val fragment = when (preference.key) {
|
||||
"eh" -> EhFragment()
|
||||
"download" -> DownloadFragment()
|
||||
"privacy" -> PrivacyFragment()
|
||||
"advance" -> AdvancedFragment()
|
||||
"about" -> AboutFragment()
|
||||
"filter" -> FilterFragment()
|
||||
"mytags" -> MyTagsFragment()
|
||||
"uconfig" -> UConfigFragment()
|
||||
"license" -> LicenseFragment()
|
||||
else -> null
|
||||
}
|
||||
fragment?.let {
|
||||
requireActivity().supportFragmentManager
|
||||
.beginTransaction()
|
||||
.setTransition(FragmentTransaction.TRANSIT_FRAGMENT_FADE)
|
||||
.replace(R.id.fragment, it)
|
||||
.addToBackStack(null)
|
||||
.commitAllowingStateLoss()
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
private fun setTitle(@StringRes string: Int) {
|
||||
requireActivity().setTitle(string)
|
||||
}
|
||||
|
||||
fun showTip(@StringRes id: Int, length: Int) {
|
||||
(requireActivity() as SettingsActivity).showTip(getString(id), length)
|
||||
}
|
||||
|
||||
fun showTip(message: CharSequence?, length: Int) {
|
||||
(requireActivity() as SettingsActivity).showTip(message, length)
|
||||
}
|
||||
}
|
||||
@@ -1,142 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.Preference
|
||||
import com.hippo.ehviewer.AppConfig
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.download.downloadLocation
|
||||
import com.hippo.ehviewer.ui.keepNoMediaFileStatus
|
||||
import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder
|
||||
import com.hippo.ehviewer.ui.scene.BaseScene
|
||||
import com.hippo.ehviewer.util.ExceptionUtils
|
||||
import com.hippo.unifile.UniFile
|
||||
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
||||
|
||||
class DownloadFragment : BasePreferenceFragment() {
|
||||
private var mDownloadLocation: Preference? = null
|
||||
private var pickImageDirLauncher = registerForActivityResult<Uri?, Uri>(
|
||||
ActivityResultContracts.OpenDocumentTree(),
|
||||
) { treeUri: Uri? ->
|
||||
if (treeUri != null) {
|
||||
requireActivity().contentResolver.takePersistableUriPermission(
|
||||
treeUri,
|
||||
Intent.FLAG_GRANT_READ_URI_PERMISSION or Intent.FLAG_GRANT_WRITE_URI_PERMISSION,
|
||||
)
|
||||
val uniFile = UniFile.fromTreeUri(activity, treeUri)
|
||||
if (uniFile != null) {
|
||||
downloadLocation = uniFile
|
||||
lifecycleScope.launchNonCancellable {
|
||||
keepNoMediaFileStatus()
|
||||
}
|
||||
onUpdateDownloadLocation()
|
||||
} else {
|
||||
showTip(
|
||||
R.string.settings_download_cant_get_download_location,
|
||||
BaseScene.LENGTH_SHORT,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.download_settings)
|
||||
val mediaScan = findPreference<Preference>(Settings.KEY_MEDIA_SCAN)
|
||||
mDownloadLocation = findPreference(KEY_DOWNLOAD_LOCATION)
|
||||
onUpdateDownloadLocation()
|
||||
mediaScan!!.onPreferenceChangeListener = this
|
||||
if (mDownloadLocation != null) {
|
||||
mDownloadLocation!!.onPreferenceClickListener = this
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroy() {
|
||||
super.onDestroy()
|
||||
mDownloadLocation = null
|
||||
}
|
||||
|
||||
private fun onUpdateDownloadLocation() {
|
||||
mDownloadLocation?.summary = downloadLocation.uri.toString()
|
||||
}
|
||||
|
||||
override fun onPreferenceClick(preference: Preference): Boolean {
|
||||
val key = preference.key
|
||||
if (KEY_DOWNLOAD_LOCATION == key) {
|
||||
val file = downloadLocation
|
||||
if (!UniFile.isFileUri(downloadLocation.uri)) {
|
||||
BaseDialogBuilder(requireContext())
|
||||
.setTitle(R.string.settings_download_download_location)
|
||||
.setMessage(file.uri.toString())
|
||||
.setPositiveButton(R.string.pick_new_download_location) { _, _ -> openDirPickerL() }
|
||||
.setNeutralButton(R.string.reset_download_location) { _, _ ->
|
||||
val uniFile = UniFile.fromFile(AppConfig.defaultDownloadDir)
|
||||
if (uniFile != null) {
|
||||
downloadLocation = uniFile
|
||||
lifecycleScope.launchNonCancellable {
|
||||
keepNoMediaFileStatus()
|
||||
}
|
||||
onUpdateDownloadLocation()
|
||||
} else {
|
||||
showTip(
|
||||
R.string.settings_download_cant_get_download_location,
|
||||
BaseScene.LENGTH_SHORT,
|
||||
)
|
||||
}
|
||||
}
|
||||
.show()
|
||||
} else {
|
||||
openDirPickerL()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun openDirPickerL() {
|
||||
try {
|
||||
pickImageDirLauncher.launch(null)
|
||||
} catch (e: Throwable) {
|
||||
ExceptionUtils.throwIfFatal(e)
|
||||
showTip(R.string.error_cant_find_activity, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
|
||||
val key = preference.key
|
||||
if (Settings.KEY_MEDIA_SCAN == key) {
|
||||
if (newValue is Boolean) {
|
||||
lifecycleScope.launchNonCancellable {
|
||||
keepNoMediaFileStatus()
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
override val fragmentTitle: Int
|
||||
get() = R.string.settings_download
|
||||
|
||||
companion object {
|
||||
const val KEY_DOWNLOAD_LOCATION = "download_location"
|
||||
}
|
||||
}
|
||||
@@ -1,150 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.app.Activity
|
||||
import android.content.res.Configuration
|
||||
import android.os.Bundle
|
||||
import android.text.format.DateFormat.is24HourFormat
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import androidx.preference.ListPreference
|
||||
import androidx.preference.Preference
|
||||
import com.google.android.material.timepicker.MaterialTimePicker
|
||||
import com.google.android.material.timepicker.MaterialTimePicker.INPUT_MODE_CLOCK
|
||||
import com.google.android.material.timepicker.TimeFormat.CLOCK_12H
|
||||
import com.google.android.material.timepicker.TimeFormat.CLOCK_24H
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.dailycheck.schedHour
|
||||
import com.hippo.ehviewer.dailycheck.schedMinute
|
||||
import com.hippo.ehviewer.dailycheck.updateDailyCheckWork
|
||||
import eu.kanade.tachiyomi.util.lang.launchNonCancellable
|
||||
|
||||
class EhFragment : BasePreferenceFragment() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.eh_settings)
|
||||
val account = findPreference<Preference>(Settings.KEY_ACCOUNT)
|
||||
val theme = findPreference<Preference>(Settings.KEY_THEME)
|
||||
val blackDarkTheme = findPreference<Preference>(Settings.KEY_BLACK_DARK_THEME)
|
||||
val gallerySite = findPreference<Preference>(Settings.KEY_GALLERY_SITE)
|
||||
val listMode = findPreference<Preference>(Settings.KEY_LIST_MODE)
|
||||
val listThumbSize = findPreference<Preference>(Settings.KEY_LIST_THUMB_SIZE)
|
||||
val detailSize = findPreference<Preference>(Settings.KEY_DETAIL_SIZE)
|
||||
val thumbSize = findPreference<Preference>(Settings.KEY_THUMB_SIZE)
|
||||
val thumbResolution = findPreference<Preference>(Settings.KEY_THUMB_RESOLUTION)
|
||||
val showTagTranslations = findPreference<Preference>(Settings.KEY_SHOW_TAG_TRANSLATIONS)
|
||||
val tagTranslationsSource = findPreference<Preference>(Settings.KEY_TAG_TRANSLATIONS_SOURCE)
|
||||
Settings.displayName?.let { account?.summary = it }
|
||||
theme!!.onPreferenceChangeListener = this
|
||||
gallerySite!!.onPreferenceChangeListener = this
|
||||
listMode!!.onPreferenceChangeListener = this
|
||||
listThumbSize!!.onPreferenceChangeListener = this
|
||||
detailSize!!.onPreferenceChangeListener = this
|
||||
thumbSize!!.onPreferenceChangeListener = this
|
||||
showTagTranslations!!.onPreferenceChangeListener = this
|
||||
blackDarkTheme!!.onPreferenceChangeListener = this
|
||||
thumbResolution!!.setSummaryProvider {
|
||||
getString(R.string.settings_eh_thumb_resolution_summary, (it as ListPreference).entry)
|
||||
}
|
||||
|
||||
if (!EhTagDatabase.isTranslatable(requireActivity())) {
|
||||
preferenceScreen.removePreference(showTagTranslations)
|
||||
preferenceScreen.removePreference(tagTranslationsSource!!)
|
||||
}
|
||||
if (!EhCookieStore.hasSignedIn()) {
|
||||
Settings.SIGN_IN_REQUIRED.forEach {
|
||||
val preference = findPreference<Preference>(it)
|
||||
preferenceScreen.removePreference(preference!!)
|
||||
}
|
||||
} else {
|
||||
findPreference<Preference>(Settings.KEY_REQUEST_NEWS_TIMER)!!.apply {
|
||||
setOnPreferenceClickListener {
|
||||
MaterialTimePicker.Builder()
|
||||
.apply {
|
||||
schedHour?.let { setHour(it) }
|
||||
schedMinute?.let { setMinute(it) }
|
||||
}
|
||||
.setTimeFormat(if (is24HourFormat(requireContext())) CLOCK_24H else CLOCK_12H)
|
||||
.setInputMode(INPUT_MODE_CLOCK)
|
||||
.build()
|
||||
.apply {
|
||||
addOnPositiveButtonClickListener {
|
||||
Settings.requestNewsTimerHour = hour
|
||||
Settings.requestNewsTimerMinute = minute
|
||||
updateDailyCheckWork(requireContext())
|
||||
}
|
||||
}
|
||||
.show(childFragmentManager, null)
|
||||
false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun onPreferenceChange(preference: Preference, newValue: Any): Boolean {
|
||||
val key = preference.key
|
||||
if (Settings.KEY_THEME == key) {
|
||||
AppCompatDelegate.setDefaultNightMode((newValue as String).toInt())
|
||||
requireActivity().recreate()
|
||||
return true
|
||||
} else if (Settings.KEY_GALLERY_SITE == key) {
|
||||
requireActivity().setResult(Activity.RESULT_OK)
|
||||
lifecycleScope.launchNonCancellable {
|
||||
runCatching {
|
||||
EhEngine.getUConfig()
|
||||
}.onFailure {
|
||||
it.printStackTrace()
|
||||
}
|
||||
}
|
||||
return true
|
||||
} else if (Settings.KEY_LIST_MODE == key) {
|
||||
requireActivity().setResult(Activity.RESULT_OK)
|
||||
return true
|
||||
} else if (Settings.KEY_LIST_THUMB_SIZE == key) {
|
||||
requireActivity().setResult(Activity.RESULT_OK)
|
||||
} else if (Settings.KEY_DETAIL_SIZE == key) {
|
||||
requireActivity().setResult(Activity.RESULT_OK)
|
||||
} else if (Settings.KEY_THUMB_SIZE == key) {
|
||||
requireActivity().setResult(Activity.RESULT_OK)
|
||||
} else if (Settings.KEY_SHOW_TAG_TRANSLATIONS == key) {
|
||||
if (java.lang.Boolean.TRUE == newValue) {
|
||||
lifecycleScope.launchNonCancellable { EhTagDatabase.update() }
|
||||
}
|
||||
} else if (Settings.KEY_BLACK_DARK_THEME == key) {
|
||||
if (requireActivity().resources.configuration.uiMode and Configuration.UI_MODE_NIGHT_YES > 0) {
|
||||
val nightMode = AppCompatDelegate.getDefaultNightMode()
|
||||
AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_AUTO_TIME)
|
||||
AppCompatDelegate.setDefaultNightMode(nightMode)
|
||||
requireActivity().recreate()
|
||||
}
|
||||
return true
|
||||
} else if (Settings.KEY_REQUEST_NEWS == key) {
|
||||
updateDailyCheckWork(requireContext())
|
||||
} else if (Settings.KEY_REQUEST_NEWS_TIMER == key) {
|
||||
updateDailyCheckWork(requireContext())
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
@get:StringRes
|
||||
override val fragmentTitle: Int
|
||||
get() = R.string.settings_eh
|
||||
}
|
||||
@@ -1,431 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Tarsin Norbin
|
||||
*
|
||||
* This file is part of EhViewer
|
||||
*
|
||||
* EhViewer is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* EhViewer is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with EhViewer.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.content.DialogInterface
|
||||
import android.os.Bundle
|
||||
import android.text.TextUtils
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.EditText
|
||||
import android.widget.ImageView
|
||||
import android.widget.TextView
|
||||
import androidx.appcompat.app.AlertDialog
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import androidx.recyclerview.widget.RecyclerView
|
||||
import com.google.android.material.checkbox.MaterialCheckBox
|
||||
import com.google.android.material.textfield.TextInputLayout
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhFilter
|
||||
import com.hippo.ehviewer.dao.Filter
|
||||
import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder
|
||||
import com.hippo.ehviewer.ui.legacy.ViewTransition
|
||||
import com.hippo.ehviewer.yorozuya.ViewUtils
|
||||
|
||||
class FilterFragment : BaseFragment() {
|
||||
private var mViewTransition: ViewTransition? = null
|
||||
private var mAdapter: FilterAdapter = FilterAdapter()
|
||||
private var mFilterList: FilterList = FilterList()
|
||||
private val mMenuProvider: MenuProvider = FilterMenuProvider()
|
||||
|
||||
inner class FilterMenuProvider : MenuProvider {
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.activity_filter, menu)
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
val itemId = menuItem.itemId
|
||||
if (itemId == R.id.action_tip) {
|
||||
showTipDialog()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
val recyclerView =
|
||||
inflater.inflate(R.layout.rv_layout, container, false) as RecyclerView
|
||||
val tip = getTipView(R.string.filter)
|
||||
mViewTransition =
|
||||
ViewTransition(recyclerView, tip)
|
||||
val fab = getFabViewAndShow()
|
||||
val drawable = ContextCompat.getDrawable(requireActivity(), R.drawable.big_filter)
|
||||
drawable?.setBounds(0, 0, drawable.intrinsicWidth, drawable.intrinsicHeight)
|
||||
tip.setCompoundDrawables(null, drawable, null, null)
|
||||
mAdapter.setHasStableIds(true)
|
||||
recyclerView.adapter = mAdapter
|
||||
recyclerView.layoutManager = LinearLayoutManager(requireContext())
|
||||
recyclerView.setHasFixedSize(true)
|
||||
val defaultItemAnimator = recyclerView.itemAnimator as DefaultItemAnimator?
|
||||
defaultItemAnimator?.supportsChangeAnimations = false
|
||||
fab.setOnClickListener { showAddFilterDialog() }
|
||||
return recyclerView
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
updateView(false)
|
||||
requireActivity().addMenuProvider(mMenuProvider)
|
||||
}
|
||||
|
||||
private fun updateView(animation: Boolean) {
|
||||
if (null == mViewTransition) {
|
||||
return
|
||||
}
|
||||
if (0 == mFilterList.size()) {
|
||||
mViewTransition!!.showView(1, animation)
|
||||
} else {
|
||||
mViewTransition!!.showView(0, animation)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
mViewTransition = null
|
||||
requireActivity().removeMenuProvider(mMenuProvider)
|
||||
super.onDestroyView()
|
||||
}
|
||||
|
||||
private fun showTipDialog() {
|
||||
BaseDialogBuilder(requireActivity())
|
||||
.setTitle(R.string.filter)
|
||||
.setMessage(R.string.filter_tip)
|
||||
.setPositiveButton(android.R.string.ok, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
private fun showAddFilterDialog() {
|
||||
val dialog = BaseDialogBuilder(requireActivity())
|
||||
.setTitle(R.string.add_filter)
|
||||
.setView(R.layout.dialog_add_filter)
|
||||
.setPositiveButton(R.string.add, null)
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
AddFilterDialogHelper(dialog)
|
||||
}
|
||||
|
||||
private fun showDeleteFilterDialog(filter: Filter) {
|
||||
val message = getString(R.string.delete_filter, filter.text)
|
||||
BaseDialogBuilder(requireActivity())
|
||||
.setMessage(message)
|
||||
.setPositiveButton(R.string.delete) { _: DialogInterface?, which: Int ->
|
||||
if (DialogInterface.BUTTON_POSITIVE != which) {
|
||||
return@setPositiveButton
|
||||
}
|
||||
mFilterList.delete(filter)
|
||||
mAdapter.notifyDataSetChanged()
|
||||
updateView(true)
|
||||
}
|
||||
.setNegativeButton(android.R.string.cancel, null)
|
||||
.show()
|
||||
}
|
||||
|
||||
override fun getFragmentTitle(): Int {
|
||||
return R.string.filter
|
||||
}
|
||||
|
||||
private class FilterHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
|
||||
val checkbox: MaterialCheckBox?
|
||||
val text: TextView?
|
||||
val delete: ImageView?
|
||||
|
||||
init {
|
||||
checkbox = itemView.findViewById(R.id.checkbox)
|
||||
text = itemView.findViewById(R.id.text)
|
||||
delete = itemView.findViewById(R.id.delete)
|
||||
}
|
||||
}
|
||||
|
||||
private inner class AddFilterDialogHelper(dialog: AlertDialog) : View.OnClickListener {
|
||||
private var mDialog: AlertDialog
|
||||
private var mSpinner: TextInputLayout
|
||||
private var mInputLayout: TextInputLayout
|
||||
private var mEditText: EditText
|
||||
private val mArray: Array<String> =
|
||||
context!!.resources.getStringArray(R.array.filter_entries)
|
||||
|
||||
init {
|
||||
mDialog = dialog
|
||||
mSpinner = ViewUtils.`$$`(dialog, R.id.spinner) as TextInputLayout
|
||||
mInputLayout = ViewUtils.`$$`(dialog, R.id.text_input_layout) as TextInputLayout
|
||||
mEditText = mInputLayout.editText!!
|
||||
val button: View? = dialog.getButton(DialogInterface.BUTTON_POSITIVE)
|
||||
button?.setOnClickListener(this)
|
||||
}
|
||||
|
||||
override fun onClick(v: View) {
|
||||
val text1 = mSpinner.editText!!.text.toString()
|
||||
if (TextUtils.isEmpty(text1)) {
|
||||
mSpinner.error = getString(R.string.text_is_empty)
|
||||
return
|
||||
} else {
|
||||
mSpinner.error = null
|
||||
}
|
||||
val text = mEditText.text.toString().trim { it <= ' ' }
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
mInputLayout.error = getString(R.string.text_is_empty)
|
||||
return
|
||||
} else {
|
||||
mInputLayout.error = null
|
||||
}
|
||||
val mode = mArray.indexOf(text1)
|
||||
val filter = Filter()
|
||||
filter.mode = mode
|
||||
filter.text = text
|
||||
if (!mFilterList.add(filter)) {
|
||||
mInputLayout.error = getString(R.string.label_text_exist)
|
||||
return
|
||||
} else {
|
||||
mInputLayout.error = null
|
||||
}
|
||||
mAdapter.notifyDataSetChanged()
|
||||
updateView(true)
|
||||
mDialog.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
private inner class FilterAdapter : RecyclerView.Adapter<FilterHolder>() {
|
||||
override fun getItemViewType(position: Int): Int {
|
||||
return if (mFilterList[position].mode == MODE_HEADER) {
|
||||
TYPE_HEADER
|
||||
} else {
|
||||
TYPE_ITEM
|
||||
}
|
||||
}
|
||||
|
||||
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): FilterHolder {
|
||||
val layoutId: Int = when (viewType) {
|
||||
TYPE_ITEM -> R.layout.item_filter
|
||||
TYPE_HEADER -> R.layout.item_filter_header
|
||||
else -> R.layout.item_filter
|
||||
}
|
||||
return FilterHolder(layoutInflater.inflate(layoutId, parent, false))
|
||||
}
|
||||
|
||||
override fun onBindViewHolder(holder: FilterHolder, position: Int) {
|
||||
val filter = mFilterList[position]
|
||||
if (MODE_HEADER == filter.mode) {
|
||||
holder.text?.text = filter.text
|
||||
} else {
|
||||
holder.checkbox?.text = filter.text
|
||||
holder.checkbox?.isChecked = filter.enable!!
|
||||
holder.itemView.setOnClickListener {
|
||||
mFilterList.trigger(filter)
|
||||
|
||||
// for updating delete line on filter text
|
||||
mAdapter.notifyItemChanged(position)
|
||||
}
|
||||
holder.delete?.setOnClickListener { showDeleteFilterDialog(filter) }
|
||||
}
|
||||
}
|
||||
|
||||
override fun getItemCount(): Int {
|
||||
return mFilterList.size()
|
||||
}
|
||||
|
||||
override fun getItemId(position: Int): Long {
|
||||
return run {
|
||||
val filter = mFilterList[position]
|
||||
if (filter.id != null) {
|
||||
(filter.text.hashCode() shr filter.mode) + filter.id!!
|
||||
} else {
|
||||
(filter.text.hashCode() shr filter.mode).toLong()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private inner class FilterList {
|
||||
private val mEhFilter: EhFilter = EhFilter
|
||||
private val mTitleFilterList: List<Filter> = mEhFilter.titleFilterList
|
||||
private val mUploaderFilterList: List<Filter> = mEhFilter.uploaderFilterList
|
||||
private val mTagFilterList: List<Filter> = mEhFilter.tagFilterList
|
||||
private val mTagNamespaceFilterList: List<Filter> = mEhFilter.tagNamespaceFilterList
|
||||
private val mCommenterFilterList: List<Filter> = mEhFilter.commenterFilterList
|
||||
private val mCommentFilterList: List<Filter> = mEhFilter.commentFilterList
|
||||
private var mTitleHeader: Filter? = null
|
||||
private var mUploaderHeader: Filter? = null
|
||||
private var mTagHeader: Filter? = null
|
||||
private var mTagNamespaceHeader: Filter? = null
|
||||
private var mCommenterHeader: Filter? = null
|
||||
private var mCommentHeader: Filter? = null
|
||||
fun size(): Int {
|
||||
var count = 0
|
||||
var size = mTitleFilterList.size
|
||||
count += if (0 == size) 0 else size + 1
|
||||
size = mUploaderFilterList.size
|
||||
count += if (0 == size) 0 else size + 1
|
||||
size = mTagFilterList.size
|
||||
count += if (0 == size) 0 else size + 1
|
||||
size = mTagNamespaceFilterList.size
|
||||
count += if (0 == size) 0 else size + 1
|
||||
size = mCommenterFilterList.size
|
||||
count += if (0 == size) 0 else size + 1
|
||||
size = mCommentFilterList.size
|
||||
count += if (0 == size) 0 else size + 1
|
||||
return count
|
||||
}
|
||||
|
||||
private val titleHeader: Filter
|
||||
get() {
|
||||
if (null == mTitleHeader) {
|
||||
mTitleHeader = Filter()
|
||||
mTitleHeader!!.mode = MODE_HEADER
|
||||
mTitleHeader!!.text = getString(R.string.filter_title)
|
||||
}
|
||||
return mTitleHeader!!
|
||||
}
|
||||
private val uploaderHeader: Filter
|
||||
get() {
|
||||
if (null == mUploaderHeader) {
|
||||
mUploaderHeader = Filter()
|
||||
mUploaderHeader!!.mode = MODE_HEADER
|
||||
mUploaderHeader!!.text = getString(R.string.filter_uploader)
|
||||
}
|
||||
return mUploaderHeader!!
|
||||
}
|
||||
private val tagHeader: Filter
|
||||
get() {
|
||||
if (null == mTagHeader) {
|
||||
mTagHeader = Filter()
|
||||
mTagHeader!!.mode = MODE_HEADER
|
||||
mTagHeader!!.text = getString(R.string.filter_tag)
|
||||
}
|
||||
return mTagHeader!!
|
||||
}
|
||||
private val tagNamespaceHeader: Filter
|
||||
get() {
|
||||
if (null == mTagNamespaceHeader) {
|
||||
mTagNamespaceHeader = Filter()
|
||||
mTagNamespaceHeader!!.mode = MODE_HEADER
|
||||
mTagNamespaceHeader!!.text = getString(R.string.filter_tag_namespace)
|
||||
}
|
||||
return mTagNamespaceHeader!!
|
||||
}
|
||||
private val commenterHeader: Filter
|
||||
get() {
|
||||
if (null == mCommenterHeader) {
|
||||
mCommenterHeader = Filter()
|
||||
mCommenterHeader!!.mode = MODE_HEADER
|
||||
mCommenterHeader!!.text = getString(R.string.filter_commenter)
|
||||
}
|
||||
return mCommenterHeader!!
|
||||
}
|
||||
private val commentHeader: Filter
|
||||
get() {
|
||||
if (null == mCommentHeader) {
|
||||
mCommentHeader = Filter()
|
||||
mCommentHeader!!.mode = MODE_HEADER
|
||||
mCommentHeader!!.text = getString(R.string.filter_comment)
|
||||
}
|
||||
return mCommentHeader!!
|
||||
}
|
||||
|
||||
operator fun get(index: Int): Filter {
|
||||
var index1 = index
|
||||
var size = mTitleFilterList.size
|
||||
if (0 != size) {
|
||||
index1 -= if (index1 == 0) {
|
||||
return titleHeader
|
||||
} else if (index1 <= size) {
|
||||
return mTitleFilterList[index1 - 1]
|
||||
} else {
|
||||
size + 1
|
||||
}
|
||||
}
|
||||
size = mUploaderFilterList.size
|
||||
if (0 != size) {
|
||||
index1 -= if (index1 == 0) {
|
||||
return uploaderHeader
|
||||
} else if (index1 <= size) {
|
||||
return mUploaderFilterList[index1 - 1]
|
||||
} else {
|
||||
size + 1
|
||||
}
|
||||
}
|
||||
size = mTagFilterList.size
|
||||
if (0 != size) {
|
||||
index1 -= if (index1 == 0) {
|
||||
return tagHeader
|
||||
} else if (index1 <= size) {
|
||||
return mTagFilterList[index1 - 1]
|
||||
} else {
|
||||
size + 1
|
||||
}
|
||||
}
|
||||
size = mTagNamespaceFilterList.size
|
||||
if (0 != size) {
|
||||
index1 -= if (index1 == 0) {
|
||||
return tagNamespaceHeader
|
||||
} else if (index1 <= size) {
|
||||
return mTagNamespaceFilterList[index1 - 1]
|
||||
} else {
|
||||
size + 1
|
||||
}
|
||||
}
|
||||
size = mCommenterFilterList.size
|
||||
if (0 != size) {
|
||||
index1 -= if (index1 == 0) {
|
||||
return commenterHeader
|
||||
} else if (index1 <= size) {
|
||||
return mCommenterFilterList[index1 - 1]
|
||||
} else {
|
||||
size + 1
|
||||
}
|
||||
}
|
||||
size = mCommentFilterList.size
|
||||
if (0 != size) {
|
||||
if (index1 == 0) {
|
||||
return commentHeader
|
||||
} else if (index1 <= size) {
|
||||
return mCommentFilterList[index1 - 1]
|
||||
}
|
||||
}
|
||||
throw IndexOutOfBoundsException()
|
||||
}
|
||||
|
||||
fun add(filter: Filter): Boolean {
|
||||
return mEhFilter.addFilter(filter)
|
||||
}
|
||||
|
||||
fun delete(filter: Filter) {
|
||||
mEhFilter.deleteFilter(filter)
|
||||
}
|
||||
|
||||
fun trigger(filter: Filter) {
|
||||
mEhFilter.triggerFilter(filter)
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val MODE_HEADER = -1
|
||||
private const val TYPE_ITEM = 0
|
||||
private const val TYPE_HEADER = 1
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.ComposeView
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.ui.compose.setMD3Content
|
||||
import com.mikepenz.aboutlibraries.ui.compose.LibrariesContainer
|
||||
|
||||
class LicenseFragment : BaseFragment() {
|
||||
override fun getFragmentTitle(): Int {
|
||||
return R.string.license
|
||||
}
|
||||
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
return ComposeView(inflater.context).apply {
|
||||
setMD3Content {
|
||||
LibrariesContainer(modifier = Modifier.fillMaxSize())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,107 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Tarsin Norbin
|
||||
*
|
||||
* This file is part of EhViewer
|
||||
*
|
||||
* EhViewer is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* EhViewer is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with EhViewer.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.databinding.ActivityWebviewBinding
|
||||
import com.hippo.ehviewer.ui.legacy.DialogWebChromeClient
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import rikka.core.res.resolveColor
|
||||
|
||||
class MyTagsFragment : BaseFragment() {
|
||||
private var _binding: ActivityWebviewBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val url = EhUrl.myTagsUrl
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
_binding = ActivityWebviewBinding.inflate(inflater, container, false)
|
||||
binding.webview.run {
|
||||
setBackgroundColor(requireActivity().theme.resolveColor(android.R.attr.colorBackground))
|
||||
settings.builtInZoomControls = true
|
||||
settings.displayZoomControls = false
|
||||
settings.javaScriptEnabled = true
|
||||
webViewClient = MyTagsWebViewClient()
|
||||
webChromeClient = DialogWebChromeClient(requireContext())
|
||||
}
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.progress.visibility = View.VISIBLE
|
||||
binding.webview.loadUrl(url)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// http://stackoverflow.com/questions/32284642/how-to-handle-an-uncatched-exception
|
||||
val cookieManager = CookieManager.getInstance()
|
||||
cookieManager.flush()
|
||||
cookieManager.removeAllCookies(null)
|
||||
cookieManager.removeSessionCookies(null)
|
||||
|
||||
// Copy cookies from okhttp cookie store to CookieManager
|
||||
val store = EhCookieStore
|
||||
for (cookie in store.getCookies(url.toHttpUrl())) {
|
||||
cookieManager.setCookie(url, cookie.toString())
|
||||
}
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
binding.webview.destroy()
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun getFragmentTitle(): Int {
|
||||
return R.string.my_tags
|
||||
}
|
||||
|
||||
private inner class MyTagsWebViewClient : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
||||
// Never load other urls
|
||||
return request.url.toString() != this@MyTagsFragment.url
|
||||
}
|
||||
|
||||
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
|
||||
binding.progress.visibility = View.VISIBLE
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
binding.progress.visibility = View.GONE
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Tarsin Norbin
|
||||
*
|
||||
* This file is part of EhViewer
|
||||
*
|
||||
* EhViewer is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* EhViewer is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with EhViewer.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.preference.SeekBarPreference
|
||||
import androidx.preference.SwitchPreferenceCompat
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.ui.isAuthenticationSupported
|
||||
|
||||
/**
|
||||
* Created by Mo10 on 2018/2/10.
|
||||
*/
|
||||
class PrivacyFragment : BasePreferenceFragment() {
|
||||
private lateinit var requireUnlock: SwitchPreferenceCompat
|
||||
private lateinit var unlockDelay: SeekBarPreference
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.privacy_settings)
|
||||
requireUnlock = findPreference("require_unlock")!!
|
||||
unlockDelay = findPreference("require_unlock_delay")!!
|
||||
requireUnlock.setOnPreferenceChangeListener { _, newValue ->
|
||||
unlockDelay.isEnabled = newValue as Boolean
|
||||
setUnlockDelaySummary(unlockDelay.value)
|
||||
true
|
||||
}
|
||||
unlockDelay.setOnPreferenceChangeListener { _, newValue ->
|
||||
setUnlockDelaySummary(newValue as Int)
|
||||
true
|
||||
}
|
||||
}
|
||||
|
||||
override fun onStart() {
|
||||
super.onStart()
|
||||
if (!requireContext().isAuthenticationSupported()) {
|
||||
Settings.security = false
|
||||
requireUnlock.isEnabled = false
|
||||
requireUnlock.isChecked = false
|
||||
unlockDelay.isEnabled = false
|
||||
} else {
|
||||
unlockDelay.isEnabled = requireUnlock.isChecked == true
|
||||
setUnlockDelaySummary(unlockDelay.value)
|
||||
}
|
||||
}
|
||||
|
||||
@get:StringRes
|
||||
override val fragmentTitle: Int
|
||||
get() = R.string.settings_privacy
|
||||
|
||||
private fun setUnlockDelaySummary(value: Int) {
|
||||
unlockDelay.summary = if (value == 0) {
|
||||
getString(R.string.settings_privacy_require_unlock_delay_summary_immediately)
|
||||
} else {
|
||||
getString(R.string.settings_privacy_require_unlock_delay_summary, value.toString())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Tarsin Norbin
|
||||
*
|
||||
* This file is part of EhViewer
|
||||
*
|
||||
* EhViewer is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* EhViewer is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with EhViewer.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.os.Bundle
|
||||
import com.hippo.ehviewer.R
|
||||
|
||||
class SettingsFragment : BasePreferenceFragment() {
|
||||
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||
addPreferencesFromResource(R.xml.settings_headers)
|
||||
}
|
||||
|
||||
override val fragmentTitle: Int
|
||||
get() = R.string.settings
|
||||
}
|
||||
@@ -1,177 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Tarsin Norbin
|
||||
*
|
||||
* This file is part of EhViewer
|
||||
*
|
||||
* EhViewer is free software: you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation, either version 3 of the
|
||||
* License, or (at your option) any later version.
|
||||
*
|
||||
* EhViewer is distributed in the hope that it will be useful, but
|
||||
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
|
||||
* or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with EhViewer.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.fragment
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.graphics.Bitmap
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.Menu
|
||||
import android.view.MenuInflater
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.webkit.CookieManager
|
||||
import android.webkit.WebResourceRequest
|
||||
import android.webkit.WebView
|
||||
import android.webkit.WebViewClient
|
||||
import androidx.core.view.MenuProvider
|
||||
import androidx.lifecycle.Lifecycle
|
||||
import androidx.lifecycle.lifecycleScope
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhCookieStore.KEY_SETTINGS_PROFILE
|
||||
import com.hippo.ehviewer.client.EhUrl
|
||||
import com.hippo.ehviewer.databinding.ActivityWebviewBinding
|
||||
import com.hippo.ehviewer.ui.legacy.DialogWebChromeClient
|
||||
import com.hippo.ehviewer.ui.scene.BaseScene
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import okhttp3.Cookie
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import rikka.core.res.resolveColor
|
||||
|
||||
class UConfigFragment : BaseFragment() {
|
||||
private var _binding: ActivityWebviewBinding? = null
|
||||
private val binding get() = _binding!!
|
||||
private val url = EhUrl.uConfigUrl
|
||||
private var loaded = false
|
||||
|
||||
@SuppressLint("SetJavaScriptEnabled")
|
||||
override fun onCreateView(
|
||||
inflater: LayoutInflater,
|
||||
container: ViewGroup?,
|
||||
savedInstanceState: Bundle?,
|
||||
): View {
|
||||
_binding = ActivityWebviewBinding.inflate(inflater, container, false)
|
||||
binding.webview.run {
|
||||
setBackgroundColor(requireActivity().theme.resolveColor(android.R.attr.colorBackground))
|
||||
settings.builtInZoomControls = true
|
||||
settings.displayZoomControls = false
|
||||
settings.javaScriptEnabled = true
|
||||
webViewClient = UConfigWebViewClient()
|
||||
webChromeClient = DialogWebChromeClient(requireContext())
|
||||
}
|
||||
return binding.root
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View, savedInstanceState: Bundle?) {
|
||||
super.onViewCreated(view, savedInstanceState)
|
||||
binding.progress.visibility = View.VISIBLE
|
||||
binding.webview.loadUrl(url)
|
||||
showTip(R.string.apply_tip, BaseScene.LENGTH_LONG)
|
||||
requireActivity().addMenuProvider(
|
||||
object : MenuProvider {
|
||||
override fun onCreateMenu(menu: Menu, menuInflater: MenuInflater) {
|
||||
menuInflater.inflate(R.menu.activity_u_config, menu)
|
||||
}
|
||||
|
||||
override fun onMenuItemSelected(menuItem: MenuItem): Boolean {
|
||||
if (menuItem.itemId == R.id.action_apply) {
|
||||
if (loaded) {
|
||||
apply()
|
||||
}
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
},
|
||||
viewLifecycleOwner,
|
||||
Lifecycle.State.RESUMED,
|
||||
)
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
super.onCreate(savedInstanceState)
|
||||
|
||||
// http://stackoverflow.com/questions/32284642/how-to-handle-an-uncatched-exception
|
||||
val cookieManager = CookieManager.getInstance()
|
||||
cookieManager.flush()
|
||||
cookieManager.removeAllCookies(null)
|
||||
cookieManager.removeSessionCookies(null)
|
||||
|
||||
// Copy cookies from okhttp cookie store to CookieManager
|
||||
val store = EhCookieStore
|
||||
for (cookie in store.getCookies(url.toHttpUrl())) {
|
||||
cookieManager.setCookie(url, cookie.toString())
|
||||
}
|
||||
}
|
||||
|
||||
private fun apply() {
|
||||
binding.webview.loadUrl(
|
||||
"""javascript:(function() {
|
||||
var apply = document.getElementById("apply").children[0];
|
||||
apply.click();
|
||||
})();""",
|
||||
)
|
||||
}
|
||||
|
||||
private fun longLive(cookie: Cookie): Cookie {
|
||||
return Cookie.Builder()
|
||||
.name(cookie.name)
|
||||
.value(cookie.value)
|
||||
.domain(cookie.domain)
|
||||
.path(cookie.path)
|
||||
.expiresAt(Long.MAX_VALUE)
|
||||
.build()
|
||||
}
|
||||
|
||||
override fun onDestroyView() {
|
||||
super.onDestroyView()
|
||||
binding.webview.destroy()
|
||||
|
||||
// Put cookies back to okhttp cookie store
|
||||
val cookieManager = CookieManager.getInstance()
|
||||
val cookiesString = cookieManager.getCookie(url)
|
||||
if (!cookiesString.isNullOrEmpty()) {
|
||||
val store = EhCookieStore
|
||||
val hostUrl = EhUrl.host.toHttpUrl()
|
||||
|
||||
lifecycleScope.launchIO {
|
||||
store.deleteCookie(hostUrl, KEY_SETTINGS_PROFILE)
|
||||
// The cookies saved in the uconfig page should not be shared between e and ex
|
||||
for (header in cookiesString.split(";".toRegex()).dropLastWhile { it.isEmpty() }) {
|
||||
Cookie.parse(hostUrl, header)?.let {
|
||||
store.addCookie(longLive(it))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
_binding = null
|
||||
}
|
||||
|
||||
override fun getFragmentTitle(): Int {
|
||||
return R.string.u_config
|
||||
}
|
||||
|
||||
private inner class UConfigWebViewClient : WebViewClient() {
|
||||
override fun shouldOverrideUrlLoading(view: WebView, request: WebResourceRequest): Boolean {
|
||||
// Never load other urls
|
||||
return true
|
||||
}
|
||||
|
||||
override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) {
|
||||
binding.progress.visibility = View.VISIBLE
|
||||
loaded = false
|
||||
}
|
||||
|
||||
override fun onPageFinished(view: WebView, url: String) {
|
||||
binding.progress.visibility = View.GONE
|
||||
loaded = true
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -33,7 +33,6 @@ import android.widget.TextView
|
||||
import androidx.annotation.IntDef
|
||||
import androidx.core.content.edit
|
||||
import androidx.core.view.forEach
|
||||
import androidx.preference.PreferenceManager
|
||||
import androidx.recyclerview.widget.DefaultItemAnimator
|
||||
import androidx.recyclerview.widget.LinearLayoutManager
|
||||
import com.google.android.material.chip.Chip
|
||||
@@ -41,6 +40,7 @@ import com.google.android.material.chip.ChipGroup
|
||||
import com.google.android.material.tabs.TabLayout
|
||||
import com.google.android.material.tabs.TabLayout.OnTabSelectedListener
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhUtils
|
||||
import com.hippo.ehviewer.client.data.ListUrlBuilder
|
||||
import com.hippo.ehviewer.client.exception.EhException
|
||||
@@ -74,11 +74,10 @@ class SearchLayout @JvmOverloads constructor(
|
||||
private var mAction: TabLayout
|
||||
private var mAdapter: SearchAdapter
|
||||
private var mHelper: Helper? = null
|
||||
private val mSharePref: SharedPreferences
|
||||
private val mSharePref: SharedPreferences = Settings.prefs
|
||||
private val mInflater: LayoutInflater
|
||||
|
||||
init {
|
||||
mSharePref = PreferenceManager.getDefaultSharedPreferences(context)
|
||||
mInflater = LayoutInflater.from(context)
|
||||
val resources = context.resources
|
||||
layoutManager = SearchLayoutManager(context)
|
||||
@@ -213,7 +212,7 @@ class SearchLayout @JvmOverloads constructor(
|
||||
}
|
||||
}
|
||||
urlBuilder.keyword = query
|
||||
urlBuilder.category = PreferenceManager.getDefaultSharedPreferences(context).getInt(
|
||||
urlBuilder.category = Settings.prefs.getInt(
|
||||
SEARCH_CATEGORY_PREF,
|
||||
0,
|
||||
)
|
||||
|
||||
@@ -1,424 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.ui.legacy.preference;
|
||||
|
||||
import android.app.Dialog;
|
||||
import android.content.Context;
|
||||
import android.content.DialogInterface;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.res.TypedArray;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.os.Bundle;
|
||||
import android.os.Parcel;
|
||||
import android.os.Parcelable;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.Window;
|
||||
import android.view.WindowManager;
|
||||
|
||||
import androidx.annotation.DrawableRes;
|
||||
import androidx.annotation.StringRes;
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.preference.Preference;
|
||||
|
||||
import com.hippo.ehviewer.ui.legacy.BaseDialogBuilder;
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
/**
|
||||
* A base class for {@link Preference} objects that are
|
||||
* dialog-based. These preferences will, when clicked, open a dialog showing the
|
||||
* actual preference controls.
|
||||
*/
|
||||
public abstract class DialogPreference extends Preference implements
|
||||
DialogInterface.OnClickListener, DialogInterface.OnDismissListener {
|
||||
|
||||
private AlertDialog.Builder mBuilder;
|
||||
|
||||
private CharSequence mDialogTitle;
|
||||
private Drawable mDialogIcon;
|
||||
private CharSequence mPositiveButtonText;
|
||||
private CharSequence mNegativeButtonText;
|
||||
private int mDialogLayoutResId;
|
||||
|
||||
/**
|
||||
* The dialog, if it is showing.
|
||||
*/
|
||||
private AlertDialog mDialog;
|
||||
|
||||
/**
|
||||
* Which button was clicked.
|
||||
*/
|
||||
private int mWhichButtonClicked;
|
||||
|
||||
public DialogPreference(Context context) {
|
||||
super(context);
|
||||
init(context, null, 0, 0);
|
||||
}
|
||||
|
||||
public DialogPreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs, 0, 0);
|
||||
}
|
||||
|
||||
public DialogPreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs, defStyleAttr, defStyleAttr);
|
||||
}
|
||||
|
||||
private void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.DialogPreference, defStyleAttr, defStyleRes);
|
||||
mDialogTitle = a.getString(R.styleable.DialogPreference_dialogTitle);
|
||||
if (mDialogTitle == null) {
|
||||
// Fallback on the regular title of the preference
|
||||
// (the one that is seen in the list)
|
||||
mDialogTitle = getTitle();
|
||||
}
|
||||
mDialogIcon = a.getDrawable(R.styleable.DialogPreference_dialogIcon);
|
||||
mPositiveButtonText = a.getString(R.styleable.DialogPreference_positiveButtonText);
|
||||
mNegativeButtonText = a.getString(R.styleable.DialogPreference_negativeButtonText);
|
||||
mDialogLayoutResId = a.getResourceId(R.styleable.DialogPreference_dialogLayout, mDialogLayoutResId);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the title to be shown on subsequent dialogs.
|
||||
*
|
||||
* @return The title.
|
||||
*/
|
||||
public CharSequence getDialogTitle() {
|
||||
return mDialogTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the title of the dialog. This will be shown on subsequent dialogs.
|
||||
*
|
||||
* @param dialogTitle The title.
|
||||
*/
|
||||
public void setDialogTitle(CharSequence dialogTitle) {
|
||||
mDialogTitle = dialogTitle;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param dialogTitleResId The dialog title as a resource.
|
||||
* @see #setDialogTitle(CharSequence)
|
||||
*/
|
||||
public void setDialogTitle(int dialogTitleResId) {
|
||||
setDialogTitle(getContext().getString(dialogTitleResId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the icon to be shown on subsequent dialogs.
|
||||
*
|
||||
* @return The icon, as a {@link Drawable}.
|
||||
*/
|
||||
public Drawable getDialogIcon() {
|
||||
return mDialogIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the icon of the dialog. This will be shown on subsequent dialogs.
|
||||
*
|
||||
* @param dialogIcon The icon, as a {@link Drawable}.
|
||||
*/
|
||||
public void setDialogIcon(Drawable dialogIcon) {
|
||||
mDialogIcon = dialogIcon;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the icon (resource ID) of the dialog. This will be shown on
|
||||
* subsequent dialogs.
|
||||
*
|
||||
* @param dialogIconRes The icon, as a resource ID.
|
||||
*/
|
||||
public void setDialogIcon(@DrawableRes int dialogIconRes) {
|
||||
mDialogIcon = ContextCompat.getDrawable(getContext(), dialogIconRes);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text of the positive button to be shown on subsequent
|
||||
* dialogs.
|
||||
*
|
||||
* @return The text of the positive button.
|
||||
*/
|
||||
public CharSequence getPositiveButtonText() {
|
||||
return mPositiveButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text of the positive button of the dialog. This will be shown on
|
||||
* subsequent dialogs.
|
||||
*
|
||||
* @param positiveButtonText The text of the positive button.
|
||||
*/
|
||||
public void setPositiveButtonText(CharSequence positiveButtonText) {
|
||||
mPositiveButtonText = positiveButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param positiveButtonTextResId The positive button text as a resource.
|
||||
* @see #setPositiveButtonText(CharSequence)
|
||||
*/
|
||||
public void setPositiveButtonText(@StringRes int positiveButtonTextResId) {
|
||||
setPositiveButtonText(getContext().getString(positiveButtonTextResId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the text of the negative button to be shown on subsequent
|
||||
* dialogs.
|
||||
*
|
||||
* @return The text of the negative button.
|
||||
*/
|
||||
public CharSequence getNegativeButtonText() {
|
||||
return mNegativeButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the text of the negative button of the dialog. This will be shown on
|
||||
* subsequent dialogs.
|
||||
*
|
||||
* @param negativeButtonText The text of the negative button.
|
||||
*/
|
||||
public void setNegativeButtonText(CharSequence negativeButtonText) {
|
||||
mNegativeButtonText = negativeButtonText;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param negativeButtonTextResId The negative button text as a resource.
|
||||
* @see #setNegativeButtonText(CharSequence)
|
||||
*/
|
||||
public void setNegativeButtonText(@StringRes int negativeButtonTextResId) {
|
||||
setNegativeButtonText(getContext().getString(negativeButtonTextResId));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the layout resource that is used as the content View for
|
||||
* subsequent dialogs.
|
||||
*
|
||||
* @return The layout resource.
|
||||
*/
|
||||
public int getDialogLayoutResource() {
|
||||
return mDialogLayoutResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the layout resource that is inflated as the {@link View} to be shown
|
||||
* as the content View of subsequent dialogs.
|
||||
*
|
||||
* @param dialogLayoutResId The layout resource ID to be inflated.
|
||||
*/
|
||||
public void setDialogLayoutResource(int dialogLayoutResId) {
|
||||
mDialogLayoutResId = dialogLayoutResId;
|
||||
}
|
||||
|
||||
/**
|
||||
* Prepares the dialog builder to be shown when the preference is clicked.
|
||||
* Use this to set custom properties on the dialog.
|
||||
* <p>
|
||||
* Do not {@link AlertDialog.Builder#create()} or
|
||||
* {@link AlertDialog.Builder#show()}.
|
||||
*/
|
||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onClick() {
|
||||
if (mDialog != null && mDialog.isShowing()) return;
|
||||
|
||||
showDialog(null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Shows the dialog associated with this Preference. This is normally initiated
|
||||
* automatically on clicking on the preference. Call this method if you need to
|
||||
* show the dialog on some other event.
|
||||
*
|
||||
* @param state Optional instance state to restore on the dialog
|
||||
*/
|
||||
protected void showDialog(Bundle state) {
|
||||
Context context = getContext();
|
||||
|
||||
mWhichButtonClicked = DialogInterface.BUTTON_NEGATIVE;
|
||||
|
||||
mBuilder = new BaseDialogBuilder(context)
|
||||
.setTitle(mDialogTitle)
|
||||
.setIcon(mDialogIcon)
|
||||
.setPositiveButton(mPositiveButtonText, this)
|
||||
.setNegativeButton(mNegativeButtonText, this);
|
||||
|
||||
View contentView = onCreateDialogView();
|
||||
if (contentView != null) {
|
||||
onBindDialogView(contentView);
|
||||
mBuilder.setView(contentView);
|
||||
}
|
||||
|
||||
onPrepareDialogBuilder(mBuilder);
|
||||
|
||||
//PreferenceUtils.registerOnActivityDestroyListener(this, this);
|
||||
|
||||
// Create the dialog
|
||||
final AlertDialog dialog = mDialog = mBuilder.create();
|
||||
if (state != null) {
|
||||
dialog.onRestoreInstanceState(state);
|
||||
}
|
||||
if (needInputMethod()) {
|
||||
requestInputMethod(dialog);
|
||||
}
|
||||
dialog.setOnDismissListener(this);
|
||||
dialog.show();
|
||||
|
||||
onDialogCreated(dialog);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the preference needs to display a soft input method when the dialog
|
||||
* is displayed. Default is false. Subclasses should override this method if they need
|
||||
* the soft input method brought up automatically.
|
||||
*/
|
||||
protected boolean needInputMethod() {
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the required flags on the dialog window to enable input method window to show up.
|
||||
*/
|
||||
private void requestInputMethod(Dialog dialog) {
|
||||
Window window = dialog.getWindow();
|
||||
window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_VISIBLE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates the content view for the dialog (if a custom content view is
|
||||
* required). By default, it inflates the dialog layout resource if it is
|
||||
* set.
|
||||
*
|
||||
* @return The content View for the dialog.
|
||||
* @see #setLayoutResource(int)
|
||||
*/
|
||||
protected View onCreateDialogView() {
|
||||
if (mDialogLayoutResId == 0) {
|
||||
return null;
|
||||
}
|
||||
|
||||
LayoutInflater inflater = LayoutInflater.from(mBuilder.getContext());
|
||||
return inflater.inflate(mDialogLayoutResId, null);
|
||||
}
|
||||
|
||||
/**
|
||||
* Binds views in the content View of the dialog to data.
|
||||
*
|
||||
* @param view The content View of the dialog, if it is custom.
|
||||
*/
|
||||
protected void onBindDialogView(View view) {
|
||||
}
|
||||
|
||||
protected void onDialogCreated(AlertDialog dialog) {
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onClick(DialogInterface dialog, int which) {
|
||||
mWhichButtonClicked = which;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onDismiss(DialogInterface dialog) {
|
||||
mDialog = null;
|
||||
onDialogClosed(mWhichButtonClicked == DialogInterface.BUTTON_POSITIVE);
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when the dialog is dismissed and should be used to save data to
|
||||
* the {@link SharedPreferences}.
|
||||
*
|
||||
* @param positiveResult Whether the positive button was clicked (true), or
|
||||
* the negative button was clicked or the dialog was canceled (false).
|
||||
*/
|
||||
protected void onDialogClosed(boolean positiveResult) {
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the dialog that is shown by this preference.
|
||||
*
|
||||
* @return The dialog, or null if a dialog is not being shown.
|
||||
*/
|
||||
public Dialog getDialog() {
|
||||
return mDialog;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected Parcelable onSaveInstanceState() {
|
||||
final Parcelable superState = super.onSaveInstanceState();
|
||||
if (mDialog == null || !mDialog.isShowing()) {
|
||||
return superState;
|
||||
}
|
||||
|
||||
final SavedState myState = new SavedState(superState);
|
||||
myState.isDialogShowing = true;
|
||||
myState.dialogBundle = mDialog.onSaveInstanceState();
|
||||
return myState;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onRestoreInstanceState(Parcelable state) {
|
||||
if (state == null || !state.getClass().equals(SavedState.class)) {
|
||||
// Didn't save state for us in onSaveInstanceState
|
||||
super.onRestoreInstanceState(state);
|
||||
return;
|
||||
}
|
||||
|
||||
SavedState myState = (SavedState) state;
|
||||
super.onRestoreInstanceState(myState.getSuperState());
|
||||
if (myState.isDialogShowing) {
|
||||
showDialog(myState.dialogBundle);
|
||||
}
|
||||
}
|
||||
|
||||
private static class SavedState extends BaseSavedState {
|
||||
public static final Parcelable.Creator<SavedState> CREATOR =
|
||||
new Parcelable.Creator<SavedState>() {
|
||||
@Override
|
||||
public SavedState createFromParcel(Parcel in) {
|
||||
return new SavedState(in);
|
||||
}
|
||||
|
||||
@Override
|
||||
public SavedState[] newArray(int size) {
|
||||
return new SavedState[size];
|
||||
}
|
||||
};
|
||||
boolean isDialogShowing;
|
||||
Bundle dialogBundle;
|
||||
|
||||
public SavedState(Parcel source) {
|
||||
super(source);
|
||||
isDialogShowing = source.readInt() == 1;
|
||||
dialogBundle = source.readBundle(DialogPreference.class.getClassLoader());
|
||||
}
|
||||
|
||||
public SavedState(Parcelable superState) {
|
||||
super(superState);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void writeToParcel(Parcel dest, int flags) {
|
||||
super.writeToParcel(dest, flags);
|
||||
dest.writeInt(isDialogShowing ? 1 : 0);
|
||||
dest.writeBundle(dialogBundle);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,86 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package com.hippo.ehviewer.ui.legacy.preference;
|
||||
|
||||
import android.content.Context;
|
||||
import android.content.res.TypedArray;
|
||||
import android.text.Html;
|
||||
import android.text.method.LinkMovementMethod;
|
||||
import android.util.AttributeSet;
|
||||
import android.view.View;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.appcompat.app.AlertDialog;
|
||||
|
||||
import com.hippo.ehviewer.R;
|
||||
|
||||
public class MessagePreference extends DialogPreference {
|
||||
|
||||
private CharSequence mDialogMessage;
|
||||
private boolean mDialogMessageLinkify;
|
||||
|
||||
public MessagePreference(Context context) {
|
||||
super(context);
|
||||
init(context, null, 0, 0);
|
||||
}
|
||||
|
||||
public MessagePreference(Context context, AttributeSet attrs) {
|
||||
super(context, attrs);
|
||||
init(context, attrs, 0, 0);
|
||||
}
|
||||
|
||||
public MessagePreference(Context context, AttributeSet attrs, int defStyleAttr) {
|
||||
super(context, attrs, defStyleAttr);
|
||||
init(context, attrs, defStyleAttr, 0);
|
||||
}
|
||||
|
||||
public void init(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
|
||||
TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.MessagePreference, defStyleAttr, defStyleRes);
|
||||
String message = a.getString(R.styleable.MessagePreference_dialogMessage);
|
||||
if (a.getBoolean(R.styleable.MessagePreference_dialogMessageHtml, false)) {
|
||||
mDialogMessage = Html.fromHtml(message, Html.FROM_HTML_MODE_LEGACY);
|
||||
} else {
|
||||
mDialogMessage = message;
|
||||
}
|
||||
mDialogMessageLinkify = a.getBoolean(R.styleable.MessagePreference_dialogMessageLinkify, false);
|
||||
a.recycle();
|
||||
}
|
||||
|
||||
public void setDialogMessage(CharSequence message) {
|
||||
mDialogMessage = message;
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onPrepareDialogBuilder(AlertDialog.Builder builder) {
|
||||
super.onPrepareDialogBuilder(builder);
|
||||
builder.setMessage(mDialogMessage);
|
||||
builder.setPositiveButton(android.R.string.ok, this);
|
||||
builder.setNegativeButton(null, null);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onDialogCreated(AlertDialog dialog) {
|
||||
super.onDialogCreated(dialog);
|
||||
|
||||
if (mDialogMessageLinkify) {
|
||||
final View messageView = dialog.findViewById(android.R.id.message);
|
||||
if (messageView instanceof TextView) {
|
||||
((TextView) messageView).setMovementMethod(LinkMovementMethod.getInstance());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
/*
|
||||
* Copyright 2016 Hippo Seven
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
package com.hippo.ehviewer.ui.legacy.preference
|
||||
|
||||
import android.content.Context
|
||||
import android.util.AttributeSet
|
||||
import androidx.preference.Preference
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.ui.openBrowser
|
||||
|
||||
class UrlPreference @JvmOverloads constructor(context: Context, attrs: AttributeSet? = null) :
|
||||
Preference(context, attrs) {
|
||||
private val mUrl: String?
|
||||
|
||||
init {
|
||||
val a = context.obtainStyledAttributes(attrs, R.styleable.UrlPreference, 0, 0)
|
||||
mUrl = a.getString(R.styleable.UrlPreference_url)
|
||||
a.recycle()
|
||||
}
|
||||
|
||||
override fun getSummary(): CharSequence? {
|
||||
return mUrl ?: super.getSummary()
|
||||
}
|
||||
|
||||
override fun onClick() {
|
||||
if (mUrl != null) {
|
||||
context.openBrowser(mUrl)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,7 +27,6 @@ import android.text.TextUtils
|
||||
import android.view.textclassifier.TextClassifier
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.ui.MainActivity
|
||||
import com.hippo.ehviewer.ui.SettingsActivity
|
||||
import com.hippo.ehviewer.ui.scene.BaseScene
|
||||
import splitties.systemservices.clipboardManager
|
||||
|
||||
@@ -52,8 +51,6 @@ fun Context.addTextToClipboard(text: CharSequence?, isSensitive: Boolean, useToa
|
||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.TIRAMISU) {
|
||||
if (this is MainActivity) {
|
||||
showTip(R.string.copied_to_clipboard, BaseScene.LENGTH_SHORT, useToast)
|
||||
} else if (this is SettingsActivity) {
|
||||
showTip(R.string.copied_to_clipboard, BaseScene.LENGTH_SHORT)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -70,7 +70,7 @@
|
||||
android:label="DownloadsScene" />
|
||||
<activity
|
||||
android:id="@+id/nav_settings"
|
||||
android:name="com.hippo.ehviewer.ui.SettingsActivity"
|
||||
android:name="com.hippo.ehviewer.ui.settings.SettingsActivity"
|
||||
android:label="SettingsActivity" />
|
||||
<fragment
|
||||
android:id="@+id/progressScene"
|
||||
|
||||
@@ -18,7 +18,6 @@
|
||||
|
||||
<style name="Base.AppTheme" parent="Theme.Material3.DynamicColors.DayNight">
|
||||
<item name="dividerColor">@color/divider</item>
|
||||
<item name="preferenceTheme">@style/PreferenceThemeOverlay.Rikka.Material3</item>
|
||||
<item name="snackbarStyle">@style/Snackbar</item>
|
||||
<item name="snackbarButtonStyle">@style/SnackbarButton</item>
|
||||
|
||||
|
||||
@@ -1,59 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2016 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Preference
|
||||
android:key="declaration"
|
||||
android:summary="@string/settings_about_declaration_summary"
|
||||
android:title="@string/settings_about_declaration"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="author"
|
||||
android:title="@string/settings_about_author"
|
||||
app:enableCopying="true"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<com.hippo.ehviewer.ui.legacy.preference.UrlPreference
|
||||
android:key="latest_release"
|
||||
android:title="@string/settings_about_latest_release"
|
||||
app:enableCopying="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:url="https://github.com/Ehviewer-Overhauled/Ehviewer/releases" />
|
||||
|
||||
<com.hippo.ehviewer.ui.legacy.preference.UrlPreference
|
||||
android:key="source"
|
||||
android:title="@string/settings_about_source"
|
||||
app:enableCopying="true"
|
||||
app:iconSpaceReserved="false"
|
||||
app:url="https://github.com/Ehviewer-Overhauled/Ehviewer" />
|
||||
|
||||
<Preference
|
||||
android:key="license"
|
||||
android:title="@string/license"
|
||||
android:iconSpaceReserved="false"/>
|
||||
|
||||
<com.hippo.ehviewer.preference.VersionPreference
|
||||
android:key="version"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="check_for_updates"
|
||||
android:title="@string/settings_about_check_for_updates"
|
||||
app:iconSpaceReserved="false" />
|
||||
</PreferenceScreen>
|
||||
@@ -1,116 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2016 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="save_parse_error_body"
|
||||
android:summary="@string/settings_advanced_save_parse_error_body_summary"
|
||||
android:title="@string/settings_advanced_save_parse_error_body"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="save_crash_log"
|
||||
android:summary="@string/settings_advanced_save_crash_log_summary"
|
||||
android:title="@string/settings_advanced_save_crash_log"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="dump_logcat"
|
||||
android:summary="@string/settings_advanced_dump_logcat_summary"
|
||||
android:title="@string/settings_advanced_dump_logcat"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="320"
|
||||
android:key="read_cache_size"
|
||||
android:title="@string/settings_advanced_read_cache_size"
|
||||
app:entries="@array/read_cache_size_entries"
|
||||
app:entryValues="@array/read_cache_size_entry_values"
|
||||
app:iconSpaceReserved="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="system"
|
||||
android:key="app_language"
|
||||
android:title="@string/settings_advanced_app_language_title"
|
||||
app:entries="@array/app_language_entries"
|
||||
app:entryValues="@array/app_language_entry_values"
|
||||
app:iconSpaceReserved="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<com.hippo.ehviewer.preference.ProxyPreference
|
||||
android:key="proxy"
|
||||
android:title="@string/settings_advanced_proxy"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="built_in_hosts_2"
|
||||
android:title="@string/settings_advanced_built_in_hosts_title"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<com.hippo.ehviewer.preference.DohPreference
|
||||
android:title="@string/settings_advanced_dns_over_http_title"
|
||||
android:iconSpaceReserved="false"/>
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="domain_fronting"
|
||||
android:summary="@string/settings_advanced_domain_fronting_summary"
|
||||
android:title="@string/settings_advanced_domain_fronting_title"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="bypass_vpn"
|
||||
android:summary="@string/settings_advanced_bypass_vpn_summary"
|
||||
android:title="@string/settings_advanced_bypass_vpn_title"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="preload_thumb_aggressively"
|
||||
android:title="@string/preload_thumb_aggressively"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="export_data"
|
||||
android:summary="@string/settings_advanced_export_data_summary"
|
||||
android:title="@string/settings_advanced_export_data"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="import_data"
|
||||
android:summary="@string/settings_advanced_import_data_summary"
|
||||
android:title="@string/settings_advanced_import_data"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="backup_favorite"
|
||||
android:summary="@string/settings_advanced_backup_favorite_summary"
|
||||
android:title="@string/settings_advanced_backup_favorite"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="open_by_default"
|
||||
android:title="@string/open_by_default"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -1,79 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2016 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<Preference
|
||||
android:key="download_location"
|
||||
android:title="@string/settings_download_download_location"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="media_scan"
|
||||
android:title="@string/settings_download_media_scan"
|
||||
app:iconSpaceReserved="false"
|
||||
app:summaryOff="@string/settings_download_media_scan_summary_off"
|
||||
app:summaryOn="@string/settings_download_media_scan_summary_on" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="3"
|
||||
android:key="download_thread"
|
||||
android:summary="@string/settings_download_concurrency_summary"
|
||||
android:title="@string/settings_download_concurrency"
|
||||
app:entries="@array/multi_thread_download_entries"
|
||||
app:entryValues="@array/multi_thread_download_entry_values"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="0"
|
||||
android:key="download_delay"
|
||||
android:summary="@string/settings_download_download_delay_summary"
|
||||
android:title="@string/settings_download_download_delay"
|
||||
app:entries="@array/download_delay_entries"
|
||||
app:entryValues="@array/download_delay_entry_values"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="5"
|
||||
android:key="preload_image"
|
||||
android:summary="@string/settings_download_preload_image_summary"
|
||||
android:title="@string/settings_download_preload_image"
|
||||
app:entries="@array/preload_image_entries"
|
||||
app:entryValues="@array/preload_image_entry_values"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="download_origin_image"
|
||||
android:summary="@string/settings_download_download_origin_image_summary"
|
||||
android:title="@string/settings_download_download_origin_image"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<com.hippo.ehviewer.preference.RestoreDownloadPreference
|
||||
android:key="restore_download_items"
|
||||
android:summary="@string/settings_download_restore_download_items_summary"
|
||||
android:title="@string/settings_download_restore_download_items"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<com.hippo.ehviewer.preference.CleanRedundancyPreference
|
||||
android:key="clean_redundancy"
|
||||
android:summary="@string/settings_download_clean_redundancy_summary"
|
||||
android:title="@string/settings_download_clean_redundancy"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -1,187 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2016 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<com.hippo.ehviewer.preference.AccountPreference
|
||||
android:key="account"
|
||||
android:summary="@string/settings_eh_identity_cookies_tourist"
|
||||
android:title="@string/account_name"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<com.hippo.ehviewer.preference.ImageLimitsPreference
|
||||
android:key="image_limits"
|
||||
android:title="@string/image_limits"
|
||||
app:iconSpaceReserved="false"
|
||||
app:negativeButtonText="@android:string/cancel"
|
||||
app:positiveButtonText="@string/reset" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="1"
|
||||
android:key="gallery_site"
|
||||
android:title="@string/settings_eh_gallery_site"
|
||||
app:entries="@array/gallery_site_entries"
|
||||
app:entryValues="@array/gallery_site_entry_values"
|
||||
app:iconSpaceReserved="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<Preference
|
||||
android:key="uconfig"
|
||||
android:summary="@string/settings_u_config_summary"
|
||||
android:title="@string/settings_u_config"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="mytags"
|
||||
android:summary="@string/settings_my_tags_summary"
|
||||
android:title="@string/settings_my_tags"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="-1"
|
||||
android:entries="@array/night_mode_entries"
|
||||
android:entryValues="@array/night_mode_values"
|
||||
android:key="theme"
|
||||
android:title="@string/dark_theme"
|
||||
app:iconSpaceReserved="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:key="black_dark_theme"
|
||||
android:title="@string/black_dark_theme"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="0"
|
||||
android:key="launch_page"
|
||||
android:title="@string/settings_eh_launch_page"
|
||||
app:entries="@array/launch_page_entries"
|
||||
app:entryValues="@array/launch_page_entry_values"
|
||||
app:iconSpaceReserved="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="0"
|
||||
android:key="list_mode"
|
||||
android:title="@string/settings_eh_list_mode"
|
||||
app:entries="@array/list_mode_entries"
|
||||
app:entryValues="@array/list_mode_entry_values"
|
||||
app:iconSpaceReserved="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="40"
|
||||
android:key="list_tile_size"
|
||||
android:max="60"
|
||||
android:title="@string/list_tile_thumb_size"
|
||||
app:iconSpaceReserved="false"
|
||||
app:min="20"
|
||||
app:showSeekBarValue="true" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="0"
|
||||
android:key="detail_size"
|
||||
android:title="@string/settings_eh_detail_size"
|
||||
app:entries="@array/detail_size_entries"
|
||||
app:entryValues="@array/detail_size_entry_values"
|
||||
app:iconSpaceReserved="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="120"
|
||||
android:key="thumb_size_"
|
||||
android:max="400"
|
||||
android:title="@string/settings_eh_thumb_size"
|
||||
app:iconSpaceReserved="false"
|
||||
app:min="80"
|
||||
app:seekBarIncrement="80"
|
||||
app:showSeekBarValue="true" />
|
||||
|
||||
<rikka.preference.SimpleMenuPreference
|
||||
android:defaultValue="0"
|
||||
android:key="thumb_resolution"
|
||||
android:title="@string/settings_eh_thumb_resolution"
|
||||
app:entries="@array/thumb_resolution_entries"
|
||||
app:entryValues="@array/thumb_resolution_entry_values"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="show_jpn_title"
|
||||
android:summary="@string/settings_eh_show_jpn_title_summary"
|
||||
android:title="@string/settings_eh_show_jpn_title"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="show_gallery_pages"
|
||||
android:summary="@string/settings_eh_show_gallery_pages_summary"
|
||||
android:title="@string/settings_eh_show_gallery_pages"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="true"
|
||||
android:key="show_gallery_comments"
|
||||
android:summary="@string/settings_eh_show_gallery_comments_summary"
|
||||
android:title="@string/settings_eh_show_gallery_comments"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="show_tag_translations"
|
||||
android:summary="@string/settings_eh_show_tag_translations_summary"
|
||||
android:title="@string/settings_eh_show_tag_translations"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<com.hippo.ehviewer.ui.legacy.preference.UrlPreference
|
||||
android:key="tag_translations_source"
|
||||
android:title="@string/settings_eh_tag_translations_source"
|
||||
app:iconSpaceReserved="false"
|
||||
app:url="@string/settings_eh_tag_translations_source_url" />
|
||||
|
||||
<Preference
|
||||
android:key="filter"
|
||||
android:summary="@string/settings_eh_filter_summary"
|
||||
android:title="@string/settings_eh_filter"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="cellular_network_warning"
|
||||
android:title="@string/settings_eh_metered_network_warning"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="request_news"
|
||||
android:title="@string/settings_eh_request_news"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<Preference
|
||||
android:key="request_news_timer"
|
||||
android:title="@string/settings_eh_request_news_timepicker"
|
||||
app:dependency="request_news"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="hide_hv_events"
|
||||
android:title="@string/settings_eh_hide_hv_events"
|
||||
app:dependency="request_news"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
</PreferenceScreen>
|
||||
@@ -1,44 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2022 Tarsin Norbin
|
||||
~
|
||||
~ This file is part of EhViewer
|
||||
~
|
||||
~ EhViewer is free software: you can redistribute it and/or modify it
|
||||
~ under the terms of the GNU General Public License as published
|
||||
~ by the Free Software Foundation, either version 3 of the License, or
|
||||
~ (at your option) any later version.
|
||||
~
|
||||
~ EhViewer is distributed in the hope that it will be useful, but WITHOUT
|
||||
~ ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
~ FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details.
|
||||
~
|
||||
~ You should have received a copy of the GNU General Public License along with EhViewer.
|
||||
~ If not, see <https://www.gnu.org/licenses/>.
|
||||
-->
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="require_unlock"
|
||||
android:title="@string/settings_privacy_require_unlock"
|
||||
app:iconSpaceReserved="false" />
|
||||
|
||||
<SeekBarPreference
|
||||
android:defaultValue="0"
|
||||
android:key="require_unlock_delay"
|
||||
android:max="30"
|
||||
android:progress="0"
|
||||
android:summary=""
|
||||
android:title="@string/settings_privacy_require_unlock_delay"
|
||||
app:iconSpaceReserved="false"
|
||||
app:showSeekBarValue="true" />
|
||||
|
||||
<rikka.material.preference.MaterialSwitchPreference
|
||||
android:defaultValue="false"
|
||||
android:key="enable_secure"
|
||||
android:title="@string/settings_privacy_secure"
|
||||
app:iconSpaceReserved="false"
|
||||
app:summaryOff="@string/settings_privacy_secure_summary"
|
||||
app:summaryOn="@string/settings_privacy_secure_summary" />
|
||||
</PreferenceScreen>
|
||||
@@ -1,43 +0,0 @@
|
||||
<?xml version="1.0" encoding="utf-8"?><!--
|
||||
~ Copyright 2016 Hippo Seven
|
||||
~
|
||||
~ Licensed under the Apache License, Version 2.0 (the "License");
|
||||
~ you may not use this file except in compliance with the License.
|
||||
~ You may obtain a copy of the License at
|
||||
~
|
||||
~ http://www.apache.org/licenses/LICENSE-2.0
|
||||
~
|
||||
~ Unless required by applicable law or agreed to in writing, software
|
||||
~ distributed under the License is distributed on an "AS IS" BASIS,
|
||||
~ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
~ See the License for the specific language governing permissions and
|
||||
~ limitations under the License.
|
||||
-->
|
||||
|
||||
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/v_sad_panda_primary_x24"
|
||||
android:key="eh"
|
||||
android:title="@string/settings_eh" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/v_download_primary_x24"
|
||||
android:key="download"
|
||||
android:title="@string/settings_download" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/v_sec_primary_x24"
|
||||
android:key="privacy"
|
||||
android:title="@string/settings_privacy" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/v_adb_primary_x24"
|
||||
android:key="advance"
|
||||
android:title="@string/settings_advanced" />
|
||||
|
||||
<Preference
|
||||
android:icon="@drawable/v_info_primary_x24"
|
||||
android:key="about"
|
||||
android:title="@string/settings_about" />
|
||||
</PreferenceScreen>
|
||||
Reference in new issue
Block a user