[DO NOT USE] Drop androidx preference, drop rikkax preference

This commit is contained in:
tarsin committed 2023-06-05 12:17:55 +08:00
1 parent 8c6d28d6e2
commit 3664ca846c
37 files changed
+9 -3630

No files matched your search

+4 -10
View File
@@ -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
}
-4
View File
@@ -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)
}
}
}
+1 -1
View File
@@ -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"
-1
View File
@@ -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>
-59
View File
@@ -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>
-116
View File
@@ -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>
-187
View File
@@ -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>
-44
View File
@@ -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>
-43
View File
@@ -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>