Added global preferences object

This commit is contained in:
tom5079
2020-08-30 19:52:51 +09:00
parent fdd9b02388
commit 7ca4418a50
23 changed files with 157 additions and 248 deletions

View File

@@ -0,0 +1,55 @@
/*
* Pupil, Hitomi.la viewer for Android
* Copyright (C) 2020 tom5079
*
* This program 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.
*
* This program 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 this program. If not, see <http://www.gnu.org/licenses/>.
*/
package xyz.quaver.pupil.util
import android.content.SharedPreferences
import kotlin.reflect.KClass
lateinit var preferences: SharedPreferences
object Preferences: SharedPreferences by preferences {
@Suppress("UNCHECKED_CAST")
val putMap = mapOf<KClass<out Any>, (String, Any) -> Unit>(
String::class to { k, v -> edit().putString(k, v as String).apply() },
Int::class to { k, v -> edit().putBoolean(k, v as Boolean).apply() },
Long::class to { k, v -> edit().putLong(k, v as Long).apply() },
Boolean::class to { k, v -> edit().putBoolean(k, v as Boolean).apply() },
Set::class to { k, v -> edit().putStringSet(k, v as Set<String>).apply() }
)
val defMap = mapOf(
String::class to "",
Int::class to -1,
Long::class to -1,
Boolean::class to false,
Set::class to emptySet<Any>()
)
inline operator fun <reified T: Any> set(key: String, value: T) {
putMap[T::class]?.invoke(key, value)
}
inline operator fun <reified T: Any> get(key: String, defaultVal: T = defMap[T::class] as T, setIfNull: Boolean = false): T =
(all[key] as? T) ?: defaultVal.also { if (setIfNull) set(key, defaultVal) }
fun remove(key: String) {
edit().remove(key).apply()
}
}

View File

@@ -25,7 +25,6 @@ import android.os.Build
import android.os.storage.StorageManager
import android.provider.DocumentsContract
import androidx.core.content.ContextCompat
import androidx.preference.PreferenceManager
import java.io.File
import java.io.FileOutputStream
import java.lang.reflect.Array
@@ -41,8 +40,8 @@ fun getCachedGallery(context: Context, galleryID: Int) =
}
fun getDownloadDirectory(context: Context) =
PreferenceManager.getDefaultSharedPreferences(context).getString("dl_location", null).let {
if (it != null && !it.startsWith("content"))
Preferences.get<String>("dl_location").let {
if (it.isNotEmpty() && !it.startsWith("content"))
File(it)
else
context.getExternalFilesDir(null)!!
@@ -124,94 +123,5 @@ fun getVolumePath(context: Context, volumeID: String?): String? {
}.getOrNull()
}
// Credits go to https://stackoverflow.com/questions/34927748/android-5-0-documentfile-from-tree-uri/36162691#36162691
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
fun getVolumeIdFromTreeUri(uri: Uri) =
DocumentsContract.getTreeDocumentId(uri).split(':').let {
if (it.isNotEmpty())
it[0]
else
null
}
@TargetApi(Build.VERSION_CODES.LOLLIPOP)
fun getDocumentPathFromTreeUri(uri: Uri) =
DocumentsContract.getTreeDocumentId(uri).split(':').let {
if (it.size >= 2)
it[1]
else
File.separator
}
fun getFullPathFromTreeUri(context: Context, uri: Uri) : String? {
val volumePath = getVolumePath(context, getVolumeIdFromTreeUri(uri) ?: return null).let {
it ?: return File.separator
if (it.endsWith(File.separator))
it.dropLast(1)
else
it
}
val documentPath = getDocumentPathFromTreeUri(uri).let {
if (it.endsWith(File.separator))
it.dropLast(1)
else
it
}
return if (documentPath.isNotEmpty()) {
if (documentPath.startsWith(File.separator))
volumePath + documentPath
else
volumePath + File.separator + documentPath
} else
volumePath
}
// Huge thanks to avluis(https://github.com/avluis)
// This code is originated from Hentoid(https://github.com/avluis/Hentoid) under Apache-2.0 license.
fun Uri.toFile(context: Context): File? {
val path = this.path ?: return null
val pathSeparator = path.indexOf(':')
val folderName = path.substring(pathSeparator+1)
// Determine whether the designated file is
// - on a removable media (e.g. SD card, OTG)
// or
// - on the internal phone memory
val removableMediaFolderRoots = getExtSdCardPaths(context)
/* First test is to compare root names with known roots of removable media
* In many cases, the SD card root name is shared between pre-SAF (File) and SAF (DocumentFile) frameworks
* (e.g. /storage/3437-3934 vs. /tree/3437-3934)
* This is what the following block is trying to do
*/
for (s in removableMediaFolderRoots) {
val sRoot = s.substring(s.lastIndexOf(File.separatorChar))
val root = path.substring(0, pathSeparator).let {
it.substring(it.lastIndexOf(File.separatorChar))
}
if (sRoot.equals(root, true)) {
return File(s + File.separatorChar + folderName)
}
}
/* In some other cases, there is no common name (e.g. /storage/sdcard1 vs. /tree/3437-3934)
* We can use a slower method to translate the Uri obtained with SAF into a pre-SAF path
* and compare it to the known removable media volume names
*/
val root = getFullPathFromTreeUri(context, this)
for (s in removableMediaFolderRoots) {
if (root?.startsWith(s) == true) {
return File(root)
}
}
return File(context.getExternalFilesDir(null)?.canonicalPath?.substringBeforeLast("/Android/data") ?: return null, folderName)
}
fun File.isParentOf(another: File) =
another.absolutePath.startsWith(this.absolutePath)

View File

@@ -19,6 +19,7 @@
package xyz.quaver.pupil.util
import android.annotation.SuppressLint
import okhttp3.OkHttpClient
import java.util.*
import kotlin.collections.ArrayList
@@ -59,4 +60,11 @@ fun byteToString(byte: Long, precision : Int = 1) : String {
*
* https://stackoverflow.com/questions/38072322/generate-16-bit-unique-ids-in-android-for-startactivityforresult
*/
fun Int.normalizeID() = this.and(0xFFFF)
fun Int.normalizeID() = this.and(0xFFFF)
fun OkHttpClient.Builder.proxyInfo(proxyInfo: ProxyInfo) = this.apply {
proxy(proxyInfo.proxy())
proxyInfo.authenticator()?.let {
proxyAuthenticator(it)
}
}

View File

@@ -19,11 +19,12 @@
package xyz.quaver.pupil.util
import android.content.Context
import androidx.preference.PreferenceManager
import kotlinx.serialization.Serializable
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.encodeToString
import kotlinx.serialization.json.Json
import okhttp3.*
import okhttp3.Authenticator
import okhttp3.Credentials
import java.net.InetSocketAddress
import java.net.Proxy
@@ -36,29 +37,22 @@ data class ProxyInfo(
val password: String? = null
) {
fun proxy() : Proxy {
return if (host == null || port == null)
return if (host.isNullOrBlank() || port == null)
return Proxy.NO_PROXY
else
Proxy(type, InetSocketAddress.createUnresolved(host, port))
}
fun authenticator() = Authenticator { _, response ->
val credential = Credentials.basic(username ?: "", password ?: "")
fun authenticator(): Authenticator? = if (username.isNullOrBlank() || password.isNullOrBlank()) null else
Authenticator { _, response ->
val credential = Credentials.basic(username, password)
response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build()
}
response.request().newBuilder()
.header("Proxy-Authorization", credential)
.build()
}
}
fun getProxy(context: Context) =
getProxyInfo(context).proxy()
fun getProxyInfo(context: Context) =
PreferenceManager.getDefaultSharedPreferences(context).getString("proxy", null).let {
if (it == null)
ProxyInfo(Proxy.Type.DIRECT)
else
Json.decodeFromString(it)
}
fun getProxyInfo(): ProxyInfo =
Json.decodeFromString(Preferences["proxy", Json.encodeToString(ProxyInfo(Proxy.Type.DIRECT))])

View File

@@ -24,14 +24,15 @@ import android.net.Uri
import android.webkit.URLUtil
import androidx.appcompat.app.AlertDialog
import androidx.preference.PreferenceManager
import com.google.android.material.snackbar.Snackbar
import kotlinx.android.synthetic.main.activity_main_content.*
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch
import kotlinx.serialization.decodeFromString
import kotlinx.serialization.json.*
import okhttp3.*
import okhttp3.Call
import okhttp3.Callback
import okhttp3.Request
import okhttp3.Response
import ru.noties.markwon.Markwon
import xyz.quaver.pupil.BuildConfig
import xyz.quaver.pupil.R
@@ -51,17 +52,14 @@ fun getReleases(url: String) : JsonArray {
}
}
fun checkUpdate(context: Context, url: String) : JsonObject? {
fun checkUpdate(url: String) : JsonObject? {
val releases = getReleases(url)
if (releases.isEmpty())
return null
return releases.firstOrNull {
if (PreferenceManager.getDefaultSharedPreferences(context).getBoolean("beta", false))
true
else
it.jsonObject["prerelease"]?.jsonPrimitive?.booleanOrNull == false
Preferences["beta"] || it.jsonObject["prerelease"]?.jsonPrimitive?.booleanOrNull == false
}?.let {
if (it.jsonObject["tag_name"]?.jsonPrimitive?.contentOrNull == BuildConfig.VERSION_NAME)
null
@@ -130,7 +128,7 @@ fun checkUpdate(context: Context, force: Boolean = false) {
CoroutineScope(Dispatchers.Default).launch {
val update =
checkUpdate(context, context.getString(R.string.release_url)) ?: return@launch
checkUpdate(context.getString(R.string.release_url)) ?: return@launch
val url = getApkUrl(update) ?: return@launch