feat: 优化代码

pull/123/head
kunfei 5 years ago
parent 30ddfe4952
commit 82b7e6feb4
  1. 5
      app/src/main/java/io/legado/app/help/storage/Backup.kt
  2. 4
      app/src/main/java/io/legado/app/help/storage/ImportOldData.kt
  3. 43
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt
  4. 21
      app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt
  5. 87
      app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt
  6. 35
      app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt
  7. 183
      app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt
  8. 2
      app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt
  9. 2
      app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt
  10. 2
      app/src/main/java/io/legado/app/ui/filechooser/utils/FilePickerIcon.java
  11. 1
      app/src/main/res/values/strings.xml

@ -101,9 +101,8 @@ object Backup {
val file = File(backupPath + File.separator + fileName) val file = File(backupPath + File.separator + fileName)
if (file.exists()) { if (file.exists()) {
treeDoc.findFile(fileName)?.delete() treeDoc.findFile(fileName)?.delete()
treeDoc.createFile("", fileName)?.let { treeDoc.createFile("", fileName)
DocumentUtils.writeBytes(context, file.readBytes(), it.uri) ?.writeBytes(context, file.readBytes())
}
} }
} }
} }

@ -115,13 +115,13 @@ object ImportOldData {
} }
} }
fun importOldBookshelf(json: String): Int { private fun importOldBookshelf(json: String): Int {
val books = OldBook.toNewBook(json) val books = OldBook.toNewBook(json)
App.db.bookDao().insert(*books.toTypedArray()) App.db.bookDao().insert(*books.toTypedArray())
return books.size return books.size
} }
fun importOldSource(json: String): Int { private fun importOldSource(json: String): Int {
val bookSources = mutableListOf<BookSource>() val bookSources = mutableListOf<BookSource>()
val items: List<Map<String, Any>> = Restore.jsonPath.parse(json).read("$") val items: List<Map<String, Any>> = Restore.jsonPath.parse(json).read("$")
for (item in items) { for (item in items) {

@ -9,6 +9,7 @@ import android.view.MenuItem
import android.view.SubMenu import android.view.SubMenu
import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.PopupMenu
import androidx.appcompat.widget.SearchView import androidx.appcompat.widget.SearchView
import androidx.documentfile.provider.DocumentFile
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.Observer import androidx.lifecycle.Observer
import androidx.recyclerview.widget.DiffUtil import androidx.recyclerview.widget.DiffUtil
@ -28,6 +29,7 @@ import io.legado.app.lib.theme.primaryTextColor
import io.legado.app.service.help.CheckSource import io.legado.app.service.help.CheckSource
import io.legado.app.ui.book.source.edit.BookSourceEditActivity import io.legado.app.ui.book.source.edit.BookSourceEditActivity
import io.legado.app.ui.filechooser.FileChooserDialog import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.filechooser.FilePicker
import io.legado.app.ui.qrcode.QrCodeActivity import io.legado.app.ui.qrcode.QrCodeActivity
import io.legado.app.ui.widget.SelectActionBar import io.legado.app.ui.widget.SelectActionBar
import io.legado.app.ui.widget.recycler.VerticalDivider import io.legado.app.ui.widget.recycler.VerticalDivider
@ -39,6 +41,7 @@ import kotlinx.android.synthetic.main.view_search.*
import org.jetbrains.anko.startActivity import org.jetbrains.anko.startActivity
import org.jetbrains.anko.startActivityForResult import org.jetbrains.anko.startActivityForResult
import org.jetbrains.anko.toast import org.jetbrains.anko.toast
import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity_book_source), class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity_book_source),
@ -51,6 +54,7 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
private val importRecordKey = "bookSourceRecordKey" private val importRecordKey = "bookSourceRecordKey"
private val qrRequestCode = 101 private val qrRequestCode = 101
private val importSource = 132 private val importSource = 132
private val exportRequestCode = 65
private lateinit var adapter: BookSourceAdapter private lateinit var adapter: BookSourceAdapter
private var bookSourceLiveDate: LiveData<List<BookSource>>? = null private var bookSourceLiveDate: LiveData<List<BookSource>>? = null
private var groups = linkedSetOf<String>() private var groups = linkedSetOf<String>()
@ -187,7 +191,7 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
R.id.menu_disable_selection -> viewModel.disableSelection(adapter.getSelection()) R.id.menu_disable_selection -> viewModel.disableSelection(adapter.getSelection())
R.id.menu_enable_explore -> viewModel.enableSelectExplore(adapter.getSelection()) R.id.menu_enable_explore -> viewModel.enableSelectExplore(adapter.getSelection())
R.id.menu_disable_explore -> viewModel.disableSelectExplore(adapter.getSelection()) R.id.menu_disable_explore -> viewModel.disableSelectExplore(adapter.getSelection())
R.id.menu_export_selection -> viewModel.exportSelection(adapter.getSelection()) R.id.menu_export_selection -> FilePicker.selectFolder(this, exportRequestCode)
R.id.menu_check_source -> CheckSource.start(this, adapter.getSelection()) R.id.menu_check_source -> CheckSource.start(this, adapter.getSelection())
} }
return true return true
@ -267,15 +271,6 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
select_action_bar.upCountView(adapter.getSelection().size, adapter.getActualItemCount()) select_action_bar.upCountView(adapter.getSelection().size, adapter.getActualItemCount())
} }
override fun onFilePicked(requestCode: Int, currentPath: String) {
if (requestCode == importSource) {
Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show()
viewModel.importSourceFromFilePath(currentPath) { msg ->
title_bar.snackbar(msg)
}
}
}
override fun onQueryTextChange(newText: String?): Boolean { override fun onQueryTextChange(newText: String?): Boolean {
newText?.let { newText?.let {
initLiveDataBookSource(it) initLiveDataBookSource(it)
@ -307,6 +302,21 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
viewModel.topSource(bookSource) viewModel.topSource(bookSource)
} }
override fun onFilePicked(requestCode: Int, currentPath: String) {
when (requestCode) {
exportRequestCode -> viewModel.exportSelection(
adapter.getSelection(),
File(currentPath)
)
importSource -> {
Snackbar.make(title_bar, R.string.importing, Snackbar.LENGTH_INDEFINITE).show()
viewModel.importSourceFromFilePath(currentPath) { msg ->
title_bar.snackbar(msg)
}
}
}
}
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
super.onActivityResult(requestCode, resultCode, data) super.onActivityResult(requestCode, resultCode, data)
when (requestCode) { when (requestCode) {
@ -344,6 +354,19 @@ class BookSourceActivity : VMBaseActivity<BookSourceViewModel>(R.layout.activity
} }
} }
} }
exportRequestCode -> {
data?.data?.let { uri ->
if (uri.toString().isContentPath()) {
DocumentFile.fromTreeUri(this, uri)?.let {
viewModel.exportSelection(adapter.getSelection(), it)
}
} else {
uri.path?.let {
viewModel.exportSelection(adapter.getSelection(), File(it))
}
}
}
}
} }
} }

