parent
8b98f85470
commit
5a5702302f
@ -0,0 +1,105 @@
|
||||
package org.thoughtcrime.securesms.database
|
||||
|
||||
import android.content.ContentValues
|
||||
import android.content.Context
|
||||
import android.database.Cursor
|
||||
import android.net.Uri
|
||||
import org.thoughtcrime.securesms.database.helpers.SQLCipherOpenHelper
|
||||
import org.thoughtcrime.securesms.database.model.BackupFileRecord
|
||||
import java.util.*
|
||||
import kotlin.collections.ArrayList
|
||||
|
||||
/**
|
||||
* Keeps track of the backup files saved by the app.
|
||||
* Uses [BackupFileRecord] as an entry data projection.
|
||||
*/
|
||||
class LokiBackupFilesDatabase(context: Context?, databaseHelper: SQLCipherOpenHelper?)
|
||||
: Database(context, databaseHelper) {
|
||||
|
||||
companion object {
|
||||
private const val TABLE_NAME = "backup_files"
|
||||
private const val COLUMN_ID = "_id"
|
||||
private const val COLUMN_URI = "uri"
|
||||
private const val COLUMN_FILE_SIZE = "file_size"
|
||||
private const val COLUMN_TIMESTAMP = "timestamp"
|
||||
|
||||
private val allColumns = arrayOf(COLUMN_ID, COLUMN_URI, COLUMN_FILE_SIZE, COLUMN_TIMESTAMP)
|
||||
|
||||
@JvmStatic
|
||||
val createTableCommand = """
|
||||
CREATE TABLE $TABLE_NAME (
|
||||
$COLUMN_ID INTEGER PRIMARY KEY,
|
||||
$COLUMN_URI TEXT NOT NULL,
|
||||
$COLUMN_FILE_SIZE INTEGER NOT NULL,
|
||||
$COLUMN_TIMESTAMP INTEGER NOT NULL
|
||||
);
|
||||
""".trimIndent()
|
||||
|
||||
private fun mapCursorToRecord(cursor: Cursor): BackupFileRecord {
|
||||
val id = cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_ID))
|
||||
val uriRaw = cursor.getString(cursor.getColumnIndexOrThrow(COLUMN_URI))
|
||||
val fileSize = cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_FILE_SIZE))
|
||||
val timestampRaw = cursor.getLong(cursor.getColumnIndexOrThrow(COLUMN_TIMESTAMP))
|
||||
return BackupFileRecord(id, Uri.parse(uriRaw), fileSize, Date(timestampRaw))
|
||||
}
|
||||
|
||||
private fun mapRecordToValues(record: BackupFileRecord): ContentValues {
|
||||
val contentValues = ContentValues()
|
||||
if (record.id >= 0) { contentValues.put(COLUMN_ID, record.id) }
|
||||
contentValues.put(COLUMN_URI, record.uri.toString())
|
||||
contentValues.put(COLUMN_FILE_SIZE, record.fileSize)
|
||||
contentValues.put(COLUMN_TIMESTAMP, record.timestamp.time)
|
||||
return contentValues
|
||||
}
|
||||
}
|
||||
|
||||
fun getBackupFiles(): List<BackupFileRecord> {
|
||||
databaseHelper.readableDatabase.query(TABLE_NAME, allColumns, null, null, null, null, null).use {
|
||||
val records = ArrayList<BackupFileRecord>()
|
||||
while (it != null && it.moveToFirst()) {
|
||||
val record = mapCursorToRecord(it)
|
||||
records.add(record)
|
||||
}
|
||||
return records
|
||||
}
|
||||
}
|
||||
|
||||
fun insertBackupFile(record: BackupFileRecord): Long {
|
||||
val contentValues = mapRecordToValues(record)
|
||||
return databaseHelper.writableDatabase.insertOrThrow(TABLE_NAME, null, contentValues)
|
||||
}
|
||||
|
||||
fun getLastBackupFileTime(): Date? {
|
||||
// SELECT $COLUMN_TIMESTAMP FROM $TABLE_NAME ORDER BY $COLUMN_TIMESTAMP DESC LIMIT 1
|
||||
databaseHelper.readableDatabase.query(
|
||||
TABLE_NAME,
|
||||
arrayOf(COLUMN_TIMESTAMP),
|
||||
null, null, null, null,
|
||||
"$COLUMN_TIMESTAMP DESC",
|
||||
"1"
|
||||
).use {
|
||||
if (it !== null && it.moveToFirst()) {
|
||||
return Date(it.getLong(0))
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getLastBackupFile(): BackupFileRecord? {
|
||||
// SELECT * FROM $TABLE_NAME ORDER BY $COLUMN_TIMESTAMP DESC LIMIT 1
|
||||
databaseHelper.readableDatabase.query(
|
||||
TABLE_NAME,
|
||||
allColumns,
|
||||
null, null, null, null,
|
||||
"$COLUMN_TIMESTAMP DESC",
|
||||
"1"
|
||||
).use {
|
||||
if (it != null && it.moveToFirst()) {
|
||||
return mapCursorToRecord(it)
|
||||
} else {
|
||||
return null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -0,0 +1,12 @@
|
||||
package org.thoughtcrime.securesms.database.model
|
||||
|
||||
import android.net.Uri
|
||||
import java.util.*
|
||||
|
||||
/**
|
||||
* Represents a record for a backup file in the [org.thoughtcrime.securesms.database.LokiBackupFilesDatabase].
|
||||
*/
|
||||
data class BackupFileRecord(val id: Long, val uri: Uri, val fileSize: Long, val timestamp: Date) {
|
||||
|
||||
constructor(uri: Uri, fileSize: Long, timestamp: Date): this(-1, uri, fileSize, timestamp)
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package org.thoughtcrime.securesms.util
|
||||
|
||||
import android.content.Context
|
||||
import network.loki.messenger.R
|
||||
import org.thoughtcrime.securesms.database.DatabaseFactory
|
||||
import org.thoughtcrime.securesms.database.model.BackupFileRecord
|
||||
import org.whispersystems.libsignal.util.ByteUtil
|
||||
import java.security.SecureRandom
|
||||
import java.util.*
|
||||
|
||||
object BackupUtil {
|
||||
|
||||
@JvmStatic
|
||||
fun getLastBackupTimeString(context: Context, locale: Locale): String {
|
||||
val timestamp = DatabaseFactory.getLokiBackupFilesDatabase(context).getLastBackupFileTime()
|
||||
if (timestamp == null) {
|
||||
return context.getString(R.string.BackupUtil_never)
|
||||
}
|
||||
return DateUtils.getExtendedRelativeTimeSpanString(context, locale, timestamp.time)
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun getLastBackup(context: Context): BackupFileRecord? {
|
||||
return DatabaseFactory.getLokiBackupFilesDatabase(context).getLastBackupFile()
|
||||
}
|
||||
|
||||
@JvmStatic
|
||||
fun generateBackupPassphrase(): Array<String> {
|
||||
val result = arrayOfNulls<String>(6)
|
||||
val random = ByteArray(30)
|
||||
SecureRandom().nextBytes(random)
|
||||
for (i in 0..5) {
|
||||
result[i] = String.format("%05d", ByteUtil.byteArray5ToLong(random, i * 5) % 100000)
|
||||
}
|
||||
@Suppress("UNCHECKED_CAST")
|
||||
return result as Array<String>
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue