parent
8effb9adf6
commit
18e9c4b69f
7 changed files with 239 additions and 57 deletions
|
@ -49,6 +49,7 @@ import org.openintents.openpgp.OpenPgpSignatureResult;
|
||||||
import org.openintents.openpgp.util.OpenPgpApi;
|
import org.openintents.openpgp.util.OpenPgpApi;
|
||||||
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
||||||
import org.shadowice.flocke.andotp.Database.Entry;
|
import org.shadowice.flocke.andotp.Database.Entry;
|
||||||
|
import org.shadowice.flocke.andotp.Dialogs.PasswordEntryDialog;
|
||||||
import org.shadowice.flocke.andotp.R;
|
import org.shadowice.flocke.andotp.R;
|
||||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||||
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
|
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
|
||||||
|
@ -122,12 +123,8 @@ public class BackupActivity extends BaseActivity {
|
||||||
|
|
||||||
if (settings.getBackupPasswordEnc().isEmpty()) {
|
if (settings.getBackupPasswordEnc().isEmpty()) {
|
||||||
cryptSetup.setVisibility(View.VISIBLE);
|
cryptSetup.setVisibility(View.VISIBLE);
|
||||||
backupCrypt.setVisibility(View.GONE);
|
|
||||||
restoreCrypt.setVisibility(View.GONE);
|
|
||||||
} else {
|
} else {
|
||||||
cryptSetup.setVisibility(View.GONE);
|
cryptSetup.setVisibility(View.GONE);
|
||||||
backupCrypt.setVisibility(View.VISIBLE);
|
|
||||||
restoreCrypt.setVisibility(View.VISIBLE);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
backupCrypt.setOnClickListener(new View.OnClickListener() {
|
backupCrypt.setOnClickListener(new View.OnClickListener() {
|
||||||
|
@ -418,10 +415,23 @@ public class BackupActivity extends BaseActivity {
|
||||||
|
|
||||||
/* Encrypted backup functions */
|
/* Encrypted backup functions */
|
||||||
|
|
||||||
private void doRestoreCrypt(Uri uri) {
|
private void doRestoreCrypt(final Uri uri) {
|
||||||
String password = settings.getBackupPasswordEnc();
|
String password = settings.getBackupPasswordEnc();
|
||||||
|
|
||||||
if (! password.isEmpty()) {
|
if (password.isEmpty()) {
|
||||||
|
PasswordEntryDialog pwDialog = new PasswordEntryDialog(this, PasswordEntryDialog.Mode.ENTER, new PasswordEntryDialog.PasswordEnteredCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPasswordEntered(String newPassword) {
|
||||||
|
doRestoreCryptWithPassword(uri, newPassword);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pwDialog.show();
|
||||||
|
} else {
|
||||||
|
doRestoreCryptWithPassword(uri, password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doRestoreCryptWithPassword(Uri uri, String password) {
|
||||||
if (Tools.isExternalStorageReadable()) {
|
if (Tools.isExternalStorageReadable()) {
|
||||||
boolean success = true;
|
boolean success = true;
|
||||||
String decryptedString = "";
|
String decryptedString = "";
|
||||||
|
@ -446,15 +456,25 @@ public class BackupActivity extends BaseActivity {
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Toast.makeText(this, R.string.backup_toast_crypt_password_not_set, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private void doBackupCrypt(Uri uri) {
|
private void doBackupCrypt(final Uri uri) {
|
||||||
String password = settings.getBackupPasswordEnc();
|
String password = settings.getBackupPasswordEnc();
|
||||||
|
|
||||||
if (! password.isEmpty()) {
|
if (password.isEmpty()) {
|
||||||
|
PasswordEntryDialog pwDialog = new PasswordEntryDialog(this, PasswordEntryDialog.Mode.UPDATE, new PasswordEntryDialog.PasswordEnteredCallback() {
|
||||||
|
@Override
|
||||||
|
public void onPasswordEntered(String newPassword) {
|
||||||
|
doBackupCryptWithPassword(uri, newPassword);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
pwDialog.show();
|
||||||
|
} else {
|
||||||
|
doBackupCryptWithPassword(uri, password);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void doBackupCryptWithPassword(Uri uri, String password) {
|
||||||
if (Tools.isExternalStorageWritable()) {
|
if (Tools.isExternalStorageWritable()) {
|
||||||
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(this, encryptionKey);
|
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(this, encryptionKey);
|
||||||
String plain = DatabaseHelper.entriesToString(entries);
|
String plain = DatabaseHelper.entriesToString(entries);
|
||||||
|
@ -479,9 +499,6 @@ public class BackupActivity extends BaseActivity {
|
||||||
} else {
|
} else {
|
||||||
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
|
Toast.makeText(this, R.string.backup_toast_storage_not_accessible, Toast.LENGTH_LONG).show();
|
||||||
}
|
}
|
||||||
} else {
|
|
||||||
Toast.makeText(this, R.string.backup_toast_crypt_password_not_set, Toast.LENGTH_LONG).show();
|
|
||||||
}
|
|
||||||
|
|
||||||
finishWithResult();
|
finishWithResult();
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,80 @@
|
||||||
|
package org.shadowice.flocke.andotp.Dialogs;
|
||||||
|
|
||||||
|
import android.content.Context;
|
||||||
|
import android.support.design.widget.TextInputEditText;
|
||||||
|
import android.support.v7.app.AppCompatDialog;
|
||||||
|
import android.text.Editable;
|
||||||
|
import android.text.TextUtils;
|
||||||
|
import android.text.TextWatcher;
|
||||||
|
import android.view.View;
|
||||||
|
import android.widget.Button;
|
||||||
|
import android.widget.EditText;
|
||||||
|
|
||||||
|
import org.shadowice.flocke.andotp.R;
|
||||||
|
import org.shadowice.flocke.andotp.Utilities.Tools;
|
||||||
|
|
||||||
|
public class PasswordEntryDialog extends AppCompatDialog
|
||||||
|
implements View.OnClickListener, TextWatcher {
|
||||||
|
|
||||||
|
public enum Mode { ENTER, UPDATE }
|
||||||
|
|
||||||
|
public interface PasswordEnteredCallback {
|
||||||
|
void onPasswordEntered(String newPassword);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Mode dialogMode;
|
||||||
|
private PasswordEnteredCallback callback;
|
||||||
|
|
||||||
|
private TextInputEditText passwordInput;
|
||||||
|
private EditText passwordConfirm;
|
||||||
|
private Button okButton;
|
||||||
|
|
||||||
|
public PasswordEntryDialog(Context context, Mode newMode, PasswordEnteredCallback newCallback) {
|
||||||
|
super(context, Tools.getThemeResource(context, R.attr.dialogTheme));
|
||||||
|
|
||||||
|
setTitle(R.string.dialog_title_enter_password);
|
||||||
|
setContentView(R.layout.dialog_password_entry);
|
||||||
|
|
||||||
|
passwordInput = findViewById(R.id.passwordInput);
|
||||||
|
passwordConfirm = findViewById(R.id.passwordConfirm);
|
||||||
|
|
||||||
|
okButton = findViewById(R.id.buttonOk);
|
||||||
|
Button cancelButton = findViewById(R.id.buttonCancel);
|
||||||
|
|
||||||
|
okButton.setOnClickListener(this);
|
||||||
|
cancelButton.setOnClickListener(this);
|
||||||
|
|
||||||
|
this.callback = newCallback;
|
||||||
|
|
||||||
|
this.dialogMode = newMode;
|
||||||
|
|
||||||
|
if (this.dialogMode == Mode.UPDATE) {
|
||||||
|
passwordConfirm.setVisibility(View.VISIBLE);
|
||||||
|
passwordInput.addTextChangedListener(this);
|
||||||
|
passwordConfirm.addTextChangedListener(this);
|
||||||
|
} else if (this.dialogMode == Mode.ENTER) {
|
||||||
|
passwordConfirm.setVisibility(View.GONE);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// TextWatcher
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
if (TextUtils.equals(passwordInput.getEditableText(), passwordConfirm.getEditableText()))
|
||||||
|
okButton.setEnabled(true);
|
||||||
|
else
|
||||||
|
okButton.setEnabled(false);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void afterTextChanged(Editable s) {}
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {}
|
||||||
|
|
||||||
|
// View.OnClickListener
|
||||||
|
public void onClick(View view) {
|
||||||
|
if (view.getId() == R.id.buttonOk) {
|
||||||
|
if (callback != null)
|
||||||
|
callback.onPasswordEntered(passwordInput.getText().toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
dismiss();
|
||||||
|
}
|
||||||
|
}
|
|
@ -25,14 +25,12 @@ package org.shadowice.flocke.andotp.Utilities;
|
||||||
import android.content.Context;
|
import android.content.Context;
|
||||||
import android.content.res.Resources;
|
import android.content.res.Resources;
|
||||||
import android.content.res.TypedArray;
|
import android.content.res.TypedArray;
|
||||||
import android.graphics.Color;
|
|
||||||
import android.graphics.ColorFilter;
|
import android.graphics.ColorFilter;
|
||||||
import android.graphics.PorterDuff;
|
import android.graphics.PorterDuff;
|
||||||
import android.graphics.PorterDuffColorFilter;
|
import android.graphics.PorterDuffColorFilter;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Build;
|
import android.os.Build;
|
||||||
import android.os.Environment;
|
import android.os.Environment;
|
||||||
import android.support.annotation.ColorInt;
|
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
import java.text.DateFormat;
|
import java.text.DateFormat;
|
||||||
|
@ -67,6 +65,16 @@ public class Tools {
|
||||||
return colorValue;
|
return colorValue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static int getThemeResource(Context context, int styleAttr) {
|
||||||
|
Resources.Theme theme = context.getTheme();
|
||||||
|
TypedArray arr = theme.obtainStyledAttributes(new int[]{styleAttr});
|
||||||
|
|
||||||
|
int styleValue = arr.getResourceId(0, -1);
|
||||||
|
arr.recycle();
|
||||||
|
|
||||||
|
return styleValue;
|
||||||
|
}
|
||||||
|
|
||||||
/* Create a ColorFilter based on the current theme */
|
/* Create a ColorFilter based on the current theme */
|
||||||
public static ColorFilter getThemeColorFilter(Context context, int colorAttr) {
|
public static ColorFilter getThemeColorFilter(Context context, int colorAttr) {
|
||||||
return new PorterDuffColorFilter(getThemeColor(context, colorAttr), PorterDuff.Mode.SRC_IN);
|
return new PorterDuffColorFilter(getThemeColor(context, colorAttr), PorterDuff.Mode.SRC_IN);
|
||||||
|
|
59
app/src/main/res/layout/dialog_password_entry.xml
Normal file
59
app/src/main/res/layout/dialog_password_entry.xml
Normal file
|
@ -0,0 +1,59 @@
|
||||||
|
<?xml version="1.0" encoding="utf-8"?>
|
||||||
|
<LinearLayout
|
||||||
|
xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
|
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin"
|
||||||
|
android:layout_marginStart="@dimen/activity_margin_small"
|
||||||
|
android:layout_marginEnd="@dimen/activity_margin_small"
|
||||||
|
android:orientation="vertical"
|
||||||
|
android:background="@null"
|
||||||
|
android:padding="@dimen/activity_margin">
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputLayout
|
||||||
|
android:id="@+id/passwordInputLayout"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/dialog_label_enter_password"
|
||||||
|
app:passwordToggleEnabled="true" >
|
||||||
|
|
||||||
|
<android.support.design.widget.TextInputEditText
|
||||||
|
android:id="@+id/passwordInput"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="match_parent"
|
||||||
|
android:inputType="textPassword" />
|
||||||
|
|
||||||
|
</android.support.design.widget.TextInputLayout>
|
||||||
|
|
||||||
|
<EditText
|
||||||
|
android:id="@+id/passwordConfirm"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:hint="@string/dialog_label_confirm_password"
|
||||||
|
android:inputType="textPassword" />
|
||||||
|
|
||||||
|
<LinearLayout
|
||||||
|
android:orientation="horizontal"
|
||||||
|
android:layout_width="match_parent"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
android:layout_marginTop="@dimen/activity_margin"
|
||||||
|
android:gravity="end">
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonCancel"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"
|
||||||
|
android:text="@android:string/cancel"/>
|
||||||
|
|
||||||
|
<Button
|
||||||
|
android:id="@+id/buttonOk"
|
||||||
|
android:layout_width="wrap_content"
|
||||||
|
android:layout_height="wrap_content"
|
||||||
|
style="@style/Widget.AppCompat.Button.ButtonBar.AlertDialog"
|
||||||
|
android:text="@android:string/ok" />
|
||||||
|
|
||||||
|
</LinearLayout>
|
||||||
|
|
||||||
|
</LinearLayout>
|
|
@ -22,8 +22,9 @@
|
||||||
<string name="backup_desc_import_crypt">Restore accounts from a password-protected JSON file</string>
|
<string name="backup_desc_import_crypt">Restore accounts from a password-protected JSON file</string>
|
||||||
<string name="backup_desc_import_openpgp">Restore accounts from an OpenPGP-encrypted JSON file</string>
|
<string name="backup_desc_import_openpgp">Restore accounts from an OpenPGP-encrypted JSON file</string>
|
||||||
|
|
||||||
<string name="backup_desc_crypt_setup">You need to set a <b>Backup password</b> in the
|
<string name="backup_desc_crypt_setup">Failed to load the backup password from the <b>Settings</b>,
|
||||||
<b>Settings</b> to use encrypted backups.
|
this either means no password was set or something went wrong. You will be asked to enter
|
||||||
|
the password manually when creating or importing a backup.
|
||||||
</string>
|
</string>
|
||||||
|
|
||||||
<string name="backup_desc_openpgp_provider">You need to install an OpenPGP provider and enable
|
<string name="backup_desc_openpgp_provider">You need to install an OpenPGP provider and enable
|
||||||
|
|
|
@ -69,6 +69,11 @@
|
||||||
<string name="dialog_title_last_used">Last used</string>
|
<string name="dialog_title_last_used">Last used</string>
|
||||||
<string name="dialog_title_keystore_error">KeyStore error</string>
|
<string name="dialog_title_keystore_error">KeyStore error</string>
|
||||||
|
|
||||||
|
<string name="dialog_title_enter_password">Enter password</string>
|
||||||
|
|
||||||
|
<string name="dialog_label_enter_password">Enter password</string>
|
||||||
|
<string name="dialog_label_confirm_password">Confirm password</string>
|
||||||
|
|
||||||
<string name="dialog_msg_auth">Please enter your device credentials to start andOTP.</string>
|
<string name="dialog_msg_auth">Please enter your device credentials to start andOTP.</string>
|
||||||
<string name="dialog_msg_confirm_delete">Are you sure you want do remove the account \"%1$s\"?</string>
|
<string name="dialog_msg_confirm_delete">Are you sure you want do remove the account \"%1$s\"?</string>
|
||||||
|
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
<!-- Definitions -->
|
<!-- Definitions -->
|
||||||
<attr name="windowBackground" format="reference" />
|
<attr name="windowBackground" format="reference" />
|
||||||
<attr name="cardStyle" format="reference" />
|
<attr name="cardStyle" format="reference" />
|
||||||
|
<attr name="dialogTheme" format="reference" />
|
||||||
|
|
||||||
<attr name="colorGithub" format="reference" />
|
<attr name="colorGithub" format="reference" />
|
||||||
<attr name="colorPaypal" format="reference" />
|
<attr name="colorPaypal" format="reference" />
|
||||||
|
@ -27,6 +28,7 @@
|
||||||
<item name="thumbnailBackground">@android:color/transparent</item>
|
<item name="thumbnailBackground">@android:color/transparent</item>
|
||||||
|
|
||||||
<item name="cardStyle">@style/CardViewStyle</item>
|
<item name="cardStyle">@style/CardViewStyle</item>
|
||||||
|
<item name="dialogTheme">@style/DialogTheme</item>
|
||||||
|
|
||||||
<item name="about_libraries_card">@color/cardview_light_background</item>
|
<item name="about_libraries_card">@color/cardview_light_background</item>
|
||||||
<item name="about_libraries_title_openSource">@color/about_libraries_text_openSource</item>
|
<item name="about_libraries_title_openSource">@color/about_libraries_text_openSource</item>
|
||||||
|
@ -44,6 +46,10 @@
|
||||||
<item name="contentPadding">@dimen/activity_margin</item>
|
<item name="contentPadding">@dimen/activity_margin</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="DialogTheme" parent="Theme.AppCompat.Light.Dialog.MinWidth">
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<!-- Dark application theme. -->
|
<!-- Dark application theme. -->
|
||||||
<style name="AppTheme.Dark" parent="Theme.AppCompat">
|
<style name="AppTheme.Dark" parent="Theme.AppCompat">
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
@ -57,6 +63,7 @@
|
||||||
<item name="colorPaypal">@color/paypal_light</item>
|
<item name="colorPaypal">@color/paypal_light</item>
|
||||||
|
|
||||||
<item name="cardStyle">@style/CardViewStyle</item>
|
<item name="cardStyle">@style/CardViewStyle</item>
|
||||||
|
<item name="dialogTheme">@style/DialogTheme.Dark</item>
|
||||||
|
|
||||||
<item name="about_libraries_card">@color/cardview_dark_background</item>
|
<item name="about_libraries_card">@color/cardview_dark_background</item>
|
||||||
<item name="about_libraries_title_openSource">@color/about_libraries_text_openSource_dark</item>
|
<item name="about_libraries_title_openSource">@color/about_libraries_text_openSource_dark</item>
|
||||||
|
@ -70,6 +77,10 @@
|
||||||
<item name="windowNoTitle">true</item>
|
<item name="windowNoTitle">true</item>
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style name="DialogTheme.Dark" parent="Theme.AppCompat.Dialog.MinWidth">
|
||||||
|
<item name="colorAccent">@color/colorAccent</item>
|
||||||
|
</style>
|
||||||
|
|
||||||
<!-- Black application theme. -->
|
<!-- Black application theme. -->
|
||||||
<style name="AppTheme.Black" parent="Theme.AppCompat">
|
<style name="AppTheme.Black" parent="Theme.AppCompat">
|
||||||
<item name="colorPrimary">@color/colorPrimary</item>
|
<item name="colorPrimary">@color/colorPrimary</item>
|
||||||
|
@ -83,6 +94,7 @@
|
||||||
<item name="colorPaypal">@color/paypal_light</item>
|
<item name="colorPaypal">@color/paypal_light</item>
|
||||||
|
|
||||||
<item name="cardStyle">@style/CardViewStyle.Black</item>
|
<item name="cardStyle">@style/CardViewStyle.Black</item>
|
||||||
|
<item name="dialogTheme">@style/DialogTheme.Dark</item>
|
||||||
|
|
||||||
<item name="about_libraries_card">@android:color/black</item>
|
<item name="about_libraries_card">@android:color/black</item>
|
||||||
<item name="about_libraries_title_openSource">@color/about_libraries_text_openSource_dark</item>
|
<item name="about_libraries_title_openSource">@color/about_libraries_text_openSource_dark</item>
|
||||||
|
|
Loading…
Reference in a new issue