Implement logic to export messages to json

This commit is contained in:
Moez Bhatti
2018-08-23 17:47:17 -04:00
parent adb6d50cad
commit cb3b1e47a1
13 changed files with 106 additions and 36 deletions
@@ -21,36 +21,51 @@ package com.moez.QKSMS.repository
import android.animation.ObjectAnimator
import android.os.Environment
import androidx.core.animation.addListener
import com.moez.QKSMS.model.Backup
import com.moez.QKSMS.model.BackupFile
import com.moez.QKSMS.model.Message
import com.moez.QKSMS.util.QkFileObserver
import com.squareup.moshi.Moshi
import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers
import io.reactivex.schedulers.Schedulers
import io.reactivex.subjects.BehaviorSubject
import io.reactivex.subjects.Subject
import io.realm.Realm
import java.io.File
import java.io.FileOutputStream
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import javax.inject.Inject
import javax.inject.Singleton
@Singleton
class BackupRepositoryImpl @Inject constructor() : BackupRepository {
class BackupRepositoryImpl @Inject constructor(
private val moshi: Moshi
) : BackupRepository {
companion object {
private val BACKUP_DIRECTORY = Environment.getExternalStorageDirectory().toString() + "/QKSMS/Backups"
}
data class Backup(
val messages: List<BackupMessage>
)
data class BackupMessage(
val protocol: Int,
val address: String,
val date: Long,
val type: Int,
val type: String,
val body: String,
val service_center: String?,
val serviceCenter: String?,
val read: Boolean,
val status: Int,
val locked: Boolean,
val date_sent: Long)
val dateSent: Long)
// Subjects to emit our progress events to
private val backupProgress: Subject<BackupRepository.Progress> = BehaviorSubject.createDefault(BackupRepository.Progress.Idle())
private val restoreProgress: Subject<BackupRepository.Progress> = BehaviorSubject.createDefault(BackupRepository.Progress.Idle())
@@ -58,34 +73,75 @@ class BackupRepositoryImpl @Inject constructor() : BackupRepository {
// If a backup or restore is already running, don't do anything
if (isBackupOrRestoreRunning()) return
backupProgress.onNext(BackupRepository.Progress.Running(0))
// Map all the messages into our object we'll use for the Json mapping
val backupMessages = Realm.getDefaultInstance().use { realm ->
// Get the messages from realm
val messages = realm.where(Message::class.java).findAll().createSnapshot()
val messageCount = messages.size
Thread.sleep(1000)
// Map the messages to the new format
val startTime = System.currentTimeMillis()
messages.mapIndexed { index, message ->
val progress = index.toDouble() / messageCount * 100
val elapsed = System.currentTimeMillis() - startTime
val remaining = when {
index > 100 -> TimeUnit.MILLISECONDS.toSeconds((elapsed.toDouble() / index * (messageCount - index)).toLong())
else -> 0
}
val animator = ObjectAnimator.ofInt(0, 100)
animator.duration = 3000
animator.addUpdateListener {
val remaining = TimeUnit.MILLISECONDS.toSeconds(it.duration - it.currentPlayTime) + 1
backupProgress.onNext(BackupRepository.Progress.Running(it.animatedValue as Int, "$remaining seconds remaining"))
// Update the progress
backupProgress.onNext(BackupRepository.Progress.Running(progress.toInt(), "$remaining seconds remaining"))
messageToBackupMessage(message)
}
}
animator.addListener(onEnd = {
backupProgress.onNext(BackupRepository.Progress.Idle())
})
AndroidSchedulers.mainThread().scheduleDirect { animator.start() }
// Update the status, and set the progress to be indeterminate since we can no longer calculate progress
backupProgress.onNext(BackupRepository.Progress.Running(0, "Saving..."))
// Convert the data to json
val adapter = moshi.adapter(Backup::class.java).indent("\t")
val json = adapter.toJson(Backup(backupMessages)).toByteArray()
try {
// Create the directory and file
val dir = File(BACKUP_DIRECTORY).apply { mkdirs() }
val file = File(dir, "backup-${SimpleDateFormat("yyyyMMddHHmmss", Locale.getDefault()).format(System.currentTimeMillis())}.json")
// Write the log to the file
FileOutputStream(file, true).use { fileOutputStream -> fileOutputStream.write(json) }
} catch (e: Exception) {
}
backupProgress.onNext(BackupRepository.Progress.Idle())
}
private fun messageToBackupMessage(message: Message): BackupMessage = BackupMessage(
protocol = 0,
address = message.address,
date = message.date,
type = message.type,
body = message.body,
serviceCenter = null,
read = message.read,
status = message.deliveryStatus,
locked = message.locked,
dateSent = message.dateSent)
override fun getBackupProgress(): Observable<BackupRepository.Progress> = backupProgress
override fun getBackups(): Observable<List<Backup>> = QkFileObserver(BACKUP_DIRECTORY)
.observable
.map { path -> File(path).listFiles() }
override fun getBackups(): Observable<List<BackupFile>> = QkFileObserver(BACKUP_DIRECTORY).observable
.map { File(BACKUP_DIRECTORY).listFiles() ?: arrayOf() }
.subscribeOn(Schedulers.io())
.observeOn(Schedulers.computation())
.map { files ->
files.map { file ->
val date = file.lastModified()
val messages = 0
val size = file.length()
Backup(date, listOf(), size)
BackupFile(date, messages, size)
}
}
.map { files -> files.sortedByDescending { file -> file.date } }
override fun performRestore() {
// If a backup or restore is already running, don't do anything
@@ -22,7 +22,7 @@ import android.os.FileObserver
import io.reactivex.Observable
import io.reactivex.subjects.BehaviorSubject
class QkFileObserver(path: String) : FileObserver(path) {
class QkFileObserver(private val path: String) : FileObserver(path) {
private val subject = BehaviorSubject.createDefault<String>(path).toSerialized()