mirror of
https://github.com/mihonapp/mihon.git
synced 2025-11-12 12:08:56 +01:00
Support backups
This commit is contained in:
@@ -0,0 +1,133 @@
|
||||
package eu.kanade.tachiyomi.ui.backup
|
||||
|
||||
import android.app.Activity
|
||||
import android.app.Dialog
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.os.Bundle
|
||||
import android.view.LayoutInflater
|
||||
import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import com.afollestad.materialdialogs.MaterialDialog
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.base.fragment.BaseRxFragment
|
||||
import eu.kanade.tachiyomi.util.toast
|
||||
import kotlinx.android.synthetic.main.fragment_backup.*
|
||||
import nucleus.factory.RequiresPresenter
|
||||
import java.io.File
|
||||
import java.text.SimpleDateFormat
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Fragment to create and restore backups of the application's data.
|
||||
* Uses R.layout.fragment_backup.
|
||||
*/
|
||||
@RequiresPresenter(BackupPresenter::class)
|
||||
class BackupFragment : BaseRxFragment<BackupPresenter>() {
|
||||
|
||||
private var backupDialog: Dialog? = null
|
||||
private var restoreDialog: Dialog? = null
|
||||
|
||||
override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedState: Bundle?): View {
|
||||
return inflater.inflate(R.layout.fragment_backup, container, false)
|
||||
}
|
||||
|
||||
override fun onViewCreated(view: View?, savedState: Bundle?) {
|
||||
backup_button.setOnClickListener {
|
||||
val today = SimpleDateFormat("yyyy-MM-dd").format(Date())
|
||||
val file = File(activity.externalCacheDir, "tachiyomi-$today.json")
|
||||
presenter.createBackup(file)
|
||||
|
||||
backupDialog = MaterialDialog.Builder(activity)
|
||||
.content(R.string.backup_please_wait)
|
||||
.progress(true, 0)
|
||||
.show()
|
||||
}
|
||||
|
||||
restore_button.setOnClickListener {
|
||||
val intent = Intent(Intent.ACTION_GET_CONTENT)
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE)
|
||||
intent.type = "application/octet-stream"
|
||||
val chooser = Intent.createChooser(intent, getString(R.string.file_select_cover))
|
||||
startActivityForResult(chooser, REQUEST_BACKUP_OPEN)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the presenter when the backup is completed.
|
||||
*/
|
||||
fun onBackupCompleted() {
|
||||
dismissBackupDialog()
|
||||
val intent = Intent(Intent.ACTION_SEND)
|
||||
intent.type = "text/plain"
|
||||
intent.putExtra(Intent.EXTRA_STREAM, Uri.parse("file://" + presenter.backupFile))
|
||||
startActivity(Intent.createChooser(intent, ""))
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the presenter when the restore is completed.
|
||||
*/
|
||||
fun onRestoreCompleted() {
|
||||
dismissRestoreDialog()
|
||||
context.toast(R.string.backup_completed)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the presenter when there's an error doing the backup.
|
||||
* @param error the exception thrown.
|
||||
*/
|
||||
fun onBackupError(error: Throwable) {
|
||||
dismissBackupDialog()
|
||||
context.toast(error.message)
|
||||
}
|
||||
|
||||
/**
|
||||
* Called from the presenter when there's an error restoring the backup.
|
||||
* @param error the exception thrown.
|
||||
*/
|
||||
fun onRestoreError(error: Throwable) {
|
||||
dismissRestoreDialog()
|
||||
context.toast(error.message)
|
||||
}
|
||||
|
||||
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
|
||||
if (data != null && resultCode == Activity.RESULT_OK && requestCode == REQUEST_BACKUP_OPEN) {
|
||||
restoreDialog = MaterialDialog.Builder(activity)
|
||||
.content(R.string.restore_please_wait)
|
||||
.progress(true, 0)
|
||||
.show()
|
||||
|
||||
val stream = context.contentResolver.openInputStream(data.data)
|
||||
presenter.restoreBackup(stream)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses the backup dialog.
|
||||
*/
|
||||
fun dismissBackupDialog() {
|
||||
backupDialog?.let {
|
||||
it.dismiss()
|
||||
backupDialog = null
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Dismisses the restore dialog.
|
||||
*/
|
||||
fun dismissRestoreDialog() {
|
||||
restoreDialog?.let {
|
||||
it.dismiss()
|
||||
restoreDialog = null
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
|
||||
private val REQUEST_BACKUP_OPEN = 102
|
||||
|
||||
fun newInstance(): BackupFragment {
|
||||
return BackupFragment()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,109 @@
|
||||
package eu.kanade.tachiyomi.ui.backup
|
||||
|
||||
import android.os.Bundle
|
||||
import eu.kanade.tachiyomi.data.backup.BackupManager
|
||||
import eu.kanade.tachiyomi.data.database.DatabaseHelper
|
||||
import eu.kanade.tachiyomi.ui.base.presenter.BasePresenter
|
||||
import rx.Observable
|
||||
import rx.android.schedulers.AndroidSchedulers
|
||||
import rx.schedulers.Schedulers
|
||||
import java.io.File
|
||||
import java.io.InputStream
|
||||
import javax.inject.Inject
|
||||
|
||||
/**
|
||||
* Presenter of [BackupFragment].
|
||||
*/
|
||||
class BackupPresenter : BasePresenter<BackupFragment>() {
|
||||
|
||||
/**
|
||||
* Database.
|
||||
*/
|
||||
@Inject lateinit var db: DatabaseHelper
|
||||
|
||||
/**
|
||||
* Backup manager.
|
||||
*/
|
||||
private lateinit var backupManager: BackupManager
|
||||
|
||||
/**
|
||||
* File where the backup is saved.
|
||||
*/
|
||||
var backupFile: File? = null
|
||||
private set
|
||||
|
||||
/**
|
||||
* Stream to restore a backup.
|
||||
*/
|
||||
private var restoreStream: InputStream? = null
|
||||
|
||||
/**
|
||||
* Id of the restartable that creates a backup.
|
||||
*/
|
||||
private val CREATE_BACKUP = 1
|
||||
|
||||
/**
|
||||
* Id of the restartable that restores a backup.
|
||||
*/
|
||||
private val RESTORE_BACKUP = 2
|
||||
|
||||
override fun onCreate(savedState: Bundle?) {
|
||||
super.onCreate(savedState)
|
||||
backupManager = BackupManager(db)
|
||||
|
||||
startableFirst(CREATE_BACKUP,
|
||||
{ getBackupObservable() },
|
||||
{ view, next -> view.onBackupCompleted() },
|
||||
{ view, error -> view.onBackupError(error) })
|
||||
|
||||
startableFirst(RESTORE_BACKUP,
|
||||
{ getRestoreObservable() },
|
||||
{ view, next -> view.onRestoreCompleted() },
|
||||
{ view, error -> view.onRestoreError(error) })
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a backup and saves it to a file.
|
||||
*
|
||||
* @param file the path where the file will be saved.
|
||||
*/
|
||||
fun createBackup(file: File) {
|
||||
if (isUnsubscribed(CREATE_BACKUP)) {
|
||||
backupFile = file
|
||||
start(CREATE_BACKUP)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Restores a backup from a stream.
|
||||
*
|
||||
* @param stream the input stream of the backup file.
|
||||
*/
|
||||
fun restoreBackup(stream: InputStream) {
|
||||
if (isUnsubscribed(RESTORE_BACKUP)) {
|
||||
restoreStream = stream
|
||||
start(RESTORE_BACKUP)
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the observable to save a backup.
|
||||
*/
|
||||
private fun getBackupObservable(): Observable<Boolean> {
|
||||
return Observable.fromCallable {
|
||||
backupManager.backupToFile(backupFile!!)
|
||||
true
|
||||
}.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the observable to restore a backup.
|
||||
*/
|
||||
private fun getRestoreObservable(): Observable<Boolean> {
|
||||
return Observable.fromCallable {
|
||||
backupManager.restoreFromStream(restoreStream!!)
|
||||
true
|
||||
}.subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread())
|
||||
}
|
||||
|
||||
}
|
||||
@@ -9,6 +9,7 @@ import android.support.v4.widget.DrawerLayout
|
||||
import android.view.MenuItem
|
||||
import android.view.View
|
||||
import eu.kanade.tachiyomi.R
|
||||
import eu.kanade.tachiyomi.ui.backup.BackupFragment
|
||||
import eu.kanade.tachiyomi.ui.base.activity.BaseActivity
|
||||
import eu.kanade.tachiyomi.ui.catalogue.CatalogueFragment
|
||||
import eu.kanade.tachiyomi.ui.download.DownloadFragment
|
||||
@@ -80,6 +81,10 @@ class MainActivity : BaseActivity() {
|
||||
item.isChecked = false
|
||||
startActivity(Intent(this, SettingsActivity::class.java))
|
||||
}
|
||||
R.id.nav_drawer_backup -> {
|
||||
setFragment(BackupFragment.newInstance())
|
||||
item.isChecked = true
|
||||
}
|
||||
}
|
||||
drawer.closeDrawer(GravityCompat.START)
|
||||
true
|
||||
|
||||
Reference in New Issue
Block a user