Implemented new favorite backup/restore feature
This commit is contained in:
@@ -62,7 +62,7 @@ dependencies {
|
|||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-core:1.3.9"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
|
implementation "org.jetbrains.kotlinx:kotlinx-coroutines-android:1.3.9"
|
||||||
implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC"
|
//implementation "org.jetbrains.kotlinx:kotlinx-serialization-core:1.0.0-RC"
|
||||||
implementation 'androidx.appcompat:appcompat:1.2.0'
|
implementation 'androidx.appcompat:appcompat:1.2.0'
|
||||||
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
implementation 'androidx.constraintlayout:constraintlayout:2.0.1'
|
||||||
implementation 'androidx.preference:preference:1.1.1'
|
implementation 'androidx.preference:preference:1.1.1'
|
||||||
@@ -92,7 +92,9 @@ dependencies {
|
|||||||
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
|
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
|
||||||
//implementation 'com.andrognito.pinlockview:pinlockview:2.1.0'
|
//implementation 'com.andrognito.pinlockview:pinlockview:2.1.0'
|
||||||
implementation "ru.noties.markwon:core:3.1.0"
|
implementation "ru.noties.markwon:core:3.1.0"
|
||||||
implementation 'xyz.quaver:libpupil:1.0'
|
implementation ("xyz.quaver:libpupil:1.0") {
|
||||||
|
exclude group: 'org.jetbrains.kotlinx', module: 'kotlinx-serialization-core-jvm'
|
||||||
|
}
|
||||||
testImplementation 'junit:junit:4.13'
|
testImplementation 'junit:junit:4.13'
|
||||||
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
|
||||||
androidTestImplementation 'androidx.test:rules:1.3.0'
|
androidTestImplementation 'androidx.test:rules:1.3.0'
|
||||||
|
|||||||
BIN
app/libs/kotlinx-serialization-core-jvm-1.0.0-RC.jar
Normal file
BIN
app/libs/kotlinx-serialization-core-jvm-1.0.0-RC.jar
Normal file
Binary file not shown.
5
app/proguard-rules.pro
vendored
5
app/proguard-rules.pro
vendored
@@ -40,9 +40,10 @@
|
|||||||
-keepattributes *Annotation*, InnerClasses
|
-keepattributes *Annotation*, InnerClasses
|
||||||
-dontnote kotlinx.serialization.SerializationKt
|
-dontnote kotlinx.serialization.SerializationKt
|
||||||
-keep,includedescriptorclasses class xyz.quaver.**$$serializer { *; } # <-- change package name to your app's
|
-keep,includedescriptorclasses class xyz.quaver.**$$serializer { *; } # <-- change package name to your app's
|
||||||
-keepclassmembers class xyz.quaver.pupil** { # <-- change package name to your app's
|
-keepclassmembers class xyz.quaver.pupil.** { # <-- change package name to your app's
|
||||||
*** Companion;
|
*** Companion;
|
||||||
}
|
}
|
||||||
-keepclasseswithmembers class xyz.quaver.pupil** { # <-- change package name to your app's
|
-keepclasseswithmembers class xyz.quaver.pupil.** { # <-- change package name to your app's
|
||||||
kotlinx.serialization.KSerializer serializer(...);
|
kotlinx.serialization.KSerializer serializer(...);
|
||||||
}
|
}
|
||||||
|
-keep class xyz.quaver.pupil.ui.fragment.ManageFavoritesFragment
|
||||||
@@ -20,6 +20,7 @@
|
|||||||
android:roundIcon="@mipmap/ic_launcher_round"
|
android:roundIcon="@mipmap/ic_launcher_round"
|
||||||
android:supportsRtl="true"
|
android:supportsRtl="true"
|
||||||
android:theme="@style/AppTheme"
|
android:theme="@style/AppTheme"
|
||||||
|
android:networkSecurityConfig="@xml/network_security_config"
|
||||||
tools:replace="android:theme"
|
tools:replace="android:theme"
|
||||||
android:requestLegacyExternalStorage="true"
|
android:requestLegacyExternalStorage="true"
|
||||||
tools:ignore="UnusedAttribute">
|
tools:ignore="UnusedAttribute">
|
||||||
@@ -36,7 +37,9 @@
|
|||||||
|
|
||||||
</provider>
|
</provider>
|
||||||
|
|
||||||
<receiver android:name=".BroadcastReciever" android:exported="true">
|
<receiver
|
||||||
|
android:name=".BroadcastReciever"
|
||||||
|
android:exported="true">
|
||||||
<intent-filter>
|
<intent-filter>
|
||||||
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
<action android:name="android.intent.action.DOWNLOAD_COMPLETE" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
@@ -204,7 +207,9 @@
|
|||||||
</activity>
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.SettingsActivity"
|
android:name=".ui.SettingsActivity"
|
||||||
android:label="@string/settings_title" />
|
android:label="@string/settings_title">
|
||||||
|
<tools:validation testUrl="http://ix.io/eer" />
|
||||||
|
</activity>
|
||||||
<activity
|
<activity
|
||||||
android:name=".ui.MainActivity"
|
android:name=".ui.MainActivity"
|
||||||
android:configChanges="keyboardHidden|orientation|screenSize"
|
android:configChanges="keyboardHidden|orientation|screenSize"
|
||||||
@@ -215,6 +220,17 @@
|
|||||||
|
|
||||||
<category android:name="android.intent.category.LAUNCHER" />
|
<category android:name="android.intent.category.LAUNCHER" />
|
||||||
</intent-filter>
|
</intent-filter>
|
||||||
|
<intent-filter>
|
||||||
|
<action android:name="android.intent.action.VIEW" />
|
||||||
|
|
||||||
|
<category android:name="android.intent.category.DEFAULT" />
|
||||||
|
<category android:name="android.intent.category.BROWSABLE" />
|
||||||
|
|
||||||
|
<data
|
||||||
|
android:scheme="http"
|
||||||
|
android:host="ix.io"
|
||||||
|
android:pathPattern="/..*" />
|
||||||
|
</intent-filter>
|
||||||
</activity>
|
</activity>
|
||||||
<activity android:name="net.rdrei.android.dirchooser.DirectoryChooserActivity" />
|
<activity android:name="net.rdrei.android.dirchooser.DirectoryChooserActivity" />
|
||||||
</application>
|
</application>
|
||||||
|
|||||||
@@ -83,6 +83,10 @@ class Pupil : MultiDexApplication() {
|
|||||||
histories.clear()
|
histories.clear()
|
||||||
histories.addAll(it)
|
histories.addAll(it)
|
||||||
}
|
}
|
||||||
|
favorites.reversed().let {
|
||||||
|
favorites.clear()
|
||||||
|
favorites.addAll(it)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
preference.edit().putBoolean("old_history", false).apply()
|
preference.edit().putBoolean("old_history", false).apply()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -67,7 +67,7 @@ data class Tag(val area: String?, val tag: String, val isNegative: Boolean = fal
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Tags(tag: List<Tag?>?) : ArrayList<Tag>() {
|
class Tags(val tags: MutableSet<Tag> = mutableSetOf()) : MutableSet<Tag> by tags {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun parse(tags: String) : Tags {
|
fun parse(tags: String) : Tags {
|
||||||
@@ -77,20 +77,13 @@ class Tags(tag: List<Tag?>?) : ArrayList<Tag>() {
|
|||||||
Tag.parse(it)
|
Tag.parse(it)
|
||||||
else
|
else
|
||||||
null
|
null
|
||||||
}
|
}.filterNotNull().toMutableSet()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init {
|
|
||||||
tag?.forEach {
|
|
||||||
if (it != null)
|
|
||||||
add(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun contains(element: String): Boolean {
|
fun contains(element: String): Boolean {
|
||||||
forEach {
|
tags.forEach {
|
||||||
if (it.toString() == element)
|
if (it.toString() == element)
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -99,23 +92,25 @@ class Tags(tag: List<Tag?>?) : ArrayList<Tag>() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun add(element: String): Boolean {
|
fun add(element: String): Boolean {
|
||||||
return super.add(Tag.parse(element))
|
return tags.add(Tag.parse(element))
|
||||||
}
|
}
|
||||||
|
|
||||||
fun remove(element: String) {
|
fun remove(element: String) {
|
||||||
filter { it.toString() == element }.forEach {
|
tags.filter { it.toString() == element }.forEach {
|
||||||
remove(it)
|
tags.remove(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun removeByArea(area: String, isNegative: Boolean? = null) {
|
fun removeByArea(area: String, isNegative: Boolean? = null) {
|
||||||
filter { it.area == area && (if(isNegative == null) true else (it.isNegative == isNegative)) }.forEach {
|
tags.filter { it.area == area && (if(isNegative == null) true else (it.isNegative == isNegative)) }.forEach {
|
||||||
remove(it)
|
tags.remove(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return joinToString(" ") { it.toString() }
|
return tags.joinToString(" ") { it.toString() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
@@ -62,7 +62,6 @@ import xyz.quaver.hitomi.getSuggestionsForQuery
|
|||||||
import xyz.quaver.pupil.Pupil
|
import xyz.quaver.pupil.Pupil
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
|
||||||
import xyz.quaver.pupil.types.Tag
|
|
||||||
import xyz.quaver.pupil.types.TagSuggestion
|
import xyz.quaver.pupil.types.TagSuggestion
|
||||||
import xyz.quaver.pupil.types.Tags
|
import xyz.quaver.pupil.types.Tags
|
||||||
import xyz.quaver.pupil.ui.dialog.GalleryDialog
|
import xyz.quaver.pupil.ui.dialog.GalleryDialog
|
||||||
@@ -153,6 +152,18 @@ class MainActivity : AppCompatActivity() {
|
|||||||
this@MainActivity.favorites = favorites
|
this@MainActivity.favorites = favorites
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (intent.action == Intent.ACTION_VIEW) {
|
||||||
|
intent.dataString?.let { url ->
|
||||||
|
restore(favorites, url,
|
||||||
|
onFailure = {
|
||||||
|
Snackbar.make(this.main_recyclerview, R.string.settings_backup_failed, Snackbar.LENGTH_LONG).show()
|
||||||
|
}, onSuccess = {
|
||||||
|
Snackbar.make(this.main_recyclerview, getString(R.string.settings_restore_success, it.size), Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
setContentView(R.layout.activity_main)
|
setContentView(R.layout.activity_main)
|
||||||
|
|
||||||
checkUpdate(this)
|
checkUpdate(this)
|
||||||
@@ -771,7 +782,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
if (!favoritesFile.exists()) {
|
if (!favoritesFile.exists()) {
|
||||||
favoritesFile.createNewFile()
|
favoritesFile.createNewFile()
|
||||||
favoritesFile.writeText(Json.encodeToString(Tags(listOf())))
|
favoritesFile.writeText(Json.encodeToString(Tags()))
|
||||||
}
|
}
|
||||||
|
|
||||||
setOnLeftMenuClickListener(object: FloatingSearchView.OnLeftMenuClickListener {
|
setOnLeftMenuClickListener(object: FloatingSearchView.OnLeftMenuClickListener {
|
||||||
@@ -833,7 +844,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
clearSuggestions()
|
clearSuggestions()
|
||||||
|
|
||||||
if (query.isEmpty() or query.endsWith(' ')) {
|
if (query.isEmpty() or query.endsWith(' ')) {
|
||||||
swapSuggestions(Json.decodeFromString<Tags>(favoritesFile.readText()).map {
|
swapSuggestions(Tags(Json.decodeFromString(favoritesFile.readText())).map {
|
||||||
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
|
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -911,7 +922,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
favorites.add(tag)
|
favorites.add(tag)
|
||||||
}
|
}
|
||||||
|
|
||||||
favoritesFile.writeText(Json.encodeToString(favorites))
|
favoritesFile.writeText(Json.encodeToString(favorites.tags))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -951,7 +962,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
setOnFocusChangeListener(object: FloatingSearchView.OnFocusChangeListener {
|
setOnFocusChangeListener(object: FloatingSearchView.OnFocusChangeListener {
|
||||||
override fun onFocus() {
|
override fun onFocus() {
|
||||||
if (query.isEmpty() or query.endsWith(' '))
|
if (query.isEmpty() or query.endsWith(' '))
|
||||||
swapSuggestions(Tags(Json.decodeFromString<List<Tag>>(favoritesFile.readText())).map {
|
swapSuggestions(Tags(Json.decodeFromString(favoritesFile.readText())).map {
|
||||||
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
|
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -1076,7 +1087,7 @@ class MainActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
Mode.FAVORITE -> {
|
Mode.FAVORITE -> {
|
||||||
when {
|
when {
|
||||||
query.isEmpty() -> favorites.toList().also {
|
query.isEmpty() -> favorites.reversed().also {
|
||||||
totalItems = it.size
|
totalItems = it.size
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
|
|||||||
@@ -80,7 +80,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
|
|
||||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||||
when(requestCode) {
|
when(requestCode) {
|
||||||
R.id.request_lock -> {
|
R.id.request_lock.normalizeID() -> {
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
supportFragmentManager
|
supportFragmentManager
|
||||||
.beginTransaction()
|
.beginTransaction()
|
||||||
@@ -89,7 +89,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
.commitAllowingStateLoss()
|
.commitAllowingStateLoss()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.request_restore -> {
|
R.id.request_restore.normalizeID() -> {
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
val uri = data?.data ?: return
|
val uri = data?.data ?: return
|
||||||
|
|
||||||
@@ -103,7 +103,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
(application as Pupil).favorites.addAll(Json.decodeFromString<List<Int>>(str).also {
|
(application as Pupil).favorites.addAll(Json.decodeFromString<List<Int>>(str).also {
|
||||||
Snackbar.make(
|
Snackbar.make(
|
||||||
window.decorView,
|
window.decorView,
|
||||||
getString(R.string.settings_restore_successful, it.size),
|
getString(R.string.settings_restore_success, it.size),
|
||||||
Snackbar.LENGTH_LONG
|
Snackbar.LENGTH_LONG
|
||||||
).show()
|
).show()
|
||||||
})
|
})
|
||||||
@@ -116,7 +116,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.request_download_folder -> {
|
R.id.request_download_folder.normalizeID() -> {
|
||||||
if (resultCode == Activity.RESULT_OK) {
|
if (resultCode == Activity.RESULT_OK) {
|
||||||
data?.data?.also { uri ->
|
data?.data?.also { uri ->
|
||||||
val takeFlags: Int =
|
val takeFlags: Int =
|
||||||
@@ -140,7 +140,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
R.id.request_download_folder_old -> {
|
R.id.request_download_folder_old.normalizeID() -> {
|
||||||
if (resultCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
|
if (resultCode == DirectoryChooserActivity.RESULT_CODE_DIR_SELECTED) {
|
||||||
val directory = data?.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR)!!
|
val directory = data?.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR)!!
|
||||||
|
|
||||||
@@ -163,7 +163,7 @@ class SettingsActivity : AppCompatActivity() {
|
|||||||
@SuppressLint("InlinedApi")
|
@SuppressLint("InlinedApi")
|
||||||
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<out String>, grantResults: IntArray) {
|
||||||
when (requestCode) {
|
when (requestCode) {
|
||||||
R.id.request_write_permission_and_saf -> {
|
R.id.request_write_permission_and_saf.normalizeID() -> {
|
||||||
if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED) {
|
if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED) {
|
||||||
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
|
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
|
||||||
putExtra("android.content.extra.SHOW_ADVANCED", true)
|
putExtra("android.content.extra.SHOW_ADVANCED", true)
|
||||||
|
|||||||
@@ -31,8 +31,7 @@ import xyz.quaver.pupil.ui.LockActivity
|
|||||||
import xyz.quaver.pupil.util.Lock
|
import xyz.quaver.pupil.util.Lock
|
||||||
import xyz.quaver.pupil.util.LockManager
|
import xyz.quaver.pupil.util.LockManager
|
||||||
|
|
||||||
class LockSettingsFragment :
|
class LockSettingsFragment : PreferenceFragmentCompat() {
|
||||||
PreferenceFragmentCompat() {
|
|
||||||
|
|
||||||
override fun onResume() {
|
override fun onResume() {
|
||||||
super.onResume()
|
super.onResume()
|
||||||
|
|||||||
@@ -0,0 +1,99 @@
|
|||||||
|
/*
|
||||||
|
* 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.ui.fragment
|
||||||
|
|
||||||
|
import android.content.Intent
|
||||||
|
import android.os.Bundle
|
||||||
|
import android.widget.EditText
|
||||||
|
import android.widget.TextView
|
||||||
|
import androidx.appcompat.app.AlertDialog
|
||||||
|
import androidx.core.content.ContextCompat
|
||||||
|
import androidx.preference.Preference
|
||||||
|
import androidx.preference.PreferenceFragmentCompat
|
||||||
|
import com.google.android.material.snackbar.Snackbar
|
||||||
|
import okhttp3.*
|
||||||
|
import xyz.quaver.pupil.Pupil
|
||||||
|
import xyz.quaver.pupil.R
|
||||||
|
import xyz.quaver.pupil.util.restore
|
||||||
|
import java.io.File
|
||||||
|
import java.io.IOException
|
||||||
|
|
||||||
|
class ManageFavoritesFragment : PreferenceFragmentCompat() {
|
||||||
|
|
||||||
|
override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) {
|
||||||
|
setPreferencesFromResource(R.xml.manage_favorites_preferences, rootKey)
|
||||||
|
|
||||||
|
initPreferences()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun initPreferences() {
|
||||||
|
findPreference<Preference>("backup")?.setOnPreferenceClickListener {
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(getString(R.string.backup_url))
|
||||||
|
.post(
|
||||||
|
FormBody.Builder()
|
||||||
|
.add("f:1", File(ContextCompat.getDataDir(requireContext()), "favorites.json").readText())
|
||||||
|
.build()
|
||||||
|
).build()
|
||||||
|
|
||||||
|
OkHttpClient().newCall(request).enqueue(object: Callback {
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
val view = view ?: return
|
||||||
|
Snackbar.make(view, R.string.settings_backup_failed, Snackbar.LENGTH_LONG).show()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
Intent(Intent.ACTION_SEND).apply {
|
||||||
|
type = "text/plain"
|
||||||
|
putExtra(Intent.EXTRA_TEXT, response.body()?.use { it.string() }?.replace("\n", ""))
|
||||||
|
}.let {
|
||||||
|
context?.startActivity(Intent.createChooser(it, getString(R.string.settings_backup_share)))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
findPreference<Preference>("restore")?.setOnPreferenceClickListener {
|
||||||
|
val editText = EditText(requireContext()).apply {
|
||||||
|
setText(getString(R.string.backup_url), TextView.BufferType.EDITABLE)
|
||||||
|
}
|
||||||
|
|
||||||
|
AlertDialog.Builder(requireContext())
|
||||||
|
.setTitle(R.string.settings_restore_title)
|
||||||
|
.setView(editText)
|
||||||
|
.setPositiveButton(android.R.string.ok) { _, _ ->
|
||||||
|
val favorites = (activity?.application as? Pupil)?.favorites ?: return@setPositiveButton
|
||||||
|
restore(favorites, editText.text.toString(),
|
||||||
|
onFailure = onFailure@{
|
||||||
|
val view = view ?: return@onFailure
|
||||||
|
Snackbar.make(view, R.string.settings_restore_failed, Snackbar.LENGTH_LONG).show()
|
||||||
|
}, onSuccess = onSuccess@{
|
||||||
|
val view = view ?: return@onSuccess
|
||||||
|
Snackbar.make(view, getString(R.string.settings_restore_success, it.size), Snackbar.LENGTH_LONG).show()
|
||||||
|
})
|
||||||
|
}.setNegativeButton(android.R.string.cancel) { _, _ ->
|
||||||
|
// Do Nothing
|
||||||
|
}.show()
|
||||||
|
|
||||||
|
true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
@@ -181,23 +181,6 @@ class SettingsFragment :
|
|||||||
"nomedia" -> {
|
"nomedia" -> {
|
||||||
File(getDownloadDirectory(context), ".nomedia").createNewFile()
|
File(getDownloadDirectory(context), ".nomedia").createNewFile()
|
||||||
}
|
}
|
||||||
"backup" -> {
|
|
||||||
File(ContextCompat.getDataDir(requireContext()), "favorites.json").copyTo(
|
|
||||||
File(getDownloadDirectory(requireContext()), "favorites.json"),
|
|
||||||
true
|
|
||||||
)
|
|
||||||
|
|
||||||
Snackbar.make(this@SettingsFragment.listView, R.string.settings_backup_snackbar, Snackbar.LENGTH_LONG)
|
|
||||||
.show()
|
|
||||||
}
|
|
||||||
"restore" -> {
|
|
||||||
val intent = Intent(Intent.ACTION_GET_CONTENT).apply {
|
|
||||||
addCategory(Intent.CATEGORY_OPENABLE)
|
|
||||||
type = "*/*"
|
|
||||||
}
|
|
||||||
|
|
||||||
activity?.startActivityForResult(intent, R.id.request_restore.normalizeID())
|
|
||||||
}
|
|
||||||
"user_id" -> {
|
"user_id" -> {
|
||||||
(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(
|
(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(
|
||||||
ClipData.newPlainText("user_id", sharedPreference.getString("user_id", ""))
|
ClipData.newPlainText("user_id", sharedPreference.getString("user_id", ""))
|
||||||
@@ -349,10 +332,7 @@ class SettingsFragment :
|
|||||||
"nomedia" -> {
|
"nomedia" -> {
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
onPreferenceClickListener = this@SettingsFragment
|
||||||
}
|
}
|
||||||
"backup" -> {
|
"old_import_galleries" -> {
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
|
||||||
}
|
|
||||||
"restore" -> {
|
|
||||||
onPreferenceClickListener = this@SettingsFragment
|
onPreferenceClickListener = this@SettingsFragment
|
||||||
}
|
}
|
||||||
"user_id" -> {
|
"user_id" -> {
|
||||||
|
|||||||
@@ -26,14 +26,15 @@ import java.io.File
|
|||||||
class GalleryList(private val file: File, private val list: MutableSet<Int> = mutableSetOf()) : MutableSet<Int> by list {
|
class GalleryList(private val file: File, private val list: MutableSet<Int> = mutableSetOf()) : MutableSet<Int> by list {
|
||||||
|
|
||||||
init {
|
init {
|
||||||
|
if (!file.exists()) {
|
||||||
|
file.parentFile?.mkdirs()
|
||||||
|
save()
|
||||||
|
}
|
||||||
load()
|
load()
|
||||||
}
|
}
|
||||||
|
|
||||||
fun load() {
|
fun load() {
|
||||||
synchronized(this) {
|
synchronized(this) {
|
||||||
if (!file.exists())
|
|
||||||
file.parentFile?.mkdirs()
|
|
||||||
|
|
||||||
list.clear()
|
list.clear()
|
||||||
list.addAll(
|
list.addAll(
|
||||||
Json.decodeFromString<List<Int>>(file.bufferedReader().use { it.readText() })
|
Json.decodeFromString<List<Int>>(file.bufferedReader().use { it.readText() })
|
||||||
|
|||||||
@@ -18,42 +18,32 @@
|
|||||||
|
|
||||||
package xyz.quaver.pupil.util
|
package xyz.quaver.pupil.util
|
||||||
|
|
||||||
import android.annotation.SuppressLint
|
|
||||||
import android.app.DownloadManager
|
import android.app.DownloadManager
|
||||||
import android.app.PendingIntent
|
|
||||||
import android.content.Context
|
import android.content.Context
|
||||||
import android.content.Intent
|
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import android.util.Base64
|
import android.webkit.URLUtil
|
||||||
import androidx.appcompat.app.AlertDialog
|
import androidx.appcompat.app.AlertDialog
|
||||||
import androidx.core.app.NotificationCompat
|
|
||||||
import androidx.core.app.NotificationManagerCompat
|
|
||||||
import androidx.preference.PreferenceManager
|
import androidx.preference.PreferenceManager
|
||||||
import kotlinx.coroutines.*
|
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.decodeFromString
|
||||||
import kotlinx.serialization.json.*
|
import kotlinx.serialization.json.*
|
||||||
import okhttp3.*
|
import okhttp3.*
|
||||||
import ru.noties.markwon.Markwon
|
import ru.noties.markwon.Markwon
|
||||||
import xyz.quaver.hitomi.GalleryBlock
|
|
||||||
import xyz.quaver.hitomi.Reader
|
|
||||||
import xyz.quaver.hitomi.getGalleryBlock
|
|
||||||
import xyz.quaver.hitomi.getReader
|
|
||||||
import xyz.quaver.proxy
|
|
||||||
import xyz.quaver.pupil.BroadcastReciever
|
|
||||||
import xyz.quaver.pupil.BuildConfig
|
import xyz.quaver.pupil.BuildConfig
|
||||||
import xyz.quaver.pupil.R
|
import xyz.quaver.pupil.R
|
||||||
import xyz.quaver.pupil.util.download.Cache
|
|
||||||
import xyz.quaver.pupil.util.download.Metadata
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import java.util.concurrent.TimeUnit
|
|
||||||
|
|
||||||
fun getReleases(url: String) : JsonArray {
|
fun getReleases(url: String) : JsonArray {
|
||||||
return try {
|
return try {
|
||||||
URL(url).readText().let {
|
URL(url).readText().let {
|
||||||
Json.decodeFromString(it)
|
Json.parseToJsonElement(it).jsonArray
|
||||||
}
|
}
|
||||||
} catch (e: Exception) {
|
} catch (e: Exception) {
|
||||||
JsonArray(emptyList())
|
JsonArray(emptyList())
|
||||||
@@ -185,3 +175,28 @@ fun checkUpdate(context: Context, force: Boolean = false) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun restore(favorites: GalleryList, url: String, onFailure: ((Exception) -> Unit)? = null, onSuccess: ((List<Int>) -> Unit)? = null) {
|
||||||
|
if (!URLUtil.isValidUrl(url)) {
|
||||||
|
onFailure?.invoke(IllegalArgumentException())
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val request = Request.Builder()
|
||||||
|
.url(url)
|
||||||
|
.get()
|
||||||
|
.build()
|
||||||
|
|
||||||
|
OkHttpClient().newCall(request).enqueue(object: Callback {
|
||||||
|
override fun onFailure(call: Call, e: IOException) {
|
||||||
|
onFailure?.invoke(e)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun onResponse(call: Call, response: Response) {
|
||||||
|
Json.decodeFromString<List<Int>>(response.body().use { it?.string() } ?: "[]").let {
|
||||||
|
favorites.addAll(it)
|
||||||
|
onSuccess?.invoke(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -54,7 +54,7 @@
|
|||||||
<string name="settings_clear_downloads">ダウンロード削除</string>
|
<string name="settings_clear_downloads">ダウンロード削除</string>
|
||||||
<string name="settings_clear_downloads_alert_message">ダウンロードしたギャラリーを全て削除します。\n実行しますか?</string>
|
<string name="settings_clear_downloads_alert_message">ダウンロードしたギャラリーを全て削除します。\n実行しますか?</string>
|
||||||
<string name="settings_mirror_summary">ミラーサーバからイメージをロード</string>
|
<string name="settings_mirror_summary">ミラーサーバからイメージをロード</string>
|
||||||
<string name="main_drawer_favorite">お気に入り</string>
|
<string name="main_drawer_favorite">ブックマーク</string>
|
||||||
<string name="main_open_gallery_by_id">ギャラリー番号で見る</string>
|
<string name="main_open_gallery_by_id">ギャラリー番号で見る</string>
|
||||||
<string name="reader_failed_to_find_gallery">エラーが発生しました</string>
|
<string name="reader_failed_to_find_gallery">エラーが発生しました</string>
|
||||||
<string name="settings_storage">ストレージ</string>
|
<string name="settings_storage">ストレージ</string>
|
||||||
@@ -91,11 +91,11 @@
|
|||||||
<string name="settings_nomedia_title">イメージを隠す</string>
|
<string name="settings_nomedia_title">イメージを隠す</string>
|
||||||
<string name="main_delete">削除</string>
|
<string name="main_delete">削除</string>
|
||||||
<string name="main_download">ダウンロード</string>
|
<string name="main_download">ダウンロード</string>
|
||||||
<string name="settings_backup_title">お気に入りバックアップ</string>
|
<string name="settings_backup_title">ブックマークバックアップ</string>
|
||||||
<string name="settings_restore_title">お気に入り復元</string>
|
<string name="settings_restore_title">ブックマーク復元</string>
|
||||||
<string name="settings_backup_snackbar">バックアップファイルを作成しました</string>
|
<string name="settings_backup_file_created">バックアップファイルを作成しました</string>
|
||||||
<string name="settings_restore_failed">復元に失敗しました</string>
|
<string name="settings_restore_failed">復元に失敗しました</string>
|
||||||
<string name="settings_restore_successful">%1$d項目を復元しました</string>
|
<string name="settings_restore_success">%1$d項目を復元しました</string>
|
||||||
<string name="settings_dl_location">ダウンロード場所</string>
|
<string name="settings_dl_location">ダウンロード場所</string>
|
||||||
<string name="settings_dl_location_internal">内部ストレージ</string>
|
<string name="settings_dl_location_internal">内部ストレージ</string>
|
||||||
<string name="settings_dl_location_removable">外部SDカード</string>
|
<string name="settings_dl_location_removable">外部SDカード</string>
|
||||||
@@ -137,4 +137,7 @@
|
|||||||
<string name="reader_fab_auto">自動スクロール</string>
|
<string name="reader_fab_auto">自動スクロール</string>
|
||||||
<string name="search_all">全てのギャラリーを対象に検索</string>
|
<string name="search_all">全てのギャラリーを対象に検索</string>
|
||||||
<string name="settings_rtl">綴じ方向を左にする</string>
|
<string name="settings_rtl">綴じ方向を左にする</string>
|
||||||
|
<string name="settings_manage_favorites">ブックマーク管理</string>
|
||||||
|
<string name="settings_backup_failed">エラーが発生しました</string>
|
||||||
|
<string name="settings_backup_share">バックアップ共有</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -91,9 +91,9 @@
|
|||||||
<string name="main_download">다운로드</string>
|
<string name="main_download">다운로드</string>
|
||||||
<string name="settings_backup_title">즐겨찾기 백업</string>
|
<string name="settings_backup_title">즐겨찾기 백업</string>
|
||||||
<string name="settings_restore_title">즐겨찾기 복원</string>
|
<string name="settings_restore_title">즐겨찾기 복원</string>
|
||||||
<string name="settings_backup_snackbar">백업 파일을 생성하였습니다</string>
|
<string name="settings_backup_file_created">백업 파일을 생성하였습니다</string>
|
||||||
<string name="settings_restore_failed">복원에 실패했습니다</string>
|
<string name="settings_restore_failed">복원에 실패했습니다</string>
|
||||||
<string name="settings_restore_successful">%1$d개 항목을 복원했습니다</string>
|
<string name="settings_restore_success">%1$d개 항목을 복원했습니다</string>
|
||||||
<string name="settings_dl_location">다운로드 위치</string>
|
<string name="settings_dl_location">다운로드 위치</string>
|
||||||
<string name="settings_dl_location_internal">내부 저장공간</string>
|
<string name="settings_dl_location_internal">내부 저장공간</string>
|
||||||
<string name="settings_dl_location_removable">외부 SD카드</string>
|
<string name="settings_dl_location_removable">외부 SD카드</string>
|
||||||
@@ -137,4 +137,7 @@
|
|||||||
<string name="reader_fab_auto">자동 스크롤</string>
|
<string name="reader_fab_auto">자동 스크롤</string>
|
||||||
<string name="search_all">모든 갤러리 검색</string>
|
<string name="search_all">모든 갤러리 검색</string>
|
||||||
<string name="settings_rtl">좌측으로 페이지 넘기기</string>
|
<string name="settings_rtl">좌측으로 페이지 넘기기</string>
|
||||||
|
<string name="settings_manage_favorites">즐겨찾기 관리</string>
|
||||||
|
<string name="settings_backup_failed">업로드 실패</string>
|
||||||
|
<string name="settings_backup_share">백업 공유</string>
|
||||||
</resources>
|
</resources>
|
||||||
@@ -5,8 +5,6 @@
|
|||||||
<item name="request_lock" type="id" />
|
<item name="request_lock" type="id" />
|
||||||
<item name="request_restore" type="id" />
|
<item name="request_restore" type="id" />
|
||||||
|
|
||||||
<item name="request_import_old_galleries" type="id" />
|
|
||||||
<item name="request_import_old_galleries_old" type="id" />
|
|
||||||
<item name="request_download_folder" type="id" />
|
<item name="request_download_folder" type="id" />
|
||||||
<item name="request_download_folder_old" type="id" />
|
<item name="request_download_folder_old" type="id" />
|
||||||
<item name="request_write_permission_and_saf" type="id" />
|
<item name="request_write_permission_and_saf" type="id" />
|
||||||
|
|||||||
@@ -9,6 +9,8 @@
|
|||||||
<string name="email" translatable="false">mailto:pupil.hentai@gmail.com</string>
|
<string name="email" translatable="false">mailto:pupil.hentai@gmail.com</string>
|
||||||
<string name="discord" translatable="false">https://discord.gg/Stj4b5v</string>
|
<string name="discord" translatable="false">https://discord.gg/Stj4b5v</string>
|
||||||
|
|
||||||
|
<string name="backup_url" translatable="false">http://ix.io/</string>
|
||||||
|
|
||||||
<string name="main_settings" translatable="false">Settings</string>
|
<string name="main_settings" translatable="false">Settings</string>
|
||||||
<string name="galleryblock_thumbnail_description" translatable="false">Thumbnail</string>
|
<string name="galleryblock_thumbnail_description" translatable="false">Thumbnail</string>
|
||||||
|
|
||||||
@@ -154,15 +156,21 @@
|
|||||||
<string name="settings_dark_mode_title">Dark mode</string>
|
<string name="settings_dark_mode_title">Dark mode</string>
|
||||||
<string name="settings_dark_mode_summary">Protect yourself against light attacks!</string>
|
<string name="settings_dark_mode_summary">Protect yourself against light attacks!</string>
|
||||||
<string name="settings_nomedia_title">Hide image from gallery</string>
|
<string name="settings_nomedia_title">Hide image from gallery</string>
|
||||||
<string name="settings_backup_title">Backup favorites</string>
|
|
||||||
<string name="settings_backup_snackbar">Backup file created</string>
|
|
||||||
<string name="settings_restore_title">Restore favorites</string>
|
|
||||||
<string name="settings_restore_failed">Restore failed</string>
|
|
||||||
<string name="settings_restore_successful">%1$d entries restored</string>
|
|
||||||
<string name="settings_import_old_galleries">Import old galleries</string>
|
<string name="settings_import_old_galleries">Import old galleries</string>
|
||||||
<string name="settings_user_id">User ID</string>
|
<string name="settings_user_id">User ID</string>
|
||||||
<string name="settings_user_id_toast">User ID is copied to clipboard</string>
|
<string name="settings_user_id_toast">User ID is copied to clipboard</string>
|
||||||
|
|
||||||
|
<!-- MANAGE FAVORITES -->
|
||||||
|
|
||||||
|
<string name="settings_manage_favorites">Manage favorites</string>
|
||||||
|
<string name="settings_backup_title">Backup favorites</string>
|
||||||
|
<string name="settings_backup_failed">Upload Failed</string>
|
||||||
|
<string name="settings_backup_share">Share Backup</string>
|
||||||
|
<string name="settings_backup_file_created">Backup file created</string>
|
||||||
|
<string name="settings_restore_title">Restore favorites</string>
|
||||||
|
<string name="settings_restore_failed">Restore failed</string>
|
||||||
|
<string name="settings_restore_success">%1$d entries restored</string>
|
||||||
|
|
||||||
<!-- SETTINGS/APP LOCK ACTIVITY -->
|
<!-- SETTINGS/APP LOCK ACTIVITY -->
|
||||||
|
|
||||||
<string name="settings_lock_none">None</string>
|
<string name="settings_lock_none">None</string>
|
||||||
|
|||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.preference.PreferenceScreen
|
<PreferenceScreen
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
@@ -23,4 +23,4 @@
|
|||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
</androidx.preference.PreferenceScreen>
|
</PreferenceScreen>
|
||||||
30
app/src/main/res/xml/manage_favorites_preferences.xml
Normal file
30
app/src/main/res/xml/manage_favorites_preferences.xml
Normal file
@@ -0,0 +1,30 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<PreferenceScreen xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:key="backup"
|
||||||
|
app:title="@string/settings_backup_title"/>
|
||||||
|
|
||||||
|
<Preference
|
||||||
|
app:key="restore"
|
||||||
|
app:title="@string/settings_restore_title"/>
|
||||||
|
|
||||||
|
</PreferenceScreen>
|
||||||
25
app/src/main/res/xml/network_security_config.xml
Normal file
25
app/src/main/res/xml/network_security_config.xml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<!--
|
||||||
|
~ 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/>.
|
||||||
|
-->
|
||||||
|
|
||||||
|
<network-security-config>
|
||||||
|
<domain-config cleartextTrafficPermitted="true">
|
||||||
|
<domain includeSubdomains="false">ix.io</domain>
|
||||||
|
</domain-config>
|
||||||
|
|
||||||
|
</network-security-config>
|
||||||
@@ -1,5 +1,5 @@
|
|||||||
<?xml version="1.0" encoding="utf-8"?>
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
<androidx.preference.PreferenceScreen
|
<PreferenceScreen
|
||||||
xmlns:app="http://schemas.android.com/apk/res-auto">
|
xmlns:app="http://schemas.android.com/apk/res-auto">
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
@@ -101,12 +101,8 @@
|
|||||||
app:title="@string/settings_nomedia_title"/>
|
app:title="@string/settings_nomedia_title"/>
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:key="backup"
|
app:fragment="xyz.quaver.pupil.ui.fragment.ManageFavoritesFragment"
|
||||||
app:title="@string/settings_backup_title"/>
|
app:title="@string/settings_manage_favorites"/>
|
||||||
|
|
||||||
<Preference
|
|
||||||
app:key="restore"
|
|
||||||
app:title="@string/settings_restore_title"/>
|
|
||||||
|
|
||||||
<Preference
|
<Preference
|
||||||
app:key="user_id"
|
app:key="user_id"
|
||||||
@@ -114,4 +110,4 @@
|
|||||||
|
|
||||||
</PreferenceCategory>
|
</PreferenceCategory>
|
||||||
|
|
||||||
</androidx.preference.PreferenceScreen>
|
</PreferenceScreen>
|
||||||
Reference in New Issue
Block a user