@ -2,6 +2,7 @@ package io.legado.app.ui.book.source.manage
import android.app.Application import android.app.Application
import android.text.TextUtils import android.text.TextUtils
import androidx.documentfile.provider.DocumentFile
import com.jayway.jsonpath.JsonPath import com.jayway.jsonpath.JsonPath
import io.legado.app.App import io.legado.app.App
import io.legado.app.base.BaseViewModel import io.legado.app.base.BaseViewModel
@ -89,12 +90,24 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application)
} }
} }
fun exportSelection(sources: LinkedHashSet<BookSource>) { fun exportSelection(sources: LinkedHashSet<BookSource>, file: File) {
execute { execute {
val json = GSON.toJson(sources) val json = GSON.toJson(sources)
val file = FileUtils.createFileIfNotExist(file, "exportBookSource.json")
FileUtils.createFileIfNotExist(Backup.exportPath + File.separator + "exportBookSource.json") .writeText(json)
file.writeText(json) }.onSuccess {
context.toast("成功导出至\n${Backup.exportPath}")
}.onError {
context.toast("导出失败\n${it.localizedMessage}")
}
}
fun exportSelection(sources: LinkedHashSet<BookSource>, doc: DocumentFile) {
execute {
val json = GSON.toJson(sources)
doc.findFile("exportBookSource.json")?.delete()
doc.createFile("", "exportBookSource.json")
?.writeText(context, json)
}.onSuccess { }.onSuccess {
context.toast("成功导出至\n${Backup.exportPath}") context.toast("成功导出至\n${Backup.exportPath}")
}.onError { }.onError {

@ -17,8 +17,7 @@ import io.legado.app.help.storage.Backup
import io.legado.app.help.storage.ImportOldData import io.legado.app.help.storage.ImportOldData
import io.legado.app.help.storage.Restore import io.legado.app.help.storage.Restore
import io.legado.app.help.storage.WebDavHelp import io.legado.app.help.storage.WebDavHelp
import io.legado.app.lib.dialogs.alert import io.legado.app.ui.filechooser.FilePicker
import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.utils.getPrefString import io.legado.app.utils.getPrefString
import io.legado.app.utils.isContentPath import io.legado.app.utils.isContentPath
import io.legado.app.utils.toast import io.legado.app.utils.toast
@ -79,32 +78,10 @@ object BackupRestoreUi {
} }
fun selectBackupFolder(fragment: Fragment, requestCode: Int = selectFolderRequestCode) { fun selectBackupFolder(fragment: Fragment, requestCode: Int = selectFolderRequestCode) {
fragment.alert { FilePicker.selectFolder(fragment, requestCode) {
titleResource = R.string.select_folder backupUsePermission(fragment, requestCode = requestCode)
items(fragment.resources.getStringArray(R.array.select_folder).toList()) { _, index ->
when (index) {
0 -> backupUsePermission(fragment, requestCode = requestCode)
1 -> {
try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
fragment.startActivityForResult(intent, requestCode)
} catch (e: java.lang.Exception) {
e.printStackTrace()
fragment.toast(e.localizedMessage ?: "ERROR")
}
}
2 -> {
FileChooserDialog.show(
fragment.childFragmentManager,
requestCode,
mode = FileChooserDialog.DIRECTORY
)
}
} }
} }
}.show()
}
fun restore(fragment: Fragment) { fun restore(fragment: Fragment) {
Coroutine.async(context = Main) { Coroutine.async(context = Main) {
@ -148,65 +125,15 @@ object BackupRestoreUi {
} }
private fun selectRestoreFolder(fragment: Fragment) { private fun selectRestoreFolder(fragment: Fragment) {
fragment.alert { FilePicker.selectFolder(fragment, restoreSelectRequestCode) {
titleResource = R.string.select_folder restoreUsePermission(fragment)
items(fragment.resources.getStringArray(R.array.select_folder).toList()) { _, index ->
when (index) {
0 -> restoreUsePermission(fragment)
1 -> {
try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
fragment.startActivityForResult(intent, restoreSelectRequestCode)
} catch (e: java.lang.Exception) {
e.printStackTrace()
fragment.toast(e.localizedMessage ?: "ERROR")
}
}
2 -> {
FileChooserDialog.show(
fragment.childFragmentManager,
restoreSelectRequestCode,
mode = FileChooserDialog.DIRECTORY
)
} }
} }
}
}.show()
}
fun importOldData(fragment: Fragment) { fun importOldData(fragment: Fragment) {
fragment.alert { FilePicker.selectFolder(fragment, oldDataRequestCode) {
titleResource = R.string.select_folder importOldUsePermission(fragment)
items(fragment.resources.getStringArray(R.array.select_folder).toList()) { _, index ->
when (index) {
0 -> importOldUsePermission(fragment)
1 -> {
try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
fragment.startActivityForResult(intent, oldDataRequestCode)
} catch (e: java.lang.Exception) {
e.printStackTrace()
fragment.toast(e.localizedMessage ?: "ERROR")
}
}
2 -> {
PermissionsCompat.Builder(fragment)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
.onGranted {
FileChooserDialog.show(
fragment.childFragmentManager,
oldDataRequestCode,
mode = FileChooserDialog.DIRECTORY
)
}
.request()
}
}
} }
}.show()
} }
private fun importOldUsePermission(fragment: Fragment) { private fun importOldUsePermission(fragment: Fragment) {

@ -16,12 +16,10 @@ import io.legado.app.constant.EventBus
import io.legado.app.constant.PreferKey import io.legado.app.constant.PreferKey
import io.legado.app.help.AppConfig import io.legado.app.help.AppConfig
import io.legado.app.help.BookHelp import io.legado.app.help.BookHelp
import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.lib.dialogs.alert
import io.legado.app.lib.theme.ATH import io.legado.app.lib.theme.ATH
import io.legado.app.receiver.SharedReceiverActivity import io.legado.app.receiver.SharedReceiverActivity
import io.legado.app.ui.filechooser.FileChooserDialog import io.legado.app.ui.filechooser.FileChooserDialog
import io.legado.app.ui.filechooser.FilePicker
import io.legado.app.ui.widget.number.NumberPickerDialog import io.legado.app.ui.widget.number.NumberPickerDialog
import io.legado.app.utils.* import io.legado.app.utils.*
@ -124,38 +122,9 @@ class OtherConfigFragment : PreferenceFragmentCompat(),
} }
private fun selectDownloadPath() { private fun selectDownloadPath() {
alert { FilePicker.selectFolder(this, requestCodeDownloadPath) {
titleResource = R.string.select_folder
items(resources.getStringArray(R.array.select_folder).toList()) { _, i ->
when (i) {
0 -> {
removePref(PreferKey.downloadPath) removePref(PreferKey.downloadPath)
} }
1 -> {
try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
startActivityForResult(intent, requestCodeDownloadPath)
} catch (e: Exception) {
e.printStackTrace()
toast(e.localizedMessage ?: "ERROR")
}
}
2 -> PermissionsCompat.Builder(this@OtherConfigFragment)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
.onGranted {
FileChooserDialog.show(
childFragmentManager,
requestCodeDownloadPath,
mode = FileChooserDialog.DIRECTORY,
initPath = BookHelp.downloadPath
)
}
.request()
}
}
}.show()
} }
override fun onFilePicked(requestCode: Int, currentPath: String) { override fun onFilePicked(requestCode: Int, currentPath: String) {

@ -0,0 +1,183 @@
package io.legado.app.ui.filechooser
import android.content.Intent
import androidx.fragment.app.Fragment
import io.legado.app.R
import io.legado.app.base.BaseActivity
import io.legado.app.help.permission.Permissions
import io.legado.app.help.permission.PermissionsCompat
import io.legado.app.lib.dialogs.alert
import io.legado.app.utils.toast
import org.jetbrains.anko.toast
object FilePicker {
fun selectFolder(activity: BaseActivity, requestCode: Int, default: (() -> Unit)? = null) {
activity.alert(titleResource = R.string.select_folder) {
val selectList =
activity.resources.getStringArray(R.array.select_folder).toMutableList()
default ?: let {
selectList.removeAt(0)
}
items(selectList) { _, index ->
when (index) {
0 -> default?.invoke()
1 -> {
try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
activity.startActivityForResult(intent, requestCode)
} catch (e: java.lang.Exception) {
e.printStackTrace()
activity.toast(e.localizedMessage ?: "ERROR")
}
}
2 -> {
PermissionsCompat.Builder(activity)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
.onGranted {
FileChooserDialog.show(
activity.supportFragmentManager,
requestCode,
mode = FileChooserDialog.DIRECTORY
)
}
.request()
}
}
}
}.show()
}
fun selectFolder(fragment: Fragment, requestCode: Int, default: (() -> Unit)? = null) {
fragment.requireContext()
.alert(titleResource = R.string.select_folder) {
val selectList =
fragment.resources.getStringArray(R.array.select_folder).toMutableList()
default ?: let {
selectList.removeAt(0)
}
items(selectList) { _, index ->
when (index) {
0 -> default?.invoke()
1 -> {
try {
val intent = Intent(Intent.ACTION_OPEN_DOCUMENT_TREE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
fragment.startActivityForResult(intent, requestCode)
} catch (e: java.lang.Exception) {
e.printStackTrace()
fragment.toast(e.localizedMessage ?: "ERROR")
}
}
2 -> {
PermissionsCompat.Builder(fragment)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
.onGranted {
FileChooserDialog.show(
fragment.childFragmentManager,
requestCode,
mode = FileChooserDialog.DIRECTORY
)
}
.request()
}
}
}
}.show()
}
fun selectFile(
activity: BaseActivity,
requestCode: Int,
type: String,
default: (() -> Unit)? = null
) {
activity.alert(titleResource = R.string.select_file) {
val selectList =
activity.resources.getStringArray(R.array.select_folder).toMutableList()
default ?: let {
selectList.removeAt(0)
}
items(selectList) { _, index ->
when (index) {
0 -> default?.invoke()
1 -> {
try {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.type = type//设置类型
activity.startActivityForResult(intent, requestCode)
} catch (e: java.lang.Exception) {
e.printStackTrace()
activity.toast(e.localizedMessage ?: "ERROR")
}
}
2 -> {
PermissionsCompat.Builder(activity)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
.onGranted {
FileChooserDialog.show(
activity.supportFragmentManager,
requestCode,
mode = FileChooserDialog.FILE
)
}
.request()
}
}
}
}.show()
}
fun selectFile(
fragment: Fragment,
requestCode: Int,
type: String,
default: (() -> Unit)? = null
) {
fragment.requireContext()
.alert(titleResource = R.string.select_file) {
val selectList =
fragment.resources.getStringArray(R.array.select_folder).toMutableList()
default ?: let {
selectList.removeAt(0)
}
items(selectList) { _, index ->
when (index) {
0 -> default?.invoke()
1 -> {
try {
val intent = Intent(Intent.ACTION_GET_CONTENT)
intent.addCategory(Intent.CATEGORY_OPENABLE)
intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION)
intent.type = type//设置类型
fragment.startActivityForResult(intent, requestCode)
} catch (e: java.lang.Exception) {
e.printStackTrace()
fragment.toast(e.localizedMessage ?: "ERROR")
}
}
2 -> {
PermissionsCompat.Builder(fragment)
.addPermissions(*Permissions.Group.STORAGE)
.rationale(R.string.tip_perm_request_storage)
.onGranted {
FileChooserDialog.show(
fragment.childFragmentManager,
requestCode,
mode = FileChooserDialog.FILE
)
}
.request()
}
}
}
}.show()
}
}

@ -6,9 +6,9 @@ import android.graphics.drawable.Drawable
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.ui.filechooser.FilePickerIcon
import io.legado.app.ui.filechooser.entity.FileItem import io.legado.app.ui.filechooser.entity.FileItem
import io.legado.app.ui.filechooser.utils.ConvertUtils import io.legado.app.ui.filechooser.utils.ConvertUtils
import io.legado.app.ui.filechooser.utils.FilePickerIcon
import io.legado.app.ui.filechooser.utils.FileUtils import io.legado.app.ui.filechooser.utils.FileUtils
import kotlinx.android.synthetic.main.item_path_filepicker.view.* import kotlinx.android.synthetic.main.item_path_filepicker.view.*
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick

@ -5,8 +5,8 @@ import android.os.Environment
import io.legado.app.R import io.legado.app.R
import io.legado.app.base.adapter.ItemViewHolder import io.legado.app.base.adapter.ItemViewHolder
import io.legado.app.base.adapter.SimpleRecyclerAdapter import io.legado.app.base.adapter.SimpleRecyclerAdapter
import io.legado.app.ui.filechooser.FilePickerIcon
import io.legado.app.ui.filechooser.utils.ConvertUtils import io.legado.app.ui.filechooser.utils.ConvertUtils
import io.legado.app.ui.filechooser.utils.FilePickerIcon
import kotlinx.android.synthetic.main.item_path_filepicker.view.* import kotlinx.android.synthetic.main.item_path_filepicker.view.*
import org.jetbrains.anko.sdk27.listeners.onClick import org.jetbrains.anko.sdk27.listeners.onClick
import java.util.* import java.util.*

@ -1,4 +1,4 @@
package io.legado.app.ui.filechooser; package io.legado.app.ui.filechooser.utils;
/** /**
* Generated by https://github.com/gzu-liyujiang/Image2ByteVar * Generated by https://github.com/gzu-liyujiang/Image2ByteVar

@ -488,6 +488,7 @@
<string name="indent_3">三字符缩进</string> <string name="indent_3">三字符缩进</string>
<string name="indent_4">四字符缩进</string> <string name="indent_4">四字符缩进</string>
<string name="select_folder">选择文件夹</string> <string name="select_folder">选择文件夹</string>
<string name="select_file">选择文件</string>
<string name="no_find">没有发现,可以在书源里添加。</string> <string name="no_find">没有发现,可以在书源里添加。</string>
<string name="restore_default">恢复默认</string> <string name="restore_default">恢复默认</string>
<string name="set_download_per">自定义缓存路径需要存储权限</string> <string name="set_download_per">自定义缓存路径需要存储权限</string>

Loading…
Cancel
Save