Adds backup nearly functional
This commit is contained in:
parent
8da8afed49
commit
3ed04c7cd5
13 changed files with 272 additions and 3 deletions
3
.gitmodules
vendored
Normal file
3
.gitmodules
vendored
Normal file
|
|
@ -0,0 +1,3 @@
|
|||
[submodule "libs/privacy-friendly-backup-api"]
|
||||
path = libs/privacy-friendly-backup-api
|
||||
url = https://github.com/SecUSo/privacy-friendly-backup-api.git
|
||||
|
|
@ -1,4 +1,6 @@
|
|||
apply plugin: 'com.android.application'
|
||||
apply plugin: 'kotlin-android'
|
||||
|
||||
|
||||
android {
|
||||
compileSdkVersion 33
|
||||
|
|
@ -48,4 +50,21 @@ dependencies {
|
|||
testImplementation 'junit:junit:4.12'
|
||||
// https://github.com/ShawnLin013/NumberPicker
|
||||
implementation 'com.shawnlin:number-picker:2.4.4'
|
||||
|
||||
// Backup
|
||||
implementation project(path: ':backup-api')
|
||||
def work_version = "2.4.0"
|
||||
implementation "androidx.work:work-runtime:$work_version"
|
||||
implementation "androidx.work:work-runtime-ktx:$work_version"
|
||||
androidTestImplementation "androidx.work:work-testing:$work_version"
|
||||
implementation 'androidx.sqlite:sqlite-ktx:2.3.1'
|
||||
|
||||
constraints {
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.0") {
|
||||
because("kotlin-stdlib-jdk7 is now a part of kotlin-stdlib")
|
||||
}
|
||||
implementation("org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.0") {
|
||||
because("kotlin-stdlib-jdk8 is now a part of kotlin-stdlib")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,5 +1,7 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
<manifest
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
|
||||
<uses-permission android:name="android.permission.SCHEDULE_EXACT_ALARM" />
|
||||
|
|
@ -11,6 +13,7 @@
|
|||
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
|
||||
|
||||
<application
|
||||
android:name=".PFAktivpause"
|
||||
android:allowBackup="true"
|
||||
android:icon="@mipmap/ic_launcher"
|
||||
android:label="@string/app_name"
|
||||
|
|
@ -117,6 +120,16 @@
|
|||
android:name="org.secuso.aktivpause.service.TimerService"
|
||||
android:enabled="true"
|
||||
android:exported="false" />
|
||||
<service
|
||||
android:name=".backup.PFABackupService"
|
||||
android:enabled="true"
|
||||
android:exported="true"
|
||||
android:process=":backup"
|
||||
tools:ignore="ExportedService">
|
||||
<intent-filter>
|
||||
<action android:name="org.secuso.privacyfriendlybackup.api.pfa.PFAAuthService" />
|
||||
</intent-filter>
|
||||
</service>
|
||||
|
||||
<receiver android:name="org.secuso.aktivpause.receivers.OnBootCompletedReceiver"
|
||||
android:exported="true">
|
||||
|
|
@ -127,6 +140,11 @@
|
|||
|
||||
<receiver android:name="org.secuso.aktivpause.receivers.TimerSchedulerReceiver"/>
|
||||
|
||||
<provider
|
||||
android:name="androidx.startup.InitializationProvider"
|
||||
android:authorities="${applicationId}.androidx-startup"
|
||||
tools:node="remove" />
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
21
app/src/main/java/org/secuso/aktivpause/PFAktivpause.kt
Normal file
21
app/src/main/java/org/secuso/aktivpause/PFAktivpause.kt
Normal file
|
|
@ -0,0 +1,21 @@
|
|||
package org.secuso.aktivpause
|
||||
|
||||
import android.app.Application
|
||||
import android.util.Log
|
||||
import androidx.work.Configuration
|
||||
import org.secuso.privacyfriendlybackup.api.pfa.BackupManager
|
||||
import org.secuso.aktivpause.backup.BackupCreator
|
||||
import org.secuso.aktivpause.backup.BackupRestorer
|
||||
|
||||
class PFAktivpause : Application(), Configuration.Provider {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
BackupManager.backupCreator = BackupCreator()
|
||||
BackupManager.backupRestorer = BackupRestorer()
|
||||
}
|
||||
|
||||
override fun getWorkManagerConfiguration(): Configuration {
|
||||
return Configuration.Builder().setMinimumLoggingLevel(Log.INFO).build()
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,55 @@
|
|||
package org.secuso.aktivpause.backup
|
||||
|
||||
|
||||
import android.content.Context
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.JsonWriter
|
||||
import android.util.Log
|
||||
import org.secuso.privacyfriendlybackup.api.backup.DatabaseUtil.getSupportSQLiteOpenHelper
|
||||
import org.secuso.privacyfriendlybackup.api.backup.DatabaseUtil.writeDatabase
|
||||
import org.secuso.privacyfriendlybackup.api.backup.PreferenceUtil.writePreferences
|
||||
import org.secuso.privacyfriendlybackup.api.pfa.IBackupCreator
|
||||
import org.secuso.aktivpause.database.SQLiteHelper
|
||||
import java.io.OutputStream
|
||||
import java.io.OutputStreamWriter
|
||||
|
||||
class BackupCreator : IBackupCreator {
|
||||
override fun writeBackup(context: Context, outputStream: OutputStream): Boolean {
|
||||
Log.d(TAG, "createBackup() started")
|
||||
val outputStreamWriter = OutputStreamWriter(outputStream, Charsets.UTF_8)
|
||||
val writer = JsonWriter(outputStreamWriter)
|
||||
writer.setIndent("")
|
||||
|
||||
try {
|
||||
writer.beginObject()
|
||||
|
||||
Log.d(TAG, "Writing database")
|
||||
writer.name("database")
|
||||
|
||||
val database = getSupportSQLiteOpenHelper(context, SQLiteHelper.DATABASE_NAME).readableDatabase
|
||||
|
||||
writeDatabase(writer, database)
|
||||
database.close()
|
||||
|
||||
Log.d(TAG, "Writing preferences")
|
||||
writer.name("preferences")
|
||||
|
||||
val pref = PreferenceManager.getDefaultSharedPreferences(context.applicationContext)
|
||||
writePreferences(writer, pref)
|
||||
|
||||
writer.endObject()
|
||||
writer.close()
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error occurred", e)
|
||||
e.printStackTrace()
|
||||
return false
|
||||
}
|
||||
|
||||
Log.d(TAG, "Backup created successfully")
|
||||
return true
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "PFABackupCreator"
|
||||
}
|
||||
}
|
||||
142
app/src/main/java/org/secuso/aktivpause/backup/BackupRestorer.kt
Normal file
142
app/src/main/java/org/secuso/aktivpause/backup/BackupRestorer.kt
Normal file
|
|
@ -0,0 +1,142 @@
|
|||
package org.secuso.aktivpause.backup
|
||||
|
||||
import android.content.Context
|
||||
import android.content.SharedPreferences
|
||||
import android.preference.PreferenceManager
|
||||
import android.util.JsonReader
|
||||
import android.util.Log
|
||||
import androidx.annotation.NonNull
|
||||
import org.secuso.privacyfriendlybackup.api.backup.DatabaseUtil
|
||||
import org.secuso.privacyfriendlybackup.api.backup.FileUtil
|
||||
import org.secuso.privacyfriendlybackup.api.pfa.IBackupRestorer
|
||||
import org.secuso.aktivpause.database.SQLiteHelper
|
||||
import java.io.IOException
|
||||
import java.io.InputStream
|
||||
import java.io.InputStreamReader
|
||||
import kotlin.system.exitProcess
|
||||
|
||||
|
||||
class BackupRestorer : IBackupRestorer {
|
||||
@Throws(IOException::class)
|
||||
private fun readDatabase(reader: JsonReader, context: Context) {
|
||||
reader.beginObject()
|
||||
val n1: String = reader.nextName()
|
||||
if (n1 != "version") {
|
||||
throw RuntimeException("Unknown value $n1")
|
||||
}
|
||||
val version: Int = reader.nextInt()
|
||||
val n2: String = reader.nextName()
|
||||
if (n2 != "content") {
|
||||
throw RuntimeException("Unknown value $n2")
|
||||
}
|
||||
|
||||
Log.d(TAG, "Restoring database...")
|
||||
val restoreDatabaseName = "restoreDatabase"
|
||||
|
||||
// delete if file already exists
|
||||
val restoreDatabaseFile = context.getDatabasePath(restoreDatabaseName)
|
||||
if (restoreDatabaseFile.exists()) {
|
||||
DatabaseUtil.deleteRoomDatabase(context, restoreDatabaseName)
|
||||
}
|
||||
|
||||
// create new restore database
|
||||
val db = DatabaseUtil.getSupportSQLiteOpenHelper(context, restoreDatabaseName, version).writableDatabase
|
||||
|
||||
db.beginTransaction()
|
||||
db.version = version
|
||||
|
||||
Log.d(TAG, "Copying database contents...")
|
||||
DatabaseUtil.readDatabaseContent(reader, db)
|
||||
Log.d(TAG, "succesfully read database")
|
||||
db.setTransactionSuccessful()
|
||||
db.endTransaction()
|
||||
db.close()
|
||||
|
||||
reader.endObject()
|
||||
|
||||
// copy file to correct location
|
||||
val actualDatabaseFile = context.getDatabasePath(SQLiteHelper.DATABASE_NAME)
|
||||
|
||||
DatabaseUtil.deleteRoomDatabase(context, SQLiteHelper.DATABASE_NAME)
|
||||
|
||||
FileUtil.copyFile(restoreDatabaseFile, actualDatabaseFile)
|
||||
Log.d(TAG, "Database Restored")
|
||||
|
||||
// delete restore database
|
||||
DatabaseUtil.deleteRoomDatabase(context, restoreDatabaseName)
|
||||
}
|
||||
|
||||
@Throws(IOException::class)
|
||||
private fun readPreferences(reader: JsonReader, preferences: SharedPreferences.Editor) {
|
||||
reader.beginObject()
|
||||
while (reader.hasNext()) {
|
||||
val name: String = reader.nextName()
|
||||
Log.d("preference", name)
|
||||
when (name) {
|
||||
"pref_schedule_exercise",
|
||||
"pref_keep_screen_on_during_exercise",
|
||||
"REPEAT_STATUS",
|
||||
"pref_hide_default_exercise_sets",
|
||||
"pref_schedule_exercise_daystrigger",
|
||||
"pref_exercise_continuous",
|
||||
"IsFirstTimeLaunch",
|
||||
"pref_schedule_random_exercise",
|
||||
"REPEAT_EXERCISES" -> preferences.putBoolean(name, reader.nextBoolean())
|
||||
"pref_exercise_time" -> preferences.putString(name, reader.nextString())
|
||||
"FirstLaunchManager.PREF_PICKER_SECONDS",
|
||||
"FirstLaunchManager.PREF_PICKER_Minutes",
|
||||
"FirstLaunchManager.PREF_BREAK_PICKER_SECONDS",
|
||||
"FirstLaunchManager.PREF_PICKER_HOURS",
|
||||
"FirstLaunchManager.PREF_BREAK_PICKER_MINUTES" -> preferences.putInt(name, reader.nextInt())
|
||||
"pref_schedule_exercise_days" -> preferences.putStringSet(name, readPreferenceSet(reader))
|
||||
"WORK_TIME",
|
||||
"PAUSE TIME",
|
||||
"pref_schedule_exercise_time",
|
||||
"DEFAULT_EXERCISE_SET" -> preferences.putLong(name, reader.nextLong())
|
||||
else -> throw RuntimeException("Unknown preference $name")
|
||||
}
|
||||
}
|
||||
reader.endObject()
|
||||
}
|
||||
|
||||
private fun readPreferenceSet(reader: JsonReader): Set<String> {
|
||||
val preferenceSet = mutableSetOf<String>()
|
||||
|
||||
reader.beginArray()
|
||||
while (reader.hasNext()) {
|
||||
preferenceSet.add(reader.nextString());
|
||||
}
|
||||
reader.endArray()
|
||||
return preferenceSet
|
||||
}
|
||||
|
||||
override fun restoreBackup(context: Context, restoreData: InputStream): Boolean {
|
||||
return try {
|
||||
val isReader = InputStreamReader(restoreData)
|
||||
val reader = JsonReader(isReader)
|
||||
val preferences = PreferenceManager.getDefaultSharedPreferences(context).edit()
|
||||
|
||||
// START
|
||||
reader.beginObject()
|
||||
while (reader.hasNext()) {
|
||||
val type: String = reader.nextName()
|
||||
when (type) {
|
||||
"database" -> readDatabase(reader, context)
|
||||
"preferences" -> readPreferences(reader, preferences)
|
||||
else -> throw RuntimeException("Can not parse type $type")
|
||||
}
|
||||
}
|
||||
reader.endObject()
|
||||
preferences.commit()
|
||||
|
||||
exitProcess(0)
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
false
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val TAG = "PFABackupRestorer"
|
||||
}
|
||||
}
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
package org.secuso.aktivpause.backup
|
||||
|
||||
import org.secuso.privacyfriendlybackup.api.pfa.PFAAuthService
|
||||
|
||||
class PFABackupService : PFAAuthService()
|
||||
|
|
@ -27,7 +27,7 @@ public class SQLiteHelper extends SQLiteAssetHelper {
|
|||
|
||||
private static final String TAG = SQLiteHelper.class.getSimpleName();
|
||||
|
||||
private static final String DATABASE_NAME = "exercises.sqlite";
|
||||
public static final String DATABASE_NAME = "exercises.sqlite";
|
||||
private static final int DATABASE_VERSION = 1;
|
||||
|
||||
private static final String[] deleteQueryList = {
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
?xml version="1.0" encoding="utf-8"?>
|
||||
<androidx.drawerlayout.widget.DrawerLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
|
|
|
|||
|
|
@ -9,8 +9,11 @@ buildscript {
|
|||
}
|
||||
google()
|
||||
}
|
||||
|
||||
ext.kotlin_version = "1.7.20"
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.4.2'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
|
|
|||
0
gradlew
vendored
Normal file → Executable file
0
gradlew
vendored
Normal file → Executable file
1
libs/privacy-friendly-backup-api
Submodule
1
libs/privacy-friendly-backup-api
Submodule
|
|
@ -0,0 +1 @@
|
|||
Subproject commit d267b0d5e899fe12f41a6b7aac2081b8d4ea71af
|
||||
|
|
@ -1 +1,3 @@
|
|||
include ':app'
|
||||
include ':backup-api'
|
||||
project(':backup-api').projectDir = new File('libs/privacy-friendly-backup-api/BackupAPI')
|
||||
Loading…
Add table
Add a link
Reference in a new issue