diff --git a/app/src/main/java/io/legado/app/help/storage/Backup.kt b/app/src/main/java/io/legado/app/help/storage/Backup.kt index 921910f34..8c4fa8fc2 100644 --- a/app/src/main/java/io/legado/app/help/storage/Backup.kt +++ b/app/src/main/java/io/legado/app/help/storage/Backup.kt @@ -101,9 +101,8 @@ object Backup { val file = File(backupPath + File.separator + fileName) if (file.exists()) { treeDoc.findFile(fileName)?.delete() - treeDoc.createFile("", fileName)?.let { - DocumentUtils.writeBytes(context, file.readBytes(), it.uri) - } + treeDoc.createFile("", fileName) + ?.writeBytes(context, file.readBytes()) } } } diff --git a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt index 7329705e9..91618a704 100644 --- a/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt +++ b/app/src/main/java/io/legado/app/help/storage/ImportOldData.kt @@ -115,13 +115,13 @@ object ImportOldData { } } - fun importOldBookshelf(json: String): Int { + private fun importOldBookshelf(json: String): Int { val books = OldBook.toNewBook(json) App.db.bookDao().insert(*books.toTypedArray()) return books.size } - fun importOldSource(json: String): Int { + private fun importOldSource(json: String): Int { val bookSources = mutableListOf() val items: List> = Restore.jsonPath.parse(json).read("$") for (item in items) { diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt index c94b59d7c..69f8133f7 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceActivity.kt @@ -9,6 +9,7 @@ import android.view.MenuItem import android.view.SubMenu import androidx.appcompat.widget.PopupMenu import androidx.appcompat.widget.SearchView +import androidx.documentfile.provider.DocumentFile import androidx.lifecycle.LiveData import androidx.lifecycle.Observer 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.ui.book.source.edit.BookSourceEditActivity 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.widget.SelectActionBar 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.startActivityForResult import org.jetbrains.anko.toast +import java.io.File import java.io.FileNotFoundException class BookSourceActivity : VMBaseActivity(R.layout.activity_book_source), @@ -51,6 +54,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity private val importRecordKey = "bookSourceRecordKey" private val qrRequestCode = 101 private val importSource = 132 + private val exportRequestCode = 65 private lateinit var adapter: BookSourceAdapter private var bookSourceLiveDate: LiveData>? = null private var groups = linkedSetOf() @@ -187,7 +191,7 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity R.id.menu_disable_selection -> viewModel.disableSelection(adapter.getSelection()) R.id.menu_enable_explore -> viewModel.enableSelectExplore(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()) } return true @@ -267,15 +271,6 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity 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 { newText?.let { initLiveDataBookSource(it) @@ -307,6 +302,21 @@ class BookSourceActivity : VMBaseActivity(R.layout.activity 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?) { super.onActivityResult(requestCode, resultCode, data) when (requestCode) { @@ -344,6 +354,19 @@ class BookSourceActivity : VMBaseActivity(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)) + } + } + } + } } } diff --git a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt index ec5c50020..85e3239bc 100644 --- a/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt +++ b/app/src/main/java/io/legado/app/ui/book/source/manage/BookSourceViewModel.kt @@ -2,6 +2,7 @@ package io.legado.app.ui.book.source.manage import android.app.Application import android.text.TextUtils +import androidx.documentfile.provider.DocumentFile import com.jayway.jsonpath.JsonPath import io.legado.app.App import io.legado.app.base.BaseViewModel @@ -89,12 +90,24 @@ class BookSourceViewModel(application: Application) : BaseViewModel(application) } } - fun exportSelection(sources: LinkedHashSet) { + fun exportSelection(sources: LinkedHashSet, file: File) { execute { val json = GSON.toJson(sources) - val file = - FileUtils.createFileIfNotExist(Backup.exportPath + File.separator + "exportBookSource.json") - file.writeText(json) + FileUtils.createFileIfNotExist(file, "exportBookSource.json") + .writeText(json) + }.onSuccess { + context.toast("成功导出至\n${Backup.exportPath}") + }.onError { + context.toast("导出失败\n${it.localizedMessage}") + } + } + + fun exportSelection(sources: LinkedHashSet, doc: DocumentFile) { + execute { + val json = GSON.toJson(sources) + doc.findFile("exportBookSource.json")?.delete() + doc.createFile("", "exportBookSource.json") + ?.writeText(context, json) }.onSuccess { context.toast("成功导出至\n${Backup.exportPath}") }.onError { diff --git a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt index f314b5f1b..7f094ebc4 100644 --- a/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt +++ b/app/src/main/java/io/legado/app/ui/config/BackupRestoreUi.kt @@ -17,8 +17,7 @@ import io.legado.app.help.storage.Backup import io.legado.app.help.storage.ImportOldData import io.legado.app.help.storage.Restore import io.legado.app.help.storage.WebDavHelp -import io.legado.app.lib.dialogs.alert -import io.legado.app.ui.filechooser.FileChooserDialog +import io.legado.app.ui.filechooser.FilePicker import io.legado.app.utils.getPrefString import io.legado.app.utils.isContentPath import io.legado.app.utils.toast @@ -79,31 +78,9 @@ object BackupRestoreUi { } fun selectBackupFolder(fragment: Fragment, requestCode: Int = selectFolderRequestCode) { - fragment.alert { - titleResource = R.string.select_folder - 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() + FilePicker.selectFolder(fragment, requestCode) { + backupUsePermission(fragment, requestCode = requestCode) + } } fun restore(fragment: Fragment) { @@ -148,65 +125,15 @@ object BackupRestoreUi { } private fun selectRestoreFolder(fragment: Fragment) { - fragment.alert { - titleResource = R.string.select_folder - 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() + FilePicker.selectFolder(fragment, restoreSelectRequestCode) { + restoreUsePermission(fragment) + } } fun importOldData(fragment: Fragment) { - fragment.alert { - titleResource = R.string.select_folder - 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() + FilePicker.selectFolder(fragment, oldDataRequestCode) { + importOldUsePermission(fragment) + } } private fun importOldUsePermission(fragment: Fragment) { diff --git a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt index 31c3d709f..e8ea335c0 100644 --- a/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt +++ b/app/src/main/java/io/legado/app/ui/config/OtherConfigFragment.kt @@ -16,12 +16,10 @@ import io.legado.app.constant.EventBus import io.legado.app.constant.PreferKey import io.legado.app.help.AppConfig 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.receiver.SharedReceiverActivity 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.utils.* @@ -124,38 +122,9 @@ class OtherConfigFragment : PreferenceFragmentCompat(), } private fun selectDownloadPath() { - alert { - titleResource = R.string.select_folder - items(resources.getStringArray(R.array.select_folder).toList()) { _, i -> - when (i) { - 0 -> { - 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() + FilePicker.selectFolder(this, requestCodeDownloadPath) { + removePref(PreferKey.downloadPath) + } } override fun onFilePicked(requestCode: Int, currentPath: String) { diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt new file mode 100644 index 000000000..0f383b49e --- /dev/null +++ b/app/src/main/java/io/legado/app/ui/filechooser/FilePicker.kt @@ -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() + } + +} \ No newline at end of file diff --git a/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt b/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt index 4ade6da7f..0fbaafe33 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/adapter/FileAdapter.kt @@ -6,9 +6,9 @@ import android.graphics.drawable.Drawable import io.legado.app.R import io.legado.app.base.adapter.ItemViewHolder 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.utils.ConvertUtils +import io.legado.app.ui.filechooser.utils.FilePickerIcon import io.legado.app.ui.filechooser.utils.FileUtils import kotlinx.android.synthetic.main.item_path_filepicker.view.* import org.jetbrains.anko.sdk27.listeners.onClick diff --git a/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt b/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt index 3d2323495..86ccf67c0 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt +++ b/app/src/main/java/io/legado/app/ui/filechooser/adapter/PathAdapter.kt @@ -5,8 +5,8 @@ import android.os.Environment import io.legado.app.R import io.legado.app.base.adapter.ItemViewHolder 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.FilePickerIcon import kotlinx.android.synthetic.main.item_path_filepicker.view.* import org.jetbrains.anko.sdk27.listeners.onClick import java.util.* diff --git a/app/src/main/java/io/legado/app/ui/filechooser/FilePickerIcon.java b/app/src/main/java/io/legado/app/ui/filechooser/utils/FilePickerIcon.java similarity index 99% rename from app/src/main/java/io/legado/app/ui/filechooser/FilePickerIcon.java rename to app/src/main/java/io/legado/app/ui/filechooser/utils/FilePickerIcon.java index 4ab481938..6732626e4 100644 --- a/app/src/main/java/io/legado/app/ui/filechooser/FilePickerIcon.java +++ b/app/src/main/java/io/legado/app/ui/filechooser/utils/FilePickerIcon.java @@ -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 diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 3f54ea194..7903cadfb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -488,6 +488,7 @@ 三字符缩进 四字符缩进 选择文件夹 + 选择文件 没有发现,可以在书源里添加。 恢复默认 自定义缓存路径需要存储权限