diff --git a/app/build.gradle b/app/build.gradle
index 35555aa3..7c267bee 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -62,7 +62,7 @@ dependencies {
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
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-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.constraintlayout:constraintlayout:2.0.1'
implementation 'androidx.preference:preference:1.1.1'
@@ -92,7 +92,9 @@ dependencies {
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
//implementation 'com.andrognito.pinlockview:pinlockview:2.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'
androidTestImplementation 'androidx.test.ext:junit:1.1.1'
androidTestImplementation 'androidx.test:rules:1.3.0'
diff --git a/app/libs/kotlinx-serialization-core-jvm-1.0.0-RC.jar b/app/libs/kotlinx-serialization-core-jvm-1.0.0-RC.jar
new file mode 100644
index 00000000..02e866d4
Binary files /dev/null and b/app/libs/kotlinx-serialization-core-jvm-1.0.0-RC.jar differ
diff --git a/app/proguard-rules.pro b/app/proguard-rules.pro
index e6b5641d..13d64938 100644
--- a/app/proguard-rules.pro
+++ b/app/proguard-rules.pro
@@ -40,9 +40,10 @@
-keepattributes *Annotation*, InnerClasses
-dontnote kotlinx.serialization.SerializationKt
-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;
}
--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(...);
-}
\ No newline at end of file
+}
+-keep class xyz.quaver.pupil.ui.fragment.ManageFavoritesFragment
\ No newline at end of file
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index dab7dfb3..bff446d8 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -6,9 +6,9 @@
-
-
-
+
+
+
@@ -36,9 +37,11 @@
-
+
-
+
@@ -204,7 +207,9 @@
+ android:label="@string/settings_title">
+
+
+
+
+
+
+
+
+
+
diff --git a/app/src/main/java/xyz/quaver/pupil/Pupil.kt b/app/src/main/java/xyz/quaver/pupil/Pupil.kt
index f13b4dd8..3e1207fa 100644
--- a/app/src/main/java/xyz/quaver/pupil/Pupil.kt
+++ b/app/src/main/java/xyz/quaver/pupil/Pupil.kt
@@ -83,6 +83,10 @@ class Pupil : MultiDexApplication() {
histories.clear()
histories.addAll(it)
}
+ favorites.reversed().let {
+ favorites.clear()
+ favorites.addAll(it)
+ }
}
preference.edit().putBoolean("old_history", false).apply()
}
diff --git a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt
index 8bf8011f..b5810214 100644
--- a/app/src/main/java/xyz/quaver/pupil/types/Tags.kt
+++ b/app/src/main/java/xyz/quaver/pupil/types/Tags.kt
@@ -67,7 +67,7 @@ data class Tag(val area: String?, val tag: String, val isNegative: Boolean = fal
}
}
-class Tags(tag: List?) : ArrayList() {
+class Tags(val tags: MutableSet = mutableSetOf()) : MutableSet by tags {
companion object {
fun parse(tags: String) : Tags {
@@ -77,20 +77,13 @@ class Tags(tag: List?) : ArrayList() {
Tag.parse(it)
else
null
- }
+ }.filterNotNull().toMutableSet()
)
}
}
- init {
- tag?.forEach {
- if (it != null)
- add(it)
- }
- }
-
fun contains(element: String): Boolean {
- forEach {
+ tags.forEach {
if (it.toString() == element)
return true
}
@@ -99,23 +92,25 @@ class Tags(tag: List?) : ArrayList() {
}
fun add(element: String): Boolean {
- return super.add(Tag.parse(element))
+ return tags.add(Tag.parse(element))
}
fun remove(element: String) {
- filter { it.toString() == element }.forEach {
- remove(it)
+ tags.filter { it.toString() == element }.forEach {
+ tags.remove(it)
}
}
fun removeByArea(area: String, isNegative: Boolean? = null) {
- filter { it.area == area && (if(isNegative == null) true else (it.isNegative == isNegative)) }.forEach {
- remove(it)
+ tags.filter { it.area == area && (if(isNegative == null) true else (it.isNegative == isNegative)) }.forEach {
+ tags.remove(it)
}
}
override fun toString(): String {
- return joinToString(" ") { it.toString() }
+ return tags.joinToString(" ") { it.toString() }
}
+
+
}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
index e62d6873..f2001f6d 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/MainActivity.kt
@@ -62,7 +62,6 @@ import xyz.quaver.hitomi.getSuggestionsForQuery
import xyz.quaver.pupil.Pupil
import xyz.quaver.pupil.R
import xyz.quaver.pupil.adapters.GalleryBlockAdapter
-import xyz.quaver.pupil.types.Tag
import xyz.quaver.pupil.types.TagSuggestion
import xyz.quaver.pupil.types.Tags
import xyz.quaver.pupil.ui.dialog.GalleryDialog
@@ -153,6 +152,18 @@ class MainActivity : AppCompatActivity() {
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)
checkUpdate(this)
@@ -771,7 +782,7 @@ class MainActivity : AppCompatActivity() {
if (!favoritesFile.exists()) {
favoritesFile.createNewFile()
- favoritesFile.writeText(Json.encodeToString(Tags(listOf())))
+ favoritesFile.writeText(Json.encodeToString(Tags()))
}
setOnLeftMenuClickListener(object: FloatingSearchView.OnLeftMenuClickListener {
@@ -833,7 +844,7 @@ class MainActivity : AppCompatActivity() {
clearSuggestions()
if (query.isEmpty() or query.endsWith(' ')) {
- swapSuggestions(Json.decodeFromString(favoritesFile.readText()).map {
+ swapSuggestions(Tags(Json.decodeFromString(favoritesFile.readText())).map {
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
})
@@ -911,7 +922,7 @@ class MainActivity : AppCompatActivity() {
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 {
override fun onFocus() {
if (query.isEmpty() or query.endsWith(' '))
- swapSuggestions(Tags(Json.decodeFromString>(favoritesFile.readText())).map {
+ swapSuggestions(Tags(Json.decodeFromString(favoritesFile.readText())).map {
TagSuggestion(it.tag, -1, "", it.area ?: "tag")
})
}
@@ -1076,7 +1087,7 @@ class MainActivity : AppCompatActivity() {
}
Mode.FAVORITE -> {
when {
- query.isEmpty() -> favorites.toList().also {
+ query.isEmpty() -> favorites.reversed().also {
totalItems = it.size
}
else -> {
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt
index 7d5e7909..f2a43b3b 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/SettingsActivity.kt
@@ -80,7 +80,7 @@ class SettingsActivity : AppCompatActivity() {
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
when(requestCode) {
- R.id.request_lock -> {
+ R.id.request_lock.normalizeID() -> {
if (resultCode == Activity.RESULT_OK) {
supportFragmentManager
.beginTransaction()
@@ -89,7 +89,7 @@ class SettingsActivity : AppCompatActivity() {
.commitAllowingStateLoss()
}
}
- R.id.request_restore -> {
+ R.id.request_restore.normalizeID() -> {
if (resultCode == Activity.RESULT_OK) {
val uri = data?.data ?: return
@@ -103,7 +103,7 @@ class SettingsActivity : AppCompatActivity() {
(application as Pupil).favorites.addAll(Json.decodeFromString>(str).also {
Snackbar.make(
window.decorView,
- getString(R.string.settings_restore_successful, it.size),
+ getString(R.string.settings_restore_success, it.size),
Snackbar.LENGTH_LONG
).show()
})
@@ -116,7 +116,7 @@ class SettingsActivity : AppCompatActivity() {
}
}
}
- R.id.request_download_folder -> {
+ R.id.request_download_folder.normalizeID() -> {
if (resultCode == Activity.RESULT_OK) {
data?.data?.also { uri ->
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) {
val directory = data?.getStringExtra(DirectoryChooserActivity.RESULT_SELECTED_DIR)!!
@@ -163,7 +163,7 @@ class SettingsActivity : AppCompatActivity() {
@SuppressLint("InlinedApi")
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) {
when (requestCode) {
- R.id.request_write_permission_and_saf -> {
+ R.id.request_write_permission_and_saf.normalizeID() -> {
if (grantResults.firstOrNull() == PackageManager.PERMISSION_GRANTED) {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE).apply {
putExtra("android.content.extra.SHOW_ADVANCED", true)
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/LockSettingsFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/LockSettingsFragment.kt
index c4a76ee0..67300e0a 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/fragment/LockSettingsFragment.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/LockSettingsFragment.kt
@@ -31,8 +31,7 @@ import xyz.quaver.pupil.ui.LockActivity
import xyz.quaver.pupil.util.Lock
import xyz.quaver.pupil.util.LockManager
-class LockSettingsFragment :
- PreferenceFragmentCompat() {
+class LockSettingsFragment : PreferenceFragmentCompat() {
override fun onResume() {
super.onResume()
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageFavoritesFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageFavoritesFragment.kt
new file mode 100644
index 00000000..b04222f9
--- /dev/null
+++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/ManageFavoritesFragment.kt
@@ -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 .
+ */
+
+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("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("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
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt b/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt
index ce2198e8..a486f289 100644
--- a/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt
+++ b/app/src/main/java/xyz/quaver/pupil/ui/fragment/SettingsFragment.kt
@@ -181,23 +181,6 @@ class SettingsFragment :
"nomedia" -> {
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" -> {
(context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager).setPrimaryClip(
ClipData.newPlainText("user_id", sharedPreference.getString("user_id", ""))
@@ -349,10 +332,7 @@ class SettingsFragment :
"nomedia" -> {
onPreferenceClickListener = this@SettingsFragment
}
- "backup" -> {
- onPreferenceClickListener = this@SettingsFragment
- }
- "restore" -> {
+ "old_import_galleries" -> {
onPreferenceClickListener = this@SettingsFragment
}
"user_id" -> {
diff --git a/app/src/main/java/xyz/quaver/pupil/util/GalleryList.kt b/app/src/main/java/xyz/quaver/pupil/util/GalleryList.kt
index e5b30991..42e35ffd 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/GalleryList.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/GalleryList.kt
@@ -26,14 +26,15 @@ import java.io.File
class GalleryList(private val file: File, private val list: MutableSet = mutableSetOf()) : MutableSet by list {
init {
+ if (!file.exists()) {
+ file.parentFile?.mkdirs()
+ save()
+ }
load()
}
fun load() {
synchronized(this) {
- if (!file.exists())
- file.parentFile?.mkdirs()
-
list.clear()
list.addAll(
Json.decodeFromString>(file.bufferedReader().use { it.readText() })
diff --git a/app/src/main/java/xyz/quaver/pupil/util/update.kt b/app/src/main/java/xyz/quaver/pupil/util/update.kt
index b2b64c05..02424fb8 100644
--- a/app/src/main/java/xyz/quaver/pupil/util/update.kt
+++ b/app/src/main/java/xyz/quaver/pupil/util/update.kt
@@ -18,42 +18,32 @@
package xyz.quaver.pupil.util
-import android.annotation.SuppressLint
import android.app.DownloadManager
-import android.app.PendingIntent
import android.content.Context
-import android.content.Intent
import android.net.Uri
-import android.util.Base64
+import android.webkit.URLUtil
import androidx.appcompat.app.AlertDialog
-import androidx.core.app.NotificationCompat
-import androidx.core.app.NotificationManagerCompat
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.json.*
import okhttp3.*
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.R
-import xyz.quaver.pupil.util.download.Cache
-import xyz.quaver.pupil.util.download.Metadata
import java.io.File
import java.io.IOException
import java.net.URL
import java.util.*
-import java.util.concurrent.TimeUnit
fun getReleases(url: String) : JsonArray {
return try {
URL(url).readText().let {
- Json.decodeFromString(it)
+ Json.parseToJsonElement(it).jsonArray
}
} catch (e: Exception) {
JsonArray(emptyList())
@@ -184,4 +174,29 @@ fun checkUpdate(context: Context, force: Boolean = false) {
dialog.show()
}
}
+}
+
+fun restore(favorites: GalleryList, url: String, onFailure: ((Exception) -> Unit)? = null, onSuccess: ((List) -> 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>(response.body().use { it?.string() } ?: "[]").let {
+ favorites.addAll(it)
+ onSuccess?.invoke(it)
+ }
+ }
+ })
}
\ No newline at end of file
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 01e3d164..fb8e76bd 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -54,7 +54,7 @@
ダウンロード削除
ダウンロードしたギャラリーを全て削除します。\n実行しますか?
ミラーサーバからイメージをロード
- お気に入り
+ ブックマーク
ギャラリー番号で見る
エラーが発生しました
ストレージ
@@ -91,11 +91,11 @@
イメージを隠す
削除
ダウンロード
- お気に入りバックアップ
- お気に入り復元
- バックアップファイルを作成しました
+ ブックマークバックアップ
+ ブックマーク復元
+ バックアップファイルを作成しました
復元に失敗しました
- %1$d項目を復元しました
+ %1$d項目を復元しました
ダウンロード場所
内部ストレージ
外部SDカード
@@ -137,4 +137,7 @@
自動スクロール
全てのギャラリーを対象に検索
綴じ方向を左にする
+ ブックマーク管理
+ エラーが発生しました
+ バックアップ共有
\ No newline at end of file
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index cdd94270..dcd8e9da 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -91,9 +91,9 @@
다운로드
즐겨찾기 백업
즐겨찾기 복원
- 백업 파일을 생성하였습니다
+ 백업 파일을 생성하였습니다
복원에 실패했습니다
- %1$d개 항목을 복원했습니다
+ %1$d개 항목을 복원했습니다
다운로드 위치
내부 저장공간
외부 SD카드
@@ -137,4 +137,7 @@
자동 스크롤
모든 갤러리 검색
좌측으로 페이지 넘기기
+ 즐겨찾기 관리
+ 업로드 실패
+ 백업 공유
\ No newline at end of file
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
index fe6b3c07..5a1d51a9 100644
--- a/app/src/main/res/values/ids.xml
+++ b/app/src/main/res/values/ids.xml
@@ -5,8 +5,6 @@
-
-
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index d8b5fd24..4c97916b 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -9,6 +9,8 @@
mailto:pupil.hentai@gmail.com
https://discord.gg/Stj4b5v
+ http://ix.io/
+
Settings
Thumbnail
@@ -154,15 +156,21 @@
Dark mode
Protect yourself against light attacks!
Hide image from gallery
- Backup favorites
- Backup file created
- Restore favorites
- Restore failed
- %1$d entries restored
Import old galleries
User ID
User ID is copied to clipboard
+
+
+ Manage favorites
+ Backup favorites
+ Upload Failed
+ Share Backup
+ Backup file created
+ Restore favorites
+ Restore failed
+ %1$d entries restored
+
None
diff --git a/app/src/main/res/xml/lock_preferences.xml b/app/src/main/res/xml/lock_preferences.xml
index f17e5c01..8dce037d 100644
--- a/app/src/main/res/xml/lock_preferences.xml
+++ b/app/src/main/res/xml/lock_preferences.xml
@@ -1,5 +1,5 @@
-
-
\ No newline at end of file
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/manage_favorites_preferences.xml b/app/src/main/res/xml/manage_favorites_preferences.xml
new file mode 100644
index 00000000..9ee911c0
--- /dev/null
+++ b/app/src/main/res/xml/manage_favorites_preferences.xml
@@ -0,0 +1,30 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/network_security_config.xml b/app/src/main/res/xml/network_security_config.xml
new file mode 100644
index 00000000..d1f163b0
--- /dev/null
+++ b/app/src/main/res/xml/network_security_config.xml
@@ -0,0 +1,25 @@
+
+
+
+
+
+ ix.io
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml
index 63f01b71..966fbe1b 100644
--- a/app/src/main/res/xml/root_preferences.xml
+++ b/app/src/main/res/xml/root_preferences.xml
@@ -1,5 +1,5 @@
-
-
-
+ app:fragment="xyz.quaver.pupil.ui.fragment.ManageFavoritesFragment"
+ app:title="@string/settings_manage_favorites"/>
-
\ No newline at end of file
+
\ No newline at end of file