Merge pull request #111 from andOTP/google-backups

Allow auto backups to Google sync
This commit is contained in:
Jakob Nixdorf 2018-02-21 15:32:14 +01:00 committed by GitHub
commit cbec5458da
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
8 changed files with 126 additions and 11 deletions

View file

@ -6,7 +6,9 @@
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" /> <uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<application <application
android:allowBackup="false" android:allowBackup="true"
android:backupAgent=".Utilities.BackupAgent"
android:fullBackupOnly="true"
android:icon="@mipmap/ic_launcher" android:icon="@mipmap/ic_launcher"
android:label="@string/app_name" android:label="@string/app_name"
android:supportsRtl="true" android:supportsRtl="true"

View file

@ -22,18 +22,26 @@
package org.shadowice.flocke.andotp.Activities; package org.shadowice.flocke.andotp.Activities;
import android.app.KeyguardManager;
import android.app.backup.BackupManager;
import android.app.backup.RestoreObserver;
import android.app.AlertDialog; import android.app.AlertDialog;
import android.content.DialogInterface; import android.content.DialogInterface;
import android.content.Intent; import android.content.Intent;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.os.Bundle; import android.os.Bundle;
import android.preference.CheckBoxPreference;
import android.preference.ListPreference; import android.preference.ListPreference;
import android.preference.Preference; import android.preference.Preference;
import android.preference.PreferenceCategory; import android.preference.PreferenceCategory;
import android.preference.PreferenceFragment; import android.preference.PreferenceFragment;
import android.preference.PreferenceManager; import android.preference.PreferenceManager;
import android.support.design.widget.Snackbar;
import android.support.v7.widget.Toolbar; import android.support.v7.widget.Toolbar;
import android.util.Log;
import android.view.View;
import android.view.ViewStub; import android.view.ViewStub;
import android.widget.CheckBox;
import android.widget.Toast; import android.widget.Toast;
import org.openintents.openpgp.util.OpenPgpAppPreference; import org.openintents.openpgp.util.OpenPgpAppPreference;
@ -114,10 +122,24 @@ public class SettingsActivity extends BaseActivity
} }
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) { public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
BackupManager backupManager = new BackupManager(this);
backupManager.dataChanged();
if (key.equals(getString(R.string.settings_key_theme)) || if (key.equals(getString(R.string.settings_key_theme)) ||
key.equals(getString(R.string.settings_key_locale)) || key.equals(getString(R.string.settings_key_locale)) ||
key.equals(getString(R.string.settings_key_special_features))) { key.equals(getString(R.string.settings_key_special_features))) {
recreate(); recreate();
}else if(key.equals(getString(R.string.settings_key_encryption))) {
if(settings.getEncryption() != EncryptionType.PASSWORD) {
boolean wasSyncEnabled = settings.getAndroidBackupServiceEnabled();
settings.setAndroidBackupServiceEnabled(false);
if(wasSyncEnabled) {
UIHelper.showGenericDialog(this,
R.string.settings_dialog_title_android_sync,
R.string.settings_dialog_msg_android_sync_disabled_encryption);
}
}
} }
} }
@ -207,7 +229,8 @@ public class SettingsActivity extends BaseActivity
} }
} }
public static class SettingsFragment extends PreferenceFragment { public static class SettingsFragment extends PreferenceFragment
implements SharedPreferences.OnSharedPreferenceChangeListener{
PreferenceCategory catSecurity; PreferenceCategory catSecurity;
Settings settings; Settings settings;
@ -216,6 +239,13 @@ public class SettingsActivity extends BaseActivity
OpenPgpAppPreference pgpProvider; OpenPgpAppPreference pgpProvider;
OpenPgpKeyPreference pgpKey; OpenPgpKeyPreference pgpKey;
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
CheckBoxPreference useAndroidSync = (CheckBoxPreference) findPreference(getString(R.string.settings_key_enable_android_backup_service));
useAndroidSync.setEnabled(settings.getEncryption() == EncryptionType.PASSWORD);
if(!useAndroidSync.isEnabled())
useAndroidSync.setChecked(false);
}
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState); super.onCreate(savedInstanceState);
@ -223,6 +253,7 @@ public class SettingsActivity extends BaseActivity
settings = new Settings(getActivity()); settings = new Settings(getActivity());
final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity().getBaseContext()); final SharedPreferences sharedPref = PreferenceManager.getDefaultSharedPreferences(getActivity().getBaseContext());
sharedPref.registerOnSharedPreferenceChangeListener(this);
addPreferencesFromResource(R.xml.preferences); addPreferencesFromResource(R.xml.preferences);
CredentialsPreference credentialsPreference = (CredentialsPreference) findPreference(getString(R.string.settings_key_auth)); CredentialsPreference credentialsPreference = (CredentialsPreference) findPreference(getString(R.string.settings_key_auth));
@ -251,9 +282,9 @@ public class SettingsActivity extends BaseActivity
} else { } else {
if (settings.getAuthCredentials().isEmpty()) { if (settings.getAuthCredentials().isEmpty()) {
UIHelper.showGenericDialog(getActivity(), R.string.settings_dialog_title_error, R.string.settings_dialog_msg_encryption_invalid_without_credentials); UIHelper.showGenericDialog(getActivity(), R.string.settings_dialog_title_error, R.string.settings_dialog_msg_encryption_invalid_without_credentials);
return false; return false;
}
} }
}
((SettingsActivity) getActivity()).tryEncryptionChangeWithAuth(encryptionType); ((SettingsActivity) getActivity()).tryEncryptionChangeWithAuth(encryptionType);
} else if (encryptionType == EncryptionType.KEYSTORE) { } else if (encryptionType == EncryptionType.KEYSTORE) {
@ -278,6 +309,8 @@ public class SettingsActivity extends BaseActivity
}); });
pgpKey.setDefaultUserId("Alice <alice@example.com>"); pgpKey.setDefaultUserId("Alice <alice@example.com>");
CheckBoxPreference useAndroidSync = (CheckBoxPreference) findPreference(getString(R.string.settings_key_enable_android_backup_service));
useAndroidSync.setEnabled(settings.getEncryption() == EncryptionType.PASSWORD);
if (sharedPref.contains(getString(R.string.settings_key_special_features)) && if (sharedPref.contains(getString(R.string.settings_key_special_features)) &&
sharedPref.getBoolean(getString(R.string.settings_key_special_features), false)) { sharedPref.getBoolean(getString(R.string.settings_key_special_features), false)) {

View file

@ -0,0 +1,49 @@
package org.shadowice.flocke.andotp.Utilities;
import android.app.backup.BackupAgentHelper;
import android.app.backup.BackupDataInput;
import android.app.backup.BackupDataOutput;
import android.app.backup.FileBackupHelper;
import android.app.backup.SharedPreferencesBackupHelper;
import android.os.ParcelFileDescriptor;
import java.io.IOException;
public class BackupAgent extends BackupAgentHelper {
static final String PREFS_BACKUP_KEY = "prefs";
static final String FILES_BACKUP_KEY = "files";
// PreferenceManager.getDefaultSharedPreferencesName is only available in API > 24, this is its implementation
String getDefaultSharedPreferencesName() {
return getPackageName() + "_preferences";
}
@Override
public void onBackup(ParcelFileDescriptor oldState, BackupDataOutput data, ParcelFileDescriptor newState) throws IOException {
Settings settings = new Settings(this);
if(settings.getAndroidBackupServiceEnabled()) {
synchronized (DatabaseHelper.DatabaseFileLock) {
super.onBackup(oldState, data, newState);
}
}
}
@Override
public void onRestore(BackupDataInput data, int appVersionCode, ParcelFileDescriptor newState) throws IOException {
synchronized (DatabaseHelper.DatabaseFileLock) {
super.onRestore(data, appVersionCode, newState);
}
}
@Override
public void onCreate() {
String prefs = getDefaultSharedPreferencesName();
SharedPreferencesBackupHelper sharedPreferencesBackupHelper = new SharedPreferencesBackupHelper(this, prefs);
addHelper(PREFS_BACKUP_KEY, sharedPreferencesBackupHelper);
FileBackupHelper fileBackupHelper = new FileBackupHelper(this, Constants.FILENAME_DATABASE, Constants.FILENAME_DATABASE_BACKUP);
addHelper(FILES_BACKUP_KEY, fileBackupHelper);
}
}

View file

@ -23,6 +23,7 @@
package org.shadowice.flocke.andotp.Utilities; package org.shadowice.flocke.andotp.Utilities;
import android.app.backup.BackupManager;
import android.content.Context; import android.content.Context;
import android.widget.Toast; import android.widget.Toast;
@ -42,6 +43,8 @@ import javax.crypto.SecretKey;
public class DatabaseHelper { public class DatabaseHelper {
static final Object DatabaseFileLock = new Object();
public static void wipeDatabase(Context context) { public static void wipeDatabase(Context context) {
File db = new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE); File db = new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE);
File dbBackup = new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE_BACKUP); File dbBackup = new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE_BACKUP);
@ -102,15 +105,19 @@ public class DatabaseHelper {
String jsonString = entriesToString(entries); String jsonString = entriesToString(entries);
try { try {
byte[] data = EncryptionHelper.encrypt(encryptionKey, jsonString.getBytes()); synchronized (DatabaseHelper.DatabaseFileLock) {
byte[] data = EncryptionHelper.encrypt(encryptionKey, jsonString.getBytes());
FileHelper.writeBytesToFile(new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE), data);
FileHelper.writeBytesToFile(new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE), data);
}
} catch (Exception error) { } catch (Exception error) {
error.printStackTrace(); error.printStackTrace();
return false; return false;
} }
BackupManager backupManager = new BackupManager(context);
backupManager.dataChanged();
return true; return true;
} }
@ -119,10 +126,12 @@ public class DatabaseHelper {
if (encryptionKey != null) { if (encryptionKey != null) {
try { try {
byte[] data = FileHelper.readFileToBytes(new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE)); synchronized (DatabaseHelper.DatabaseFileLock) {
data = EncryptionHelper.decrypt(encryptionKey, data); byte[] data = FileHelper.readFileToBytes(new File(context.getFilesDir() + "/" + Constants.FILENAME_DATABASE));
data = EncryptionHelper.decrypt(encryptionKey, data);
entries = stringToEntries(new String(data)); entries = stringToEntries(new String(data));
}
} catch (Exception error) { } catch (Exception error) {
error.printStackTrace(); error.printStackTrace();
} }

View file

@ -469,6 +469,14 @@ public class Settings {
setBoolean(R.string.settings_key_last_used_dialog_shown, value); setBoolean(R.string.settings_key_last_used_dialog_shown, value);
} }
public boolean getAndroidBackupServiceEnabled() {
return getBoolean(R.string.settings_key_enable_android_backup_service, false);
}
public void setAndroidBackupServiceEnabled(boolean value) {
setBoolean(R.string.settings_key_enable_android_backup_service, value);
}
public boolean getIsAppendingDateTimeToBackups() { public boolean getIsAppendingDateTimeToBackups() {
return getBoolean(R.string.settings_key_backup_append_date_time, false); return getBoolean(R.string.settings_key_backup_append_date_time, false);
} }

View file

@ -43,6 +43,7 @@
<string name="settings_key_tags_toggles" translatable="false">pref_tags_toggles</string> <string name="settings_key_tags_toggles" translatable="false">pref_tags_toggles</string>
<string name="settings_key_enable_screenshot" translatable="false">pref_enable_screenshot</string> <string name="settings_key_enable_screenshot" translatable="false">pref_enable_screenshot</string>
<string name="settings_key_enable_android_backup_service" translatable="false">pref_enable_android_backup_service</string>
<string name="settings_key_clear_keystore" translatable="false">pref_clear_keystore</string> <string name="settings_key_clear_keystore" translatable="false">pref_clear_keystore</string>
<string name="settings_key_last_used_dialog_shown" translatable="false">pref_last_used_dialog_shown</string> <string name="settings_key_last_used_dialog_shown" translatable="false">pref_last_used_dialog_shown</string>

View file

@ -35,6 +35,7 @@
<string name="settings_title_special_features">Enable special features</string> <string name="settings_title_special_features">Enable special features</string>
<string name="settings_title_enable_screenshot">Enable screenshots</string> <string name="settings_title_enable_screenshot">Enable screenshots</string>
<string name="settings_title_enable_android_backup_service">Enable android sync</string>
<string name="settings_title_clear_keystore">Clear KeyStore</string> <string name="settings_title_clear_keystore">Clear KeyStore</string>
<!-- Descriptions --> <!-- Descriptions -->
@ -64,6 +65,8 @@
<string name="settings_desc_special_features">Uncheck to disable the special features again</string> <string name="settings_desc_special_features">Uncheck to disable the special features again</string>
<string name="settings_desc_enable_screenshot">Allow to take screenshots of the main screen <string name="settings_desc_enable_screenshot">Allow to take screenshots of the main screen
(disabled by default for security reasons)</string> (disabled by default for security reasons)</string>
<string name="settings_desc_enable_android_backup_service">Enables andOTP to use android\'s
built in backup service to bacup keys and preferences</string>
<string name="settings_desc_clear_keystore">Delete the encryption key from the KeyStore</string> <string name="settings_desc_clear_keystore">Delete the encryption key from the KeyStore</string>
<!-- Toasts --> <!-- Toasts -->
@ -91,6 +94,7 @@
<string name="settings_dialog_title_error">Error</string> <string name="settings_dialog_title_error">Error</string>
<string name="settings_dialog_title_clear_keystore">Clear the KeyStore?</string> <string name="settings_dialog_title_clear_keystore">Clear the KeyStore?</string>
<string name="settings_dialog_title_android_sync">Android sync</string>
<string name="settings_dialog_msg_auth_invalid_with_encryption">You can only use Password or PIN as <string name="settings_dialog_msg_auth_invalid_with_encryption">You can only use Password or PIN as
long as the database encryption is set to \"Password / PIN\"!</string> long as the database encryption is set to \"Password / PIN\"!</string>
@ -110,6 +114,9 @@
your accounts. Make sure you have a backup!\n\n<b>Are you really sure you want to clear the your accounts. Make sure you have a backup!\n\n<b>Are you really sure you want to clear the
KeyStore?</b></string> KeyStore?</b></string>
<string name="settings_dialog_msg_android_sync_disabled_encryption">Android sync can not be used with keystore
encryption, <b>you should perform a manual backup!</b></string>
<!-- List entries --> <!-- List entries -->
<string-array name="settings_entries_auth"> <string-array name="settings_entries_auth">
<item>None</item> <item>None</item>

View file

@ -104,6 +104,12 @@
<PreferenceCategory <PreferenceCategory
android:title="@string/settings_category_title_backup"> android:title="@string/settings_category_title_backup">
<CheckBoxPreference
android:key="@string/settings_key_enable_android_backup_service"
android:title="@string/settings_title_enable_android_backup_service"
android:summary="@string/settings_desc_enable_android_backup_service"
android:defaultValue="false" />
<CheckBoxPreference <CheckBoxPreference
android:key="@string/settings_key_backup_append_date_time" android:key="@string/settings_key_backup_append_date_time"
android:title="@string/settings_title_backup_append_date" android:title="@string/settings_title_backup_append_date"