diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..5fb4a5c --- /dev/null +++ b/.gitmodules @@ -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 diff --git a/app/build.gradle b/app/build.gradle index f55791c..aea1d0b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -1,16 +1,16 @@ apply plugin: 'com.android.application' +apply plugin: 'kotlin-android' android { - compileSdkVersion 28 - buildToolsVersion '28.0.3' + compileSdkVersion 32 defaultConfig { applicationId "org.secuso.privacyfriendlyfoodtracker" minSdkVersion 21 - targetSdkVersion 27 + targetSdkVersion 32 versionCode 4 versionName "1.1.0" - testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" + testInstrumentationRunner 'androidx.test.runner.AndroidJUnitRunner' javaCompileOptions { annotationProcessorOptions { @@ -18,6 +18,13 @@ android { } } } + + compileOptions { + coreLibraryDesugaringEnabled true + sourceCompatibility JavaVersion.VERSION_1_8 + targetCompatibility JavaVersion.VERSION_1_8 + } + buildTypes { release { minifyEnabled false @@ -31,37 +38,39 @@ android { } dependencies { - implementation 'com.android.support.constraint:constraint-layout:1.1.3' - testImplementation 'junit:junit:4.12' - androidTestImplementation 'androidx.test:runner:1.2.0' - androidTestImplementation 'androidx.test:rules:1.2.0' + implementation 'androidx.constraintlayout:constraintlayout:2.1.4' + testImplementation 'junit:junit:4.13.2' + androidTestImplementation 'androidx.test:runner:1.4.0' + androidTestImplementation 'androidx.test:rules:1.4.0' - androidTestImplementation 'com.android.support.test:runner:1.0.2' + androidTestImplementation 'androidx.test.ext:junit:1.1.3' implementation fileTree(dir: 'libs', include: ['*.jar']) - implementation 'com.android.support:appcompat-v7:28.0.0' - implementation 'com.android.support:design:28.0.0' - implementation 'com.android.support:support-v4:28.0.0' + implementation 'androidx.appcompat:appcompat:1.5.0' + implementation 'com.google.android.material:material:1.6.1' + implementation 'androidx.legacy:legacy-support-v4:1.0.0' + implementation 'androidx.cardview:cardview:1.0.0' + implementation 'androidx.recyclerview:recyclerview:1.2.1' + + coreLibraryDesugaring 'com.android.tools:desugar_jdk_libs:1.1.5' // Room components - implementation "android.arch.persistence.room:runtime:$rootProject.roomVersion" - annotationProcessor "android.arch.persistence.room:compiler:$rootProject.roomVersion" - androidTestImplementation "android.arch.persistence.room:testing:$rootProject.roomVersion" + implementation 'androidx.room:room-runtime:2.4.3' + annotationProcessor 'androidx.room:room-compiler:2.4.3' + androidTestImplementation 'androidx.room:room-testing:2.4.3' // Lifecycle components - implementation "android.arch.lifecycle:extensions:$rootProject.archLifecycleVersion" - annotationProcessor "android.arch.lifecycle:compiler:$rootProject.archLifecycleVersion" + implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0' // Gson - implementation 'com.google.code.gson:gson:2.8.0' + implementation 'com.google.code.gson:gson:2.8.9' - // Saferoom - implementation "com.commonsware.cwac:saferoom:0.4.4" - implementation 'com.android.support:cardview-v7:28.0.0' + // SQLCipher + implementation 'net.zetetic:android-database-sqlcipher:4.5.0' // Retrofit - implementation 'com.squareup.retrofit2:retrofit:2.5.0' - implementation 'com.squareup.retrofit2:converter-gson:2.5.0' + implementation 'com.squareup.retrofit2:retrofit:2.9.0' + implementation 'com.squareup.retrofit2:converter-gson:2.9.0' //Jakewharton implementation 'com.jakewharton.picasso:picasso2-okhttp3-downloader:1.1.0' @@ -69,8 +78,11 @@ dependencies { // Statistic implementation 'com.jjoe64:graphview:4.2.2' - implementation 'com.android.support:recyclerview-v7:28.0.0' - - + // Backup API + implementation project(':backup-api') + def work_version = '2.7.1' + implementation "androidx.work:work-runtime:$work_version" + implementation "androidx.work:work-runtime-ktx:$work_version" + androidTestImplementation "androidx.work:work-testing:$work_version" } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index a403d5a..0909750 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -1,5 +1,6 @@ - @@ -74,13 +76,28 @@ android:name=".ui.BaseStatisticActivity" android:label="@string/action_statistic" android:parentActivityName=".ui.MainActivity" - android:theme="@style/AppTheme.NoActionBar"> + android:theme="@style/AppTheme.NoActionBar" /> + android:windowSoftInputMode="adjustResize" /> + + + + + + + \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/PFFoodTrackerApplication.kt b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/PFFoodTrackerApplication.kt new file mode 100644 index 0000000..f707fd1 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/PFFoodTrackerApplication.kt @@ -0,0 +1,21 @@ +package org.secuso.privacyfriendlyfoodtracker + +import android.app.Application +import android.util.Log +import androidx.work.Configuration +import org.secuso.privacyfriendlybackup.api.pfa.BackupManager +import org.secuso.privacyfriendlyfoodtracker.backup.BackupCreator +import org.secuso.privacyfriendlyfoodtracker.backup.BackupRestorer + +class PFFoodTrackerApplication : 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() + } +} \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/backup/BackupCreator.kt b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/backup/BackupCreator.kt new file mode 100644 index 0000000..a26c956 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/backup/BackupCreator.kt @@ -0,0 +1,73 @@ +package org.secuso.privacyfriendlyfoodtracker.backup + +import android.content.Context +import android.preference.PreferenceManager +import android.util.JsonWriter +import android.util.Log +import net.sqlcipher.database.SQLiteDatabase +import net.sqlcipher.database.SQLiteDatabaseHook +import org.secuso.privacyfriendlybackup.api.backup.DatabaseUtil +import org.secuso.privacyfriendlybackup.api.backup.PreferenceUtil +import org.secuso.privacyfriendlybackup.api.pfa.IBackupCreator +import org.secuso.privacyfriendlyfoodtracker.database.ApplicationDatabase +import org.secuso.privacyfriendlyfoodtracker.helpers.KeyGenHelper +import java.io.OutputStream +import java.io.OutputStreamWriter + +class BackupCreator : IBackupCreator { + override fun writeBackup(context: Context, outputStream: OutputStream) { + 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") + + SQLiteDatabase.loadLibs(context) + + if (context.getDatabasePath(ApplicationDatabase.DATABASE_NAME).exists()) { + val database = SQLiteDatabase.openDatabase( + context.getDatabasePath(ApplicationDatabase.DATABASE_NAME).path, + KeyGenHelper.getSecretKeyAsChar(context), + null, + SQLiteDatabase.OPEN_READONLY, + object : SQLiteDatabaseHook { + override fun preKey(database: SQLiteDatabase) {} + override fun postKey(database: SQLiteDatabase) { + database.rawExecSQL("PRAGMA cipher_compatibility = 3;") + } + }) + + DatabaseUtil.writeDatabase(writer, database) + database.close() + } else { + Log.d(TAG, "No database found") + writer.beginObject() + writer.endObject() + } + + Log.d(TAG, "Writing preferences") + writer.name("preferences") + + val pref = PreferenceManager.getDefaultSharedPreferences(context.applicationContext) + PreferenceUtil.writePreferences(writer, pref, arrayOf(KeyGenHelper.PREFERENCE_ENCRYPTED_KEY_NAME)) + + Log.d(TAG, "Writing files") + writer.endObject() + writer.close() + } catch (e: Exception) { + Log.e(TAG, "Error occurred", e) + e.printStackTrace() + } + + Log.d(TAG, "Backup created successfully") + } + + companion object { + const val TAG = "PFABackupCreator" + } +} \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/backup/BackupRestorer.kt b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/backup/BackupRestorer.kt new file mode 100644 index 0000000..c767db7 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/backup/BackupRestorer.kt @@ -0,0 +1,126 @@ +package org.secuso.privacyfriendlyfoodtracker.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 net.sqlcipher.database.SQLiteDatabase +import net.sqlcipher.database.SQLiteDatabaseHook +import org.secuso.privacyfriendlybackup.api.backup.DatabaseUtil +import org.secuso.privacyfriendlybackup.api.backup.FileUtil +import org.secuso.privacyfriendlybackup.api.pfa.IBackupRestorer +import org.secuso.privacyfriendlyfoodtracker.database.ApplicationDatabase +import org.secuso.privacyfriendlyfoodtracker.helpers.KeyGenHelper +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(@NonNull reader: JsonReader, @NonNull 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) + } + + // check if key exists + if (!KeyGenHelper.isKeyGenerated()) { + Log.d(TAG, "No key found. Generating new key...") + KeyGenHelper.generateKey(context) + KeyGenHelper.generatePassphrase(context) + Log.d(TAG, "Key generated") + } + val databasePassword = KeyGenHelper.getSecretKeyAsChar(context) + // create new restore database + val db: SQLiteDatabase = + SQLiteDatabase.openOrCreateDatabase(context.getDatabasePath(restoreDatabaseName).path, databasePassword, null, object : SQLiteDatabaseHook { + override fun preKey(database: SQLiteDatabase?) {} + override fun postKey(database: SQLiteDatabase) { + database.rawExecSQL("PRAGMA cipher_compatibility = 3;") + } + }) + + db.beginTransaction() + db.version = version + + Log.d(TAG, "Copying database contents...") + DatabaseUtil.readDatabaseContent(reader, db) + db.setTransactionSuccessful() + db.endTransaction() + db.close() + + reader.endObject() + + // copy file to correct location + val actualDatabaseFile = context.getDatabasePath(ApplicationDatabase.DATABASE_NAME) + + DatabaseUtil.deleteRoomDatabase(context, ApplicationDatabase.DATABASE_NAME) + + FileUtil.copyFile(restoreDatabaseFile, actualDatabaseFile) + Log.d(TAG, "Database Restored") + + // delete restore database + DatabaseUtil.deleteRoomDatabase(context, restoreDatabaseName) + } + + @Throws(IOException::class) + private fun readPreferences(@NonNull reader: JsonReader, @NonNull context: Context) { + reader.beginObject() + val pref: SharedPreferences.Editor = PreferenceManager.getDefaultSharedPreferences(context).edit() + while (reader.hasNext()) { + val name: String = reader.nextName() + when (name) { + "IsFirstTimeLaunch" -> pref + .putBoolean(name, reader.nextBoolean()) + else -> throw RuntimeException("Unknown preference $name") + } + } + pref.commit() + reader.endObject() + } + + override fun restoreBackup(context: Context, restoreData: InputStream): Boolean { + return try { + val isReader = InputStreamReader(restoreData) + val reader = JsonReader(isReader) + + // START + reader.beginObject() + while (reader.hasNext()) { + val type: String = reader.nextName() + when (type) { + "database" -> readDatabase(reader, context) + "preferences" -> readPreferences(reader, context) + else -> throw RuntimeException("Can not parse type $type") + } + } + reader.endObject() + exitProcess(0) + } catch (e: Exception) { + false + } + } + + companion object { + const val TAG = "PFABackupRestorer" + } +} \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/backup/PFABackupService.kt b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/backup/PFABackupService.kt new file mode 100644 index 0000000..114d425 --- /dev/null +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/backup/PFABackupService.kt @@ -0,0 +1,5 @@ +package org.secuso.privacyfriendlyfoodtracker.backup + +import org.secuso.privacyfriendlybackup.api.pfa.PFAAuthService + +class PFABackupService : PFAAuthService() \ No newline at end of file diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ApplicationDatabase.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ApplicationDatabase.java index 7d62ea8..8e48b1b 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ApplicationDatabase.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ApplicationDatabase.java @@ -16,21 +16,24 @@ */ package org.secuso.privacyfriendlyfoodtracker.database; -import android.arch.lifecycle.LiveData; -import android.arch.lifecycle.MutableLiveData; -import android.arch.persistence.db.SupportSQLiteDatabase; -import android.arch.persistence.room.Database; -import android.arch.persistence.room.Room; -import android.arch.persistence.room.RoomDatabase; -import android.arch.persistence.room.TypeConverters; import android.content.Context; -import android.support.annotation.NonNull; import android.util.Log; -import com.commonsware.cwac.saferoom.SafeHelperFactory; +import androidx.annotation.NonNull; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.room.Database; +import androidx.room.Room; +import androidx.room.RoomDatabase; +import androidx.room.TypeConverters; +import androidx.sqlite.db.SupportSQLiteDatabase; + +import net.sqlcipher.database.SQLiteDatabase; +import net.sqlcipher.database.SQLiteDatabaseHook; +import net.sqlcipher.database.SupportFactory; -import org.secuso.privacyfriendlyfoodtracker.helpers.KeyGenHelper; import org.secuso.privacyfriendlyfoodtracker.database.converter.DateConverter; +import org.secuso.privacyfriendlyfoodtracker.helpers.KeyGenHelper; /** * Database singleton. @@ -44,7 +47,9 @@ public abstract class ApplicationDatabase extends RoomDatabase { public static final String DATABASE_NAME = "consumed_entries_database"; public abstract ConsumedEntriesDao getConsumedEntriesDao(); + public abstract ProductDao getProductDao(); + public abstract ConsumedEntrieAndProductDao getConsumedEntriesAndProductDao(); private static ApplicationDatabase sInstance; @@ -55,9 +60,18 @@ public static ApplicationDatabase getInstance(final Context context) throws Exce if (sInstance == null) { synchronized (ApplicationDatabase.class) { if (sInstance == null) { - SafeHelperFactory factory = new SafeHelperFactory(KeyGenHelper.getSecretKeyAsChar(context)); - - sInstance = Room.databaseBuilder(context.getApplicationContext(),ApplicationDatabase.class, DATABASE_NAME) + SupportFactory factory = new SupportFactory(SQLiteDatabase.getBytes(KeyGenHelper.getSecretKeyAsChar(context)), new SQLiteDatabaseHook() { + @Override + public void preKey(SQLiteDatabase database) { + } + + @Override + public void postKey(SQLiteDatabase database) { + database.rawExecSQL("PRAGMA cipher_compatibility = 3;"); + } + }); + + sInstance = Room.databaseBuilder(context.getApplicationContext(), ApplicationDatabase.class, DATABASE_NAME) .openHelperFactory(factory) .allowMainThreadQueries() .addCallback(new Callback() { @@ -86,6 +100,7 @@ private void setDatabaseCreated() { /** * Indicates if the database is created. + * * @return if database is created */ public LiveData getDatabaseCreated() { diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntrieAndProductDao.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntrieAndProductDao.java index f21188d..af643d0 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntrieAndProductDao.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntrieAndProductDao.java @@ -16,11 +16,9 @@ */ package org.secuso.privacyfriendlyfoodtracker.database; -import android.arch.lifecycle.LiveData; -import android.arch.persistence.room.Dao; -import android.arch.persistence.room.Query; - -import com.jjoe64.graphview.series.DataPoint; +import androidx.lifecycle.LiveData; +import androidx.room.Dao; +import androidx.room.Query; import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseEntry; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntries.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntries.java index 9042caf..a5093f7 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntries.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntries.java @@ -16,17 +16,17 @@ */ package org.secuso.privacyfriendlyfoodtracker.database; -import android.arch.persistence.room.Entity; -import android.arch.persistence.room.ForeignKey; -import android.arch.persistence.room.PrimaryKey; -import android.arch.persistence.room.TypeConverters; +import androidx.room.Entity; +import androidx.room.ForeignKey; +import androidx.room.PrimaryKey; +import androidx.room.TypeConverters; import org.secuso.privacyfriendlyfoodtracker.database.converter.DateConverter; import java.sql.Date; -import static android.arch.persistence.room.ForeignKey.CASCADE; +import static androidx.room.ForeignKey.CASCADE; /** * Information about consuming. diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntriesDao.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntriesDao.java index d412bb2..30e7b26 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntriesDao.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ConsumedEntriesDao.java @@ -16,12 +16,12 @@ */ package org.secuso.privacyfriendlyfoodtracker.database; -import android.arch.lifecycle.LiveData; -import android.arch.persistence.room.Dao; -import android.arch.persistence.room.Delete; -import android.arch.persistence.room.Insert; -import android.arch.persistence.room.Query; -import android.arch.persistence.room.Update; +import androidx.lifecycle.LiveData; +import androidx.room.Dao; +import androidx.room.Delete; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; import java.sql.Date; import java.util.List; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/Product.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/Product.java index 4662413..f4dd5ba 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/Product.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/Product.java @@ -16,10 +16,10 @@ */ package org.secuso.privacyfriendlyfoodtracker.database; -import android.arch.persistence.room.ColumnInfo; -import android.arch.persistence.room.Entity; -import android.arch.persistence.room.PrimaryKey; -import android.support.annotation.NonNull; +import androidx.room.ColumnInfo; +import androidx.room.Entity; +import androidx.room.PrimaryKey; +import androidx.annotation.NonNull; /** * A Product. diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ProductDao.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ProductDao.java index 3e6b0b1..e6b3b96 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ProductDao.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/ProductDao.java @@ -16,16 +16,14 @@ */ package org.secuso.privacyfriendlyfoodtracker.database; -import android.arch.lifecycle.LiveData; -import android.arch.persistence.room.Dao; -import android.arch.persistence.room.Delete; -import android.arch.persistence.room.Insert; -import android.arch.persistence.room.Query; -import android.arch.persistence.room.Update; - -import java.sql.Date; +import androidx.lifecycle.LiveData; +import androidx.room.Dao; +import androidx.room.Delete; +import androidx.room.Insert; +import androidx.room.Query; +import androidx.room.Update; + import java.util.List; -import java.util.concurrent.Future; /** * Includes methods that offer abstract access to the app database to manage products. diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/converter/DateConverter.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/converter/DateConverter.java index 27d97d4..b7a0ca2 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/converter/DateConverter.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/database/converter/DateConverter.java @@ -16,7 +16,7 @@ */ package org.secuso.privacyfriendlyfoodtracker.database.converter; -import android.arch.persistence.room.TypeConverter; +import androidx.room.TypeConverter; import java.sql.Date; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/helpers/KeyGenHelper.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/helpers/KeyGenHelper.java index cff27d9..facbc12 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/helpers/KeyGenHelper.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/helpers/KeyGenHelper.java @@ -26,22 +26,15 @@ import java.io.ByteArrayInputStream; import java.io.ByteArrayOutputStream; import java.math.BigInteger; -import java.nio.ByteBuffer; -import java.nio.CharBuffer; -import java.nio.charset.Charset; -import java.security.Key; import java.security.KeyPairGenerator; import java.security.KeyStore; -import java.security.KeyStoreException; import java.security.SecureRandom; import java.util.ArrayList; -import java.util.Arrays; import java.util.Calendar; import javax.crypto.Cipher; import javax.crypto.CipherInputStream; import javax.crypto.CipherOutputStream; -import javax.crypto.spec.SecretKeySpec; import javax.security.auth.x500.X500Principal; @@ -55,7 +48,7 @@ public class KeyGenHelper { private static final String AndroidKeyStore = "AndroidKeyStore"; private static final String KEY_ALIAS = "FoodTrackerKeyAlias"; private static final String SHARED_PREFERENCE_NAME = "FoodTrackerSharedPreferenceName"; - private static final String ENCRYPTED_KEY = "FoodTrackerKey"; + public static final String PREFERENCE_ENCRYPTED_KEY_NAME = "FoodTrackerKey"; private static final String RSA_MODE = "RSA/ECB/PKCS1Padding"; /** @@ -113,13 +106,13 @@ public static void generateKey(Context context) throws Exception { public static void generatePassphrase(Context context) throws Exception { SharedPreferences pref = context.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); - String enryptedKeyB64 = pref.getString(ENCRYPTED_KEY, null); - if(enryptedKeyB64 != null) return; + String encryptedKeyB64 = pref.getString(PREFERENCE_ENCRYPTED_KEY_NAME, null); + if(encryptedKeyB64 != null) return; byte[] encryptedKey = rsaEncrypt(generateKeyPassphrase()); - enryptedKeyB64 = Base64.encodeToString(encryptedKey, Base64.DEFAULT); + encryptedKeyB64 = Base64.encodeToString(encryptedKey, Base64.DEFAULT); SharedPreferences.Editor edit = pref.edit(); - edit.putString(ENCRYPTED_KEY, enryptedKeyB64); + edit.putString(PREFERENCE_ENCRYPTED_KEY_NAME, encryptedKeyB64); edit.apply(); } @@ -131,10 +124,10 @@ public static void generatePassphrase(Context context) throws Exception { */ public static char[] getSecretKeyAsChar(Context context) throws Exception { SharedPreferences pref = context.getSharedPreferences(SHARED_PREFERENCE_NAME, Context.MODE_PRIVATE); - String enryptedKeyB64 = pref.getString(ENCRYPTED_KEY, null); + String encryptedKeyB64 = pref.getString(PREFERENCE_ENCRYPTED_KEY_NAME, null); // need to check null, omitted here - byte[] encryptedKey = Base64.decode(enryptedKeyB64, Base64.DEFAULT); + byte[] encryptedKey = Base64.decode(encryptedKeyB64, Base64.DEFAULT); byte[] key = rsaDecrypt(encryptedKey); return charFromBytes(key); @@ -145,8 +138,8 @@ public static char[] getSecretKeyAsChar(Context context) throws Exception { * @param byteData the source byte array * @return source data as char array */ - public static char[] charFromBytes(byte byteData[]) { - char charData[] = new char[byteData.length]; + public static char[] charFromBytes(byte[] byteData) { + char[] charData = new char[byteData.length]; for(int i = 0; i < charData.length; i++) { charData[i] = (char) (((int) byteData[i]) & 0xFF); } @@ -197,7 +190,7 @@ private static byte[] rsaDecrypt(byte[] encrypted) throws Exception { */ private static byte[] generateKeyPassphrase() { SecureRandom random = new SecureRandom(); - byte bytes[] = new byte[16]; + byte[] bytes = new byte[16]; random.nextBytes(bytes); return bytes; } diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/AboutActivity.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/AboutActivity.java index 4257cf3..a2b5c50 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/AboutActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/AboutActivity.java @@ -18,8 +18,8 @@ package org.secuso.privacyfriendlyfoodtracker.ui; import android.os.Bundle; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; import android.text.method.LinkMovementMethod; import android.view.View; import android.widget.TextView; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/AddFoodFragment.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/AddFoodFragment.java index 2894cd5..3775364 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/AddFoodFragment.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/AddFoodFragment.java @@ -16,12 +16,7 @@ */ package org.secuso.privacyfriendlyfoodtracker.ui; -import android.arch.lifecycle.ViewModelProviders; import android.os.Bundle; -import android.support.design.widget.FloatingActionButton; -import android.support.design.widget.Snackbar; -import android.support.design.widget.TextInputLayout; -import android.support.v4.app.Fragment; import android.text.InputFilter; import android.text.InputType; import android.util.Log; @@ -30,6 +25,14 @@ import android.view.ViewGroup; import android.widget.EditText; import android.widget.TextView; + +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import com.google.android.material.snackbar.Snackbar; +import com.google.android.material.textfield.TextInputLayout; + import org.secuso.privacyfriendlyfoodtracker.R; import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseFacade; import org.secuso.privacyfriendlyfoodtracker.ui.viewmodels.SharedStatisticViewModel; @@ -71,7 +74,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, referenceActivity = (BaseAddFoodActivity) getActivity(); parentHolder = inflater.inflate(R.layout.content_food, container, false); - sharedStatisticViewModel = ViewModelProviders.of(getActivity()).get(SharedStatisticViewModel.class); + sharedStatisticViewModel = new ViewModelProvider(getActivity()).get(SharedStatisticViewModel.class); try { databaseFacade = new DatabaseFacade(referenceActivity.getApplicationContext()); } catch (Exception e){ diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/BaseAddFoodActivity.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/BaseAddFoodActivity.java index 1d07cc4..f56048b 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/BaseAddFoodActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/BaseAddFoodActivity.java @@ -17,12 +17,12 @@ package org.secuso.privacyfriendlyfoodtracker.ui; import android.content.Intent; -import android.support.design.widget.TabLayout; -import android.support.v4.view.ViewPager; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatActivity; +import com.google.android.material.tabs.TabLayout; +import androidx.viewpager.widget.ViewPager; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; -import android.support.v7.widget.Toolbar; +import androidx.appcompat.widget.Toolbar; import android.view.MenuItem; import org.secuso.privacyfriendlyfoodtracker.R; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/BaseStatisticActivity.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/BaseStatisticActivity.java index f43b6cc..2fc8e6b 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/BaseStatisticActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/BaseStatisticActivity.java @@ -16,8 +16,8 @@ */ package org.secuso.privacyfriendlyfoodtracker.ui; -import android.support.design.widget.TabLayout; -import android.support.v4.view.ViewPager; +import com.google.android.material.tabs.TabLayout; +import androidx.viewpager.widget.ViewPager; import android.os.Bundle; import org.secuso.privacyfriendlyfoodtracker.R; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/GenerateKeyActivity.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/GenerateKeyActivity.java index 4498706..34002cc 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/GenerateKeyActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/GenerateKeyActivity.java @@ -17,8 +17,8 @@ package org.secuso.privacyfriendlyfoodtracker.ui; import android.content.Intent; -import android.support.design.widget.FloatingActionButton; -import android.support.v7.app.AppCompatActivity; +import com.google.android.material.floatingactionbutton.FloatingActionButton; +import androidx.appcompat.app.AppCompatActivity; import android.os.Bundle; import android.util.Log; import android.view.View; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/MonthStatisticFragment.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/MonthStatisticFragment.java index 0cb9f04..af9f156 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/MonthStatisticFragment.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/MonthStatisticFragment.java @@ -16,13 +16,11 @@ */ package org.secuso.privacyfriendlyfoodtracker.ui; +import static org.secuso.privacyfriendlyfoodtracker.ui.helper.MathHelper.round; + import android.app.Activity; import android.app.DatePickerDialog; -import android.arch.lifecycle.Observer; -import android.arch.lifecycle.ViewModelProviders; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -31,15 +29,20 @@ import android.widget.DatePicker; import android.widget.TextView; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; + import com.jjoe64.graphview.GraphView; import com.jjoe64.graphview.helper.DateAsXAxisLabelFormatter; import com.jjoe64.graphview.series.DataPoint; import com.jjoe64.graphview.series.LineGraphSeries; import org.secuso.privacyfriendlyfoodtracker.R; +import org.secuso.privacyfriendlyfoodtracker.database.ConsumedEntrieAndProductDao; import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseFacade; import org.secuso.privacyfriendlyfoodtracker.ui.helper.DateHelper; -import org.secuso.privacyfriendlyfoodtracker.database.ConsumedEntrieAndProductDao; import org.secuso.privacyfriendlyfoodtracker.ui.viewmodels.SharedStatisticViewModel; import java.math.BigDecimal; @@ -47,8 +50,6 @@ import java.util.Date; import java.util.List; -import static org.secuso.privacyfriendlyfoodtracker.ui.helper.MathHelper.round; - /** * A simple {@link Fragment} subclass. Contains the statistic for a month. @@ -73,7 +74,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, referenceActivity = getActivity(); parentHolder = inflater.inflate(R.layout.fragment_month_statistic, container, false); - sharedStatisticViewModel = ViewModelProviders.of(getActivity()).get(SharedStatisticViewModel.class); + sharedStatisticViewModel = new ViewModelProvider(getActivity()).get(SharedStatisticViewModel.class); try { databaseFacade = new DatabaseFacade(referenceActivity.getApplicationContext()); } catch (Exception e){ @@ -120,7 +121,7 @@ public void onClick(View v) { } }); UpdateGraph(); - sharedStatisticViewModel.getLiveCalendar().observe(this, new Observer() { + sharedStatisticViewModel.getLiveCalendar().observe(getViewLifecycleOwner(), new Observer() { @Override public void onChanged(@Nullable Calendar calendar) { editText.setText(DateHelper.dateToString(getMonthByValue(-1)) + System.getProperty("line.separator") + " - " + System.getProperty("line.separator") + DateHelper.dateToString(calendar.getTime())); diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/OverviewActivity.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/OverviewActivity.java index 0060af2..a25f6cb 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/OverviewActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/OverviewActivity.java @@ -16,50 +16,47 @@ */ package org.secuso.privacyfriendlyfoodtracker.ui; -import org.secuso.privacyfriendlyfoodtracker.database.ConsumedEntries; -import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseEntry; -import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseFacade; -import org.secuso.privacyfriendlyfoodtracker.ui.viewmodels.OverviewViewModel; -import org.secuso.privacyfriendlyfoodtracker.ui.viewmodels.SharedStatisticViewModel; -import org.secuso.privacyfriendlyfoodtracker.ui.views.CheckableCardView; -import org.secuso.privacyfriendlyfoodtracker.R; - import android.app.AlertDialog; -import android.arch.lifecycle.Observer; -import android.arch.lifecycle.ViewModelProviders; -import android.arch.persistence.room.Database; import android.content.DialogInterface; -import android.support.annotation.NonNull; -import android.support.annotation.Nullable; -import android.support.v7.app.ActionBar; -import android.support.v7.widget.CardView; +import android.content.Intent; +import android.content.res.TypedArray; +import android.graphics.drawable.Drawable; +import android.os.Bundle; import android.text.InputFilter; import android.text.InputType; import android.util.TypedValue; import android.view.LayoutInflater; import android.view.Menu; -import android.support.v7.widget.Toolbar; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.widget.EditText; import android.widget.LinearLayout; import android.widget.TextView; -import android.content.res.TypedArray; -import android.graphics.drawable.Drawable; -import android.support.v4.content.ContextCompat; -import android.content.Intent; -import android.support.constraint.ConstraintLayout; -import android.support.constraint.ConstraintSet; -import android.support.design.widget.FloatingActionButton; -import android.support.v7.app.AppCompatActivity; -import android.os.Bundle; + +import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; +import androidx.cardview.widget.CardView; +import androidx.constraintlayout.widget.ConstraintLayout; +import androidx.constraintlayout.widget.ConstraintSet; +import androidx.core.content.ContextCompat; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; + +import org.secuso.privacyfriendlyfoodtracker.R; +import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseEntry; +import org.secuso.privacyfriendlyfoodtracker.ui.viewmodels.OverviewViewModel; +import org.secuso.privacyfriendlyfoodtracker.ui.views.CheckableCardView; import java.math.BigDecimal; import java.text.SimpleDateFormat; +import java.util.Date; import java.util.LinkedList; import java.util.List; -import java.util.Date; import java.util.Locale; /** @@ -95,7 +92,7 @@ protected void onCreate(Bundle savedInstanceState) { Intent intent = getIntent(); date = intent.getLongExtra("DATE", System.currentTimeMillis()); - viewModel = ViewModelProviders.of(this).get(OverviewViewModel.class); + viewModel = new ViewModelProvider(this).get(OverviewViewModel.class); viewModel.init(getDateForActivity()); viewModel.getList().observe(this, new Observer>() { diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/SearchFoodFragment.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/SearchFoodFragment.java index 9fbc43f..1aab8ab 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/SearchFoodFragment.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/SearchFoodFragment.java @@ -17,14 +17,7 @@ package org.secuso.privacyfriendlyfoodtracker.ui; import android.app.Activity; -import android.arch.lifecycle.ViewModelProviders; import android.os.Bundle; -import android.support.annotation.NonNull; -import android.support.design.widget.FloatingActionButton; -import android.support.v4.app.Fragment; -import android.support.v4.view.ViewPager; -import android.support.v7.widget.CardView; -import android.support.v7.widget.DividerItemDecoration; import android.text.Editable; import android.text.TextWatcher; import android.util.Log; @@ -36,18 +29,27 @@ import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.TextView; -import android.support.v7.widget.RecyclerView; -import android.support.v7.widget.LinearLayoutManager; + +import androidx.annotation.NonNull; +import androidx.cardview.widget.CardView; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.ViewModelProvider; +import androidx.recyclerview.widget.DividerItemDecoration; +import androidx.recyclerview.widget.LinearLayoutManager; +import androidx.recyclerview.widget.RecyclerView; +import androidx.viewpager.widget.ViewPager; + +import com.google.android.material.floatingactionbutton.FloatingActionButton; import org.secuso.privacyfriendlyfoodtracker.R; -import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseFacade; -import org.secuso.privacyfriendlyfoodtracker.ui.adapter.SearchResultAdapter; import org.secuso.privacyfriendlyfoodtracker.database.Product; import org.secuso.privacyfriendlyfoodtracker.network.ApiManager; import org.secuso.privacyfriendlyfoodtracker.network.ProductApiService; import org.secuso.privacyfriendlyfoodtracker.network.models.NetworkProduct; import org.secuso.privacyfriendlyfoodtracker.network.models.ProductResponse; import org.secuso.privacyfriendlyfoodtracker.network.utils.ProductConversionHelper; +import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseFacade; +import org.secuso.privacyfriendlyfoodtracker.ui.adapter.SearchResultAdapter; import org.secuso.privacyfriendlyfoodtracker.ui.viewmodels.SharedStatisticViewModel; import java.util.Calendar; @@ -98,7 +100,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, // Inflate the layout for this fragment referenceActivity = getActivity(); parentHolder = inflater.inflate(R.layout.content_search, container, false); - sharedStatisticViewModel = ViewModelProviders.of(getActivity()).get(SharedStatisticViewModel.class); + sharedStatisticViewModel = new ViewModelProvider(getActivity()).get(SharedStatisticViewModel.class); try { databaseFacade = new DatabaseFacade(referenceActivity.getApplicationContext()); } catch (Exception e) { diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/SplashActivity.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/SplashActivity.java index 9cf8369..31ee3cb 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/SplashActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/SplashActivity.java @@ -19,7 +19,7 @@ import android.content.Intent; import android.os.Bundle; -import android.support.v7.app.AppCompatActivity; +import androidx.appcompat.app.AppCompatActivity; import org.secuso.privacyfriendlyfoodtracker.helpers.FirstLaunchManager; import org.secuso.privacyfriendlyfoodtracker.helpers.KeyGenHelper; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/TutorialActivity.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/TutorialActivity.java index d5f0298..0a573ac 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/TutorialActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/TutorialActivity.java @@ -22,10 +22,10 @@ import android.graphics.Color; import android.os.Build; import android.os.Bundle; -import android.support.v4.content.ContextCompat; -import android.support.v4.view.PagerAdapter; -import android.support.v4.view.ViewPager; -import android.support.v7.app.AppCompatActivity; +import androidx.core.content.ContextCompat; +import androidx.viewpager.widget.PagerAdapter; +import androidx.viewpager.widget.ViewPager; +import androidx.appcompat.app.AppCompatActivity; import android.text.Html; import android.view.LayoutInflater; import android.view.View; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/WeekStatisticFragment.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/WeekStatisticFragment.java index cdc6506..c05162e 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/WeekStatisticFragment.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/WeekStatisticFragment.java @@ -17,13 +17,11 @@ package org.secuso.privacyfriendlyfoodtracker.ui; +import static org.secuso.privacyfriendlyfoodtracker.ui.helper.MathHelper.round; + import android.app.Activity; import android.app.DatePickerDialog; -import android.arch.lifecycle.Observer; -import android.arch.lifecycle.ViewModelProviders; import android.os.Bundle; -import android.support.annotation.Nullable; -import android.support.v4.app.Fragment; import android.util.Log; import android.view.LayoutInflater; import android.view.View; @@ -32,15 +30,20 @@ import android.widget.DatePicker; import android.widget.TextView; +import androidx.annotation.Nullable; +import androidx.fragment.app.Fragment; +import androidx.lifecycle.Observer; +import androidx.lifecycle.ViewModelProvider; + import com.jjoe64.graphview.GraphView; import com.jjoe64.graphview.helper.DateAsXAxisLabelFormatter; import com.jjoe64.graphview.series.DataPoint; import com.jjoe64.graphview.series.LineGraphSeries; import org.secuso.privacyfriendlyfoodtracker.R; +import org.secuso.privacyfriendlyfoodtracker.database.ConsumedEntrieAndProductDao; import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseFacade; import org.secuso.privacyfriendlyfoodtracker.ui.helper.DateHelper; -import org.secuso.privacyfriendlyfoodtracker.database.ConsumedEntrieAndProductDao; import org.secuso.privacyfriendlyfoodtracker.ui.viewmodels.SharedStatisticViewModel; import java.math.BigDecimal; @@ -49,8 +52,6 @@ import java.util.Date; import java.util.List; -import static org.secuso.privacyfriendlyfoodtracker.ui.helper.MathHelper.round; - /** * A simple {@link Fragment} subclass. Contains the statistic for a week. @@ -74,7 +75,7 @@ public View onCreateView(LayoutInflater inflater, ViewGroup container, // Inflate the layout for this fragment referenceActivity = getActivity(); parentHolder = inflater.inflate(R.layout.fragment_month_statistic, container, false); - sharedStatisticViewModel = ViewModelProviders.of(getActivity()).get(SharedStatisticViewModel.class); + sharedStatisticViewModel = new ViewModelProvider(getActivity()).get(SharedStatisticViewModel.class); try { databaseFacade = new DatabaseFacade(referenceActivity.getApplicationContext()); } catch (Exception e){ @@ -119,7 +120,7 @@ public void onClick(View v) { } }); UpdateGraph(); - sharedStatisticViewModel.getLiveCalendar().observe(this, new Observer() { + sharedStatisticViewModel.getLiveCalendar().observe(getViewLifecycleOwner(), new Observer() { @Override public void onChanged(@Nullable Calendar calendar) { editText.setText(DateHelper.dateToString(getWeekByValue(-1)) + System.getProperty("line.separator") + " - " + System.getProperty("line.separator") + DateHelper.dateToString(calendar.getTime())); diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/AddFoodPagerAdapter.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/AddFoodPagerAdapter.java index cbe573c..fc6a6ee 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/AddFoodPagerAdapter.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/AddFoodPagerAdapter.java @@ -17,9 +17,9 @@ package org.secuso.privacyfriendlyfoodtracker.ui.adapter; import android.content.Context; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; import org.secuso.privacyfriendlyfoodtracker.R; import org.secuso.privacyfriendlyfoodtracker.ui.AddFoodFragment; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/DatabaseFacade.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/DatabaseFacade.java index 80967c7..b3d95cd 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/DatabaseFacade.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/DatabaseFacade.java @@ -16,7 +16,7 @@ */ package org.secuso.privacyfriendlyfoodtracker.ui.adapter; -import android.arch.lifecycle.LiveData; +import androidx.lifecycle.LiveData; import android.content.Context; import android.util.Log; @@ -30,7 +30,6 @@ import java.util.ArrayList; import java.util.List; import java.util.concurrent.Executors; -import java.util.concurrent.Future; /** * Database access functions. diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/SearchResultAdapter.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/SearchResultAdapter.java index 7bc4e6b..2f52cc4 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/SearchResultAdapter.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/SearchResultAdapter.java @@ -16,8 +16,8 @@ */ package org.secuso.privacyfriendlyfoodtracker.ui.adapter; -import android.support.v7.widget.CardView; -import android.support.v7.widget.RecyclerView; +import androidx.cardview.widget.CardView; +import androidx.recyclerview.widget.RecyclerView; import android.widget.TextView; import android.view.ViewGroup; import android.view.LayoutInflater; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/StatisticPagerAdapter.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/StatisticPagerAdapter.java index ae4f8f4..69474a5 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/StatisticPagerAdapter.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/adapter/StatisticPagerAdapter.java @@ -17,9 +17,9 @@ package org.secuso.privacyfriendlyfoodtracker.ui.adapter; import android.content.Context; -import android.support.v4.app.Fragment; -import android.support.v4.app.FragmentManager; -import android.support.v4.app.FragmentStatePagerAdapter; +import androidx.fragment.app.Fragment; +import androidx.fragment.app.FragmentManager; +import androidx.fragment.app.FragmentStatePagerAdapter; import org.secuso.privacyfriendlyfoodtracker.R; import org.secuso.privacyfriendlyfoodtracker.ui.MonthStatisticFragment; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/helper/AppCompatPreferenceActivity.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/helper/AppCompatPreferenceActivity.java index b81410b..0d24004 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/helper/AppCompatPreferenceActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/helper/AppCompatPreferenceActivity.java @@ -20,11 +20,11 @@ import android.content.res.Configuration; import android.os.Bundle; import android.preference.PreferenceActivity; -import android.support.annotation.LayoutRes; -import android.support.annotation.Nullable; -import android.support.v7.app.ActionBar; -import android.support.v7.app.AppCompatDelegate; -import android.support.v7.widget.Toolbar; +import androidx.annotation.LayoutRes; +import androidx.annotation.Nullable; +import androidx.appcompat.app.ActionBar; +import androidx.appcompat.app.AppCompatDelegate; +import androidx.appcompat.widget.Toolbar; import android.view.MenuInflater; import android.view.View; import android.view.ViewGroup; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/helper/BaseActivity.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/helper/BaseActivity.java index 58b658a..10c7a3b 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/helper/BaseActivity.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/helper/BaseActivity.java @@ -23,14 +23,14 @@ import android.os.Bundle; import android.os.Handler; import android.preference.PreferenceManager; -import android.support.design.widget.NavigationView; -import android.support.design.widget.NavigationView.OnNavigationItemSelectedListener; -import android.support.v4.app.TaskStackBuilder; -import android.support.v4.view.GravityCompat; -import android.support.v4.widget.DrawerLayout; -import android.support.v7.app.ActionBarDrawerToggle; -import android.support.v7.app.AppCompatActivity; -import android.support.v7.widget.Toolbar; +import com.google.android.material.navigation.NavigationView; +import com.google.android.material.navigation.NavigationView.OnNavigationItemSelectedListener; +import androidx.core.app.TaskStackBuilder; +import androidx.core.view.GravityCompat; +import androidx.drawerlayout.widget.DrawerLayout; +import androidx.appcompat.app.ActionBarDrawerToggle; +import androidx.appcompat.app.AppCompatActivity; +import androidx.appcompat.widget.Toolbar; import android.view.MenuItem; import android.view.View; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/viewmodels/OverviewViewModel.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/viewmodels/OverviewViewModel.java index 8f56e05..e8e1120 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/viewmodels/OverviewViewModel.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/viewmodels/OverviewViewModel.java @@ -1,23 +1,18 @@ package org.secuso.privacyfriendlyfoodtracker.ui.viewmodels; import android.app.Application; -import android.arch.lifecycle.AndroidViewModel; -import android.arch.lifecycle.LiveData; -import android.arch.lifecycle.MutableLiveData; -import android.arch.lifecycle.ViewModel; -import android.arch.lifecycle.ViewModelProvider; -import android.support.annotation.NonNull; -import android.util.Log; - -import org.secuso.privacyfriendlyfoodtracker.database.ConsumedEntries; -import org.secuso.privacyfriendlyfoodtracker.database.Product; +import androidx.lifecycle.AndroidViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; +import androidx.lifecycle.ViewModelProvider; +import androidx.annotation.NonNull; + import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseEntry; import org.secuso.privacyfriendlyfoodtracker.ui.adapter.DatabaseFacade; -import java.util.ArrayList; import java.util.Date; import java.util.List; -import java.util.concurrent.Executors; public class OverviewViewModel extends AndroidViewModel { private LiveData> list = new MutableLiveData<>(); diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/viewmodels/SharedStatisticViewModel.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/viewmodels/SharedStatisticViewModel.java index 5c3241d..1f5ece2 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/viewmodels/SharedStatisticViewModel.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/viewmodels/SharedStatisticViewModel.java @@ -16,9 +16,9 @@ */ package org.secuso.privacyfriendlyfoodtracker.ui.viewmodels; -import android.arch.lifecycle.LiveData; -import android.arch.lifecycle.MutableLiveData; -import android.arch.lifecycle.ViewModel; +import androidx.lifecycle.LiveData; +import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.ViewModel; import java.util.Calendar; import java.util.Date; diff --git a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/views/CheckableCardView.java b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/views/CheckableCardView.java index 94094ae..80fdf50 100644 --- a/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/views/CheckableCardView.java +++ b/app/src/main/java/org/secuso/privacyfriendlyfoodtracker/ui/views/CheckableCardView.java @@ -17,7 +17,7 @@ package org.secuso.privacyfriendlyfoodtracker.ui.views; import android.content.Context; -import android.support.v7.widget.CardView; +import androidx.cardview.widget.CardView; import android.widget.Checkable; /** diff --git a/app/src/main/res/layout-land/activity_about_with_navigationdrawer.xml b/app/src/main/res/layout-land/activity_about_with_navigationdrawer.xml index b681efb..40b865c 100644 --- a/app/src/main/res/layout-land/activity_about_with_navigationdrawer.xml +++ b/app/src/main/res/layout-land/activity_about_with_navigationdrawer.xml @@ -1,5 +1,5 @@ - - - - - + - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_base_statistic.xml b/app/src/main/res/layout-land/activity_base_statistic.xml index 61b8bf1..f691dd3 100644 --- a/app/src/main/res/layout-land/activity_base_statistic.xml +++ b/app/src/main/res/layout-land/activity_base_statistic.xml @@ -1,5 +1,5 @@ - android:orientation="vertical"> - android:textAlignment="center" > - + - - + - app:headerLayout="@layout/nav_header_main" app:menu="@menu/main_drawer" /> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_calendar.xml b/app/src/main/res/layout-land/activity_calendar.xml index 3ff0ee4..cae10b0 100644 --- a/app/src/main/res/layout-land/activity_calendar.xml +++ b/app/src/main/res/layout-land/activity_calendar.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_generate_key.xml b/app/src/main/res/layout-land/activity_generate_key.xml index 22daf6b..17979ea 100644 --- a/app/src/main/res/layout-land/activity_generate_key.xml +++ b/app/src/main/res/layout-land/activity_generate_key.xml @@ -108,7 +108,7 @@ android:textSize="@dimen/slide_desc"> - - - + diff --git a/app/src/main/res/layout-land/activity_help.xml b/app/src/main/res/layout-land/activity_help.xml index 240117e..bc6cb73 100644 --- a/app/src/main/res/layout-land/activity_help.xml +++ b/app/src/main/res/layout-land/activity_help.xml @@ -1,5 +1,5 @@ - - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_main.xml b/app/src/main/res/layout-land/activity_main.xml index d9ba2f0..34e4ecb 100644 --- a/app/src/main/res/layout-land/activity_main.xml +++ b/app/src/main/res/layout-land/activity_main.xml @@ -1,5 +1,5 @@ - - - + - - + diff --git a/app/src/main/res/layout-land/activity_settings.xml b/app/src/main/res/layout-land/activity_settings.xml index 3dd2f88..43b93dd 100644 --- a/app/src/main/res/layout-land/activity_settings.xml +++ b/app/src/main/res/layout-land/activity_settings.xml @@ -1,5 +1,5 @@ - - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout-land/activity_tutorial.xml b/app/src/main/res/layout-land/activity_tutorial.xml index e7ada0c..5fc8a53 100644 --- a/app/src/main/res/layout-land/activity_tutorial.xml +++ b/app/src/main/res/layout-land/activity_tutorial.xml @@ -7,7 +7,7 @@ tools:showIn="@layout/activity_tutorial"> - diff --git a/app/src/main/res/layout-land/content_food.xml b/app/src/main/res/layout-land/content_food.xml index 454523f..e266738 100644 --- a/app/src/main/res/layout-land/content_food.xml +++ b/app/src/main/res/layout-land/content_food.xml @@ -1,12 +1,12 @@ - - - - + - - - + - + - - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout-land/content_search.xml b/app/src/main/res/layout-land/content_search.xml index de0f3fb..d565f74 100644 --- a/app/src/main/res/layout-land/content_search.xml +++ b/app/src/main/res/layout-land/content_search.xml @@ -1,5 +1,5 @@ - - - + - - - + diff --git a/app/src/main/res/layout-land/dialog_edit_entry.xml b/app/src/main/res/layout-land/dialog_edit_entry.xml index 0b904c2..90e0dff 100644 --- a/app/src/main/res/layout-land/dialog_edit_entry.xml +++ b/app/src/main/res/layout-land/dialog_edit_entry.xml @@ -14,7 +14,7 @@ android:scaleType="fitCenter" app:srcCompat="@mipmap/ic__edit" android:padding="2dp"/> - - + - - - - - + - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_base_add_food.xml b/app/src/main/res/layout/activity_base_add_food.xml index fdeff4b..26ad895 100644 --- a/app/src/main/res/layout/activity_base_add_food.xml +++ b/app/src/main/res/layout/activity_base_add_food.xml @@ -1,5 +1,5 @@ - - - + - - + - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_base_statistic.xml b/app/src/main/res/layout/activity_base_statistic.xml index 61b8bf1..f691dd3 100644 --- a/app/src/main/res/layout/activity_base_statistic.xml +++ b/app/src/main/res/layout/activity_base_statistic.xml @@ -1,5 +1,5 @@ - android:orientation="vertical"> - android:textAlignment="center" > - + - - + - app:headerLayout="@layout/nav_header_main" app:menu="@menu/main_drawer" /> - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_calendar.xml b/app/src/main/res/layout/activity_calendar.xml index 3ff0ee4..cae10b0 100644 --- a/app/src/main/res/layout/activity_calendar.xml +++ b/app/src/main/res/layout/activity_calendar.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_food.xml b/app/src/main/res/layout/activity_food.xml index edb1e6f..3c8ff0a 100644 --- a/app/src/main/res/layout/activity_food.xml +++ b/app/src/main/res/layout/activity_food.xml @@ -1,28 +1,28 @@ - - - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_generate_key.xml b/app/src/main/res/layout/activity_generate_key.xml index 22daf6b..17979ea 100644 --- a/app/src/main/res/layout/activity_generate_key.xml +++ b/app/src/main/res/layout/activity_generate_key.xml @@ -108,7 +108,7 @@ android:textSize="@dimen/slide_desc"> - - - + diff --git a/app/src/main/res/layout/activity_help.xml b/app/src/main/res/layout/activity_help.xml index 240117e..bc6cb73 100644 --- a/app/src/main/res/layout/activity_help.xml +++ b/app/src/main/res/layout/activity_help.xml @@ -1,5 +1,5 @@ - - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index d9ba2f0..34e4ecb 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -1,5 +1,5 @@ - - - + - - + diff --git a/app/src/main/res/layout/activity_overview.xml b/app/src/main/res/layout/activity_overview.xml index c14ea14..c2b02d2 100644 --- a/app/src/main/res/layout/activity_overview.xml +++ b/app/src/main/res/layout/activity_overview.xml @@ -1,18 +1,18 @@ - - - - + - - - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_settings.xml b/app/src/main/res/layout/activity_settings.xml index 3dd2f88..43b93dd 100644 --- a/app/src/main/res/layout/activity_settings.xml +++ b/app/src/main/res/layout/activity_settings.xml @@ -1,5 +1,5 @@ - - - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_tutorial.xml b/app/src/main/res/layout/activity_tutorial.xml index e7ada0c..5fc8a53 100644 --- a/app/src/main/res/layout/activity_tutorial.xml +++ b/app/src/main/res/layout/activity_tutorial.xml @@ -7,7 +7,7 @@ tools:showIn="@layout/activity_tutorial"> - diff --git a/app/src/main/res/layout/content_food.xml b/app/src/main/res/layout/content_food.xml index 1e3dd5a..c75d5bb 100644 --- a/app/src/main/res/layout/content_food.xml +++ b/app/src/main/res/layout/content_food.xml @@ -1,5 +1,5 @@ - - - - + - - + - - + - + - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/content_overview.xml b/app/src/main/res/layout/content_overview.xml index afba288..c337508 100644 --- a/app/src/main/res/layout/content_overview.xml +++ b/app/src/main/res/layout/content_overview.xml @@ -1,5 +1,5 @@ - - \ No newline at end of file + \ No newline at end of file diff --git a/app/src/main/res/layout/content_search.xml b/app/src/main/res/layout/content_search.xml index de0f3fb..d565f74 100644 --- a/app/src/main/res/layout/content_search.xml +++ b/app/src/main/res/layout/content_search.xml @@ -1,5 +1,5 @@ - - - + - - - + diff --git a/app/src/main/res/layout/dialog_edit_entry.xml b/app/src/main/res/layout/dialog_edit_entry.xml index 0b904c2..90e0dff 100644 --- a/app/src/main/res/layout/dialog_edit_entry.xml +++ b/app/src/main/res/layout/dialog_edit_entry.xml @@ -14,7 +14,7 @@ android:scaleType="fitCenter" app:srcCompat="@mipmap/ic__edit" android:padding="2dp"/> - - + - - + - - + diff --git a/app/src/main/res/layout/fragment_week_statistic.xml b/app/src/main/res/layout/fragment_week_statistic.xml index dc9b9c6..3d0b91c 100644 --- a/app/src/main/res/layout/fragment_week_statistic.xml +++ b/app/src/main/res/layout/fragment_week_statistic.xml @@ -18,7 +18,7 @@ android:orientation="vertical" android:padding="@dimen/activity_horizontal_margin"> - - + - - + diff --git a/app/src/main/res/layout/search_result.xml b/app/src/main/res/layout/search_result.xml index ea177eb..1bb5bde 100644 --- a/app/src/main/res/layout/search_result.xml +++ b/app/src/main/res/layout/search_result.xml @@ -1,11 +1,11 @@ - - @@ -37,7 +37,7 @@ android:id="@+id/resultId" android:visibility="invisible" /> - + - + diff --git a/app/src/main/res/layout/toolbar.xml b/app/src/main/res/layout/toolbar.xml index def7a29..9f8bd29 100644 --- a/app/src/main/res/layout/toolbar.xml +++ b/app/src/main/res/layout/toolbar.xml @@ -1,16 +1,16 @@ - - - \ No newline at end of file + \ No newline at end of file diff --git a/build.gradle b/build.gradle index ed43547..12579e0 100644 --- a/build.gradle +++ b/build.gradle @@ -1,12 +1,15 @@ // Top-level build file where you can add configuration options common to all sub-projects/modules. buildscript { + ext.kotlin_version = '1.7.10' + repositories { - jcenter() + mavenCentral() google() } dependencies { - classpath 'com.android.tools.build:gradle:3.6.3' + classpath 'com.android.tools.build:gradle:7.2.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 @@ -15,7 +18,7 @@ buildscript { allprojects { repositories { - jcenter() + mavenCentral() google() maven { url "https://s3.amazonaws.com/repo.commonsware.com" @@ -25,9 +28,4 @@ allprojects { task clean(type: Delete) { delete rootProject.buildDir -} - -ext { - roomVersion = '1.1.1' - archLifecycleVersion = '1.1.1' } \ No newline at end of file diff --git a/gradle.properties b/gradle.properties index ccd5dda..915f0e6 100644 --- a/gradle.properties +++ b/gradle.properties @@ -16,4 +16,5 @@ # This option should only be used with decoupled projects. More details, visit # http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects # org.gradle.parallel=true +android.enableJetifier=true android.useAndroidX=true \ No newline at end of file diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index e11ae00..626b3c3 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -3,4 +3,4 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-5.6.4-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-7.3.3-all.zip diff --git a/libs/privacy-friendly-backup-api b/libs/privacy-friendly-backup-api new file mode 160000 index 0000000..af64403 --- /dev/null +++ b/libs/privacy-friendly-backup-api @@ -0,0 +1 @@ +Subproject commit af644030455d943511975c3873aefdfc2e66e018 diff --git a/settings.gradle b/settings.gradle index e7b4def..3669f94 100644 --- a/settings.gradle +++ b/settings.gradle @@ -1 +1,3 @@ include ':app' +include ':backup-api' +project(':backup-api').projectDir = new File('libs/privacy-friendly-backup-api/BackupAPI')