Merge pull request #111 from andOTP/google-backups
Allow auto backups to Google sync
This commit is contained in:
commit
cbec5458da
8 changed files with 126 additions and 11 deletions
|
@ -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"
|
||||||
|
|
|
@ -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)) {
|
||||||
|
|
|
@ -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);
|
||||||
|
}
|
||||||
|
}
|
|
@ -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();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
}
|
}
|
||||||
|
|
|
@ -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>
|
||||||
|
|
|
@ -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>
|
||||||
|
@ -165,4 +172,4 @@
|
||||||
|
|
||||||
<string name="settings_label_short_password">The password needs to be at least %1$d characters long!</string>
|
<string name="settings_label_short_password">The password needs to be at least %1$d characters long!</string>
|
||||||
<string name="settings_label_short_pin">The PIN needs to be at least %1$d digits long!</string>
|
<string name="settings_label_short_pin">The PIN needs to be at least %1$d digits long!</string>
|
||||||
</resources>
|
</resources>
|
||||||
|
|
|
@ -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"
|
||||||
|
|
Loading…
Reference in a new issue