codebase for unified backup

This commit is contained in:
Bnyro 2022-09-18 12:41:35 +02:00
parent d385fea09d
commit 26ec53f654
8 changed files with 196 additions and 0 deletions

View File

@ -0,0 +1,38 @@
package com.github.libretube.adapters
import android.view.LayoutInflater
import android.view.ViewGroup
import androidx.recyclerview.widget.RecyclerView
import com.github.libretube.databinding.BackupRowBinding
class BackupOptionsAdapter(
private val options: List<Int>,
private val onChange: (position: Int, isChecked: Boolean) -> Unit
) : RecyclerView.Adapter<BackupOptionsViewHolder>() {
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): BackupOptionsViewHolder {
val binding = BackupRowBinding.inflate(
LayoutInflater.from(parent.context),
parent,
false
)
return BackupOptionsViewHolder(binding)
}
override fun getItemCount(): Int {
return options.size
}
override fun onBindViewHolder(holder: BackupOptionsViewHolder, position: Int) {
holder.binding.apply {
title.text = root.context?.getString(options[position])
switchWidget.setOnCheckedChangeListener { _, isChecked ->
onChange.invoke(position, isChecked)
}
}
}
}
class BackupOptionsViewHolder(
val binding: BackupRowBinding
) : RecyclerView.ViewHolder(binding.root)

View File

@ -0,0 +1,49 @@
package com.github.libretube.dialogs
import android.app.Dialog
import android.os.Bundle
import androidx.fragment.app.DialogFragment
import androidx.recyclerview.widget.LinearLayoutManager
import com.github.libretube.R
import com.github.libretube.adapters.BackupOptionsAdapter
import com.github.libretube.databinding.DialogBackupBinding
import com.github.libretube.db.DatabaseHolder
import com.github.libretube.obj.BackupFile
import com.google.android.material.dialog.MaterialAlertDialogBuilder
class BackupDialog() : DialogFragment() {
private lateinit var binding: DialogBackupBinding
override fun onCreateDialog(savedInstanceState: Bundle?): Dialog {
val backupOptions = listOf(
R.string.watch_history,
R.string.watch_positions,
R.string.search_history,
R.string.local_subscriptions,
R.string.customInstance
)
val selected = mutableListOf(false, false, false, false, false)
val backupFile = BackupFile()
binding = DialogBackupBinding.inflate(layoutInflater)
binding.backupOptionsRecycler.layoutManager = LinearLayoutManager(context)
binding.backupOptionsRecycler.adapter = BackupOptionsAdapter(backupOptions) { position, isChecked ->
selected[position] = isChecked
}
return MaterialAlertDialogBuilder(requireContext())
.setTitle(R.string.backup)
.setView(binding.root)
.setNegativeButton(R.string.cancel, null)
.setPositiveButton(R.string.backup) { _, _ ->
if (selected[0]) backupFile.watchHistory = DatabaseHolder.db.watchHistoryDao().getAll()
if (selected[1]) backupFile.watchPositions = DatabaseHolder.db.watchPositionDao().getAll()
if (selected[2]) backupFile.searchHistory = DatabaseHolder.db.searchHistoryDao().getAll()
if (selected[3]) backupFile.localSubscriptions = DatabaseHolder.db.localSubscriptionDao().getAll()
if (selected[4]) backupFile.customInstances = DatabaseHolder.db.customInstanceDao().getAll()
}
.create()
}
}

View File

@ -0,0 +1,5 @@
package com.github.libretube.extensions
fun query(block: () -> Unit) {
Thread(block).start()
}

View File

@ -0,0 +1,15 @@
package com.github.libretube.obj
import com.github.libretube.db.obj.CustomInstance
import com.github.libretube.db.obj.LocalSubscription
import com.github.libretube.db.obj.SearchHistoryItem
import com.github.libretube.db.obj.WatchHistoryItem
import com.github.libretube.db.obj.WatchPosition
data class BackupFile(
var watchHistory: List<WatchHistoryItem>? = null,
var watchPositions: List<WatchPosition>? = null,
var searchHistory: List<SearchHistoryItem>? = null,
var localSubscriptions: List<LocalSubscription>? = null,
var customInstances: List<CustomInstance>? = null
)

View File

@ -4,6 +4,10 @@ import android.content.Context
import android.net.Uri import android.net.Uri
import androidx.core.content.edit import androidx.core.content.edit
import androidx.preference.PreferenceManager import androidx.preference.PreferenceManager
import com.fasterxml.jackson.databind.ObjectMapper
import com.github.libretube.db.DatabaseHolder
import com.github.libretube.extensions.query
import com.github.libretube.obj.BackupFile
import java.io.FileInputStream import java.io.FileInputStream
import java.io.FileOutputStream import java.io.FileOutputStream
import java.io.ObjectInputStream import java.io.ObjectInputStream
@ -64,4 +68,54 @@ class BackupHelper(private val context: Context) {
e.printStackTrace() e.printStackTrace()
} }
} }
/**
* Backup the database
*/
fun advancedBackup(uri: Uri?, backupFile: BackupFile) {
if (uri == null) return
try {
context.contentResolver.openFileDescriptor(uri, "w")?.use {
FileOutputStream(it.fileDescriptor).use { fileOutputStream ->
fileOutputStream.write(
ObjectMapper().writeValueAsBytes(backupFile)
)
}
}
} catch (e: Exception) {
e.printStackTrace()
}
}
/**
* Restore a database backup
*/
fun restoreAdvancedBackup(uri: Uri?) {
if (uri == null) return
val mapper = ObjectMapper()
val json = context.contentResolver.openInputStream(uri)?.use {
it.bufferedReader().use { reader -> reader.readText() }
}.orEmpty()
val backupFile = mapper.readValue(json, BackupFile::class.java)
query {
DatabaseHolder.db.watchHistoryDao().insertAll(
*backupFile.watchHistory?.toTypedArray().orEmpty()
)
DatabaseHolder.db.searchHistoryDao().insertAll(
*backupFile.searchHistory?.toTypedArray().orEmpty()
)
DatabaseHolder.db.watchPositionDao().insertAll(
*backupFile.watchPositions?.toTypedArray().orEmpty()
)
DatabaseHolder.db.localSubscriptionDao().insertAll(
*backupFile.localSubscriptions?.toTypedArray().orEmpty()
)
DatabaseHolder.db.customInstanceDao().insertAll(
*backupFile.customInstances?.toTypedArray().orEmpty()
)
}
}
} }

View File

@ -0,0 +1,22 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:orientation="horizontal"
android:paddingHorizontal="10dp"
android:paddingVertical="5dp">
<TextView
android:id="@+id/title"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:layout_marginEnd="5dp"
android:layout_weight="1"
android:textSize="16sp" />
<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/switchWidget"
android:layout_width="wrap_content"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@ -0,0 +1,11 @@
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="wrap_content">
<androidx.recyclerview.widget.RecyclerView
android:id="@+id/backupOptionsRecycler"
android:layout_width="match_parent"
android:layout_height="wrap_content" />
</LinearLayout>

View File

@ -324,6 +324,8 @@
<string name="skip_segment">Skip segment</string> <string name="skip_segment">Skip segment</string>
<string name="sb_skip_manual">Skip manually</string> <string name="sb_skip_manual">Skip manually</string>
<string name="sb_skip_manual_summary">Don\'t skip segments automatically, always prompt before.</string> <string name="sb_skip_manual_summary">Don\'t skip segments automatically, always prompt before.</string>
<string name="local_subscriptions">Local subscriptions</string>
<string name="preferences">Preferences</string>
<!-- Notification channel strings --> <!-- Notification channel strings -->
<string name="download_channel_name">Download Service</string> <string name="download_channel_name">Download Service</string>