Merge branch 'master' into AuthenticationActivity-is-missing-secure-flag-and-can-be-screencaptured-#378
This commit is contained in:
commit
d6edb612e5
34 changed files with 464 additions and 192 deletions
|
@ -54,6 +54,7 @@
|
|||
android:theme="@style/AppTheme.NoActionBar" />
|
||||
<activity
|
||||
android:name="com.journeyapps.barcodescanner.CaptureActivity"
|
||||
android:hardwareAccelerated="true"
|
||||
android:screenOrientation="fullSensor"
|
||||
tools:replace="screenOrientation" />
|
||||
<activity android:name=".Activities.SecureCaptureActivity" />
|
||||
|
|
|
@ -36,7 +36,6 @@ import android.support.v4.app.ActivityCompat;
|
|||
import android.support.v4.content.ContextCompat;
|
||||
import android.support.v7.widget.Toolbar;
|
||||
import android.text.TextUtils;
|
||||
import android.util.Log;
|
||||
import android.view.View;
|
||||
import android.view.ViewStub;
|
||||
import android.widget.LinearLayout;
|
||||
|
@ -51,17 +50,20 @@ import org.openintents.openpgp.util.OpenPgpServiceConnection;
|
|||
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.Utilities.BackupHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.FileHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.StorageAccessHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Tools;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.InputStream;
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Arrays;
|
||||
|
||||
import javax.crypto.SecretKey;
|
||||
|
||||
|
@ -120,6 +122,7 @@ public class BackupActivity extends BaseActivity {
|
|||
TextView cryptSetup = v.findViewById(R.id.msg_crypt_setup);
|
||||
LinearLayout backupCrypt = v.findViewById(R.id.button_backup_crypt);
|
||||
LinearLayout restoreCrypt = v.findViewById(R.id.button_restore_crypt);
|
||||
LinearLayout restoreCryptOld = v.findViewById(R.id.button_restore_crypt_old);
|
||||
|
||||
if (settings.getBackupPasswordEnc().isEmpty()) {
|
||||
cryptSetup.setVisibility(View.VISIBLE);
|
||||
|
@ -141,6 +144,13 @@ public class BackupActivity extends BaseActivity {
|
|||
}
|
||||
});
|
||||
|
||||
restoreCryptOld.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
openFileWithPermissions(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT_OLD, Constants.PERMISSIONS_BACKUP_READ_IMPORT_CRYPT_OLD);
|
||||
}
|
||||
});
|
||||
|
||||
// OpenPGP
|
||||
|
||||
String PGPProvider = settings.getOpenPGPProvider();
|
||||
|
@ -179,6 +189,23 @@ public class BackupActivity extends BaseActivity {
|
|||
|
||||
replace = v.findViewById(R.id.backup_replace);
|
||||
|
||||
if (! settings.getNewBackupFormatDialogShown()) {
|
||||
showNewBackupInfo();
|
||||
}
|
||||
}
|
||||
|
||||
private void showNewBackupInfo() {
|
||||
AlertDialog.Builder builder = new AlertDialog.Builder(this);
|
||||
builder.setTitle(R.string.backup_new_format_dialog_title)
|
||||
.setMessage(R.string.backup_new_format_dialog_msg)
|
||||
.setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(DialogInterface dialogInterface, int i) {
|
||||
settings.setNewBackupFormatDialogShown(true);
|
||||
}
|
||||
})
|
||||
.create()
|
||||
.show();
|
||||
}
|
||||
|
||||
// End with a result
|
||||
|
@ -212,7 +239,7 @@ public class BackupActivity extends BaseActivity {
|
|||
|
||||
// Get the result from permission requests
|
||||
@Override
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String permissions[], @NonNull int[] grantResults) {
|
||||
public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions, @NonNull int[] grantResults) {
|
||||
if (requestCode == Constants.PERMISSIONS_BACKUP_READ_IMPORT_PLAIN) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_PLAIN);
|
||||
|
@ -231,6 +258,12 @@ public class BackupActivity extends BaseActivity {
|
|||
} else {
|
||||
Toast.makeText(this, R.string.backup_toast_storage_permissions, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
} else if (requestCode == Constants.PERMISSIONS_BACKUP_READ_IMPORT_CRYPT_OLD) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
showOpenFileSelector(Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT_OLD);
|
||||
} else {
|
||||
Toast.makeText(this, R.string.backup_toast_storage_permissions, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
} else if (requestCode == Constants.PERMISSIONS_BACKUP_WRITE_EXPORT_CRYPT) {
|
||||
if (grantResults.length > 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) {
|
||||
showSaveFileSelector(Constants.BACKUP_MIMETYPE_CRYPT, Constants.BackupType.ENCRYPTED, Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT);
|
||||
|
@ -269,7 +302,11 @@ public class BackupActivity extends BaseActivity {
|
|||
}
|
||||
} else if (requestCode == Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT && resultCode == RESULT_OK) {
|
||||
if (intent != null) {
|
||||
doRestoreCrypt(intent.getData());
|
||||
doRestoreCrypt(intent.getData(), false);
|
||||
}
|
||||
} else if (requestCode == Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT_OLD && resultCode == RESULT_OK) {
|
||||
if (intent != null) {
|
||||
doRestoreCrypt(intent.getData(), true);
|
||||
}
|
||||
} else if (requestCode == Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT && resultCode == RESULT_OK) {
|
||||
if (intent != null) {
|
||||
|
@ -300,7 +337,9 @@ public class BackupActivity extends BaseActivity {
|
|||
if (intentId == Constants.INTENT_BACKUP_OPEN_DOCUMENT_PLAIN)
|
||||
doRestorePlain(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_PLAIN));
|
||||
else if (intentId == Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT)
|
||||
doRestoreCrypt(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_CRYPT));
|
||||
doRestoreCrypt(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_CRYPT), false);
|
||||
else if (intentId == Constants.INTENT_BACKUP_OPEN_DOCUMENT_CRYPT_OLD)
|
||||
doRestoreCrypt(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_CRYPT), true);
|
||||
else if (intentId == Constants.INTENT_BACKUP_OPEN_DOCUMENT_PGP)
|
||||
restoreEncryptedWithPGP(Tools.buildUri(settings.getBackupDir(), Constants.BACKUP_FILENAME_PGP), null);
|
||||
}
|
||||
|
@ -311,16 +350,16 @@ public class BackupActivity extends BaseActivity {
|
|||
Intent intent = new Intent(Intent.ACTION_CREATE_DOCUMENT);
|
||||
intent.addCategory(Intent.CATEGORY_OPENABLE);
|
||||
intent.setType(mimeType);
|
||||
intent.putExtra(Intent.EXTRA_TITLE, FileHelper.backupFilename(this, backupType));
|
||||
intent.putExtra(Intent.EXTRA_TITLE, BackupHelper.backupFilename(this, backupType));
|
||||
startActivityForResult(intent, intentId);
|
||||
} else {
|
||||
if (Tools.mkdir(settings.getBackupDir())) {
|
||||
if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_PLAIN)
|
||||
doBackupPlain(Tools.buildUri(settings.getBackupDir(), FileHelper.backupFilename(this, Constants.BackupType.PLAIN_TEXT)));
|
||||
doBackupPlain(Tools.buildUri(settings.getBackupDir(), BackupHelper.backupFilename(this, Constants.BackupType.PLAIN_TEXT)));
|
||||
else if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_CRYPT)
|
||||
doBackupCrypt(Tools.buildUri(settings.getBackupDir(), FileHelper.backupFilename(this, Constants.BackupType.ENCRYPTED)));
|
||||
doBackupCrypt(Tools.buildUri(settings.getBackupDir(), BackupHelper.backupFilename(this, Constants.BackupType.ENCRYPTED)));
|
||||
else if (intentId == Constants.INTENT_BACKUP_SAVE_DOCUMENT_PGP)
|
||||
backupEncryptedWithPGP(Tools.buildUri(settings.getBackupDir(), FileHelper.backupFilename(this, Constants.BackupType.OPEN_PGP)), null);
|
||||
backupEncryptedWithPGP(Tools.buildUri(settings.getBackupDir(), BackupHelper.backupFilename(this, Constants.BackupType.OPEN_PGP)), null);
|
||||
} else {
|
||||
Toast.makeText(this, R.string.backup_toast_mkdir_failed, Toast.LENGTH_LONG).show();
|
||||
}
|
||||
|
@ -370,7 +409,7 @@ public class BackupActivity extends BaseActivity {
|
|||
|
||||
private void doRestorePlain(Uri uri) {
|
||||
if (Tools.isExternalStorageReadable()) {
|
||||
String content = FileHelper.readFileToString(this, uri);
|
||||
String content = StorageAccessHelper.loadFileString(this, uri);
|
||||
|
||||
restoreEntries(content);
|
||||
} else {
|
||||
|
@ -382,7 +421,7 @@ public class BackupActivity extends BaseActivity {
|
|||
if (Tools.isExternalStorageWritable()) {
|
||||
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(this, encryptionKey);
|
||||
|
||||
if (FileHelper.writeStringToFile(this, uri, DatabaseHelper.entriesToString(entries)))
|
||||
if (StorageAccessHelper.saveFile(this, uri, DatabaseHelper.entriesToString(entries)))
|
||||
Toast.makeText(this, R.string.backup_toast_export_success, Toast.LENGTH_LONG).show();
|
||||
else
|
||||
Toast.makeText(this, R.string.backup_toast_export_failed, Toast.LENGTH_LONG).show();
|
||||
|
@ -415,34 +454,47 @@ public class BackupActivity extends BaseActivity {
|
|||
|
||||
/* Encrypted backup functions */
|
||||
|
||||
private void doRestoreCrypt(final Uri uri) {
|
||||
private void doRestoreCrypt(final Uri uri, final boolean old_format) {
|
||||
String password = settings.getBackupPasswordEnc();
|
||||
|
||||
if (password.isEmpty()) {
|
||||
PasswordEntryDialog pwDialog = new PasswordEntryDialog(this, PasswordEntryDialog.Mode.ENTER, new PasswordEntryDialog.PasswordEnteredCallback() {
|
||||
@Override
|
||||
public void onPasswordEntered(String newPassword) {
|
||||
doRestoreCryptWithPassword(uri, newPassword);
|
||||
doRestoreCryptWithPassword(uri, newPassword, old_format);
|
||||
}
|
||||
});
|
||||
pwDialog.show();
|
||||
} else {
|
||||
doRestoreCryptWithPassword(uri, password);
|
||||
doRestoreCryptWithPassword(uri, password, old_format);
|
||||
}
|
||||
}
|
||||
|
||||
private void doRestoreCryptWithPassword(Uri uri, String password) {
|
||||
private void doRestoreCryptWithPassword(Uri uri, String password, boolean old_format) {
|
||||
if (Tools.isExternalStorageReadable()) {
|
||||
boolean success = true;
|
||||
String decryptedString = "";
|
||||
|
||||
try {
|
||||
byte[] encrypted = FileHelper.readFileToBytes(this, uri);
|
||||
byte[] data = StorageAccessHelper.loadFile(this, uri);
|
||||
|
||||
if (old_format) {
|
||||
SecretKey key = EncryptionHelper.generateSymmetricKeyFromPassword(password);
|
||||
byte[] decrypted = EncryptionHelper.decrypt(key, encrypted);
|
||||
byte[] decrypted = EncryptionHelper.decrypt(key, data);
|
||||
|
||||
decryptedString = new String(decrypted, StandardCharsets.UTF_8);
|
||||
} else {
|
||||
byte[] iterBytes = Arrays.copyOfRange(data, 0, Constants.INT_LENGTH);
|
||||
byte[] salt = Arrays.copyOfRange(data, Constants.INT_LENGTH, Constants.INT_LENGTH + Constants.ENCRYPTION_IV_LENGTH);
|
||||
byte[] encrypted = Arrays.copyOfRange(data, Constants.INT_LENGTH + Constants.ENCRYPTION_IV_LENGTH, data.length);
|
||||
|
||||
int iter = ByteBuffer.wrap(iterBytes).getInt();
|
||||
|
||||
SecretKey key = EncryptionHelper.generateSymmetricKeyPBKDF2(password, iter, salt);
|
||||
|
||||
byte[] decrypted = EncryptionHelper.decrypt(key, encrypted);
|
||||
decryptedString = new String(decrypted, StandardCharsets.UTF_8);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
success = false;
|
||||
e.printStackTrace();
|
||||
|
@ -482,10 +534,20 @@ public class BackupActivity extends BaseActivity {
|
|||
boolean success = true;
|
||||
|
||||
try {
|
||||
SecretKey key = EncryptionHelper.generateSymmetricKeyFromPassword(password);
|
||||
int iter = EncryptionHelper.generateRandomIterations();
|
||||
byte[] salt = EncryptionHelper.generateRandom(Constants.ENCRYPTION_IV_LENGTH);
|
||||
|
||||
SecretKey key = EncryptionHelper.generateSymmetricKeyPBKDF2(password, iter, salt);
|
||||
byte[] encrypted = EncryptionHelper.encrypt(key, plain.getBytes(StandardCharsets.UTF_8));
|
||||
|
||||
FileHelper.writeBytesToFile(this, uri, encrypted);
|
||||
byte[] iterBytes = ByteBuffer.allocate(Constants.INT_LENGTH).putInt(iter).array();
|
||||
byte[] data = new byte[Constants.INT_LENGTH + Constants.ENCRYPTION_IV_LENGTH + encrypted.length];
|
||||
|
||||
System.arraycopy(iterBytes, 0, data, 0, Constants.INT_LENGTH);
|
||||
System.arraycopy(salt, 0, data, Constants.INT_LENGTH, Constants.ENCRYPTION_IV_LENGTH);
|
||||
System.arraycopy(encrypted, 0, data, Constants.INT_LENGTH + Constants.ENCRYPTION_IV_LENGTH, encrypted.length);
|
||||
|
||||
StorageAccessHelper.saveFile(this, uri, data);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
|
@ -509,8 +571,7 @@ public class BackupActivity extends BaseActivity {
|
|||
if (decryptIntent == null)
|
||||
decryptIntent = new Intent(OpenPgpApi.ACTION_DECRYPT_VERIFY);
|
||||
|
||||
String input = FileHelper.readFileToString(this, uri);
|
||||
Log.d("OpenPGP", input);
|
||||
String input = StorageAccessHelper.loadFileString(this, uri);
|
||||
|
||||
InputStream is = new ByteArrayInputStream(input.getBytes(StandardCharsets.UTF_8));
|
||||
ByteArrayOutputStream os = new ByteArrayOutputStream();
|
||||
|
@ -521,7 +582,7 @@ public class BackupActivity extends BaseActivity {
|
|||
|
||||
private void doBackupEncrypted(Uri uri, String data) {
|
||||
if (Tools.isExternalStorageWritable()) {
|
||||
boolean success = FileHelper.writeStringToFile(this, uri, data);
|
||||
boolean success = StorageAccessHelper.saveFile(this, uri, data);
|
||||
|
||||
if (success)
|
||||
Toast.makeText(this, R.string.backup_toast_export_success, Toast.LENGTH_LONG).show();
|
||||
|
|
|
@ -98,6 +98,7 @@ public class MainActivity extends BaseActivity
|
|||
private ListView tagsDrawerListView;
|
||||
private TagsAdapter tagsDrawerAdapter;
|
||||
private ActionBarDrawerToggle tagsToggle;
|
||||
private String filterString;
|
||||
|
||||
// QR code scanning
|
||||
private void scanQRCode(){
|
||||
|
@ -284,6 +285,10 @@ public class MainActivity extends BaseActivity
|
|||
ManualEntryDialog.show(MainActivity.this, settings, adapter);
|
||||
}
|
||||
}
|
||||
|
||||
if (savedInstanceState != null){
|
||||
setFilterString(savedInstanceState.getString("filterString", ""));
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -320,6 +325,11 @@ public class MainActivity extends BaseActivity
|
|||
}
|
||||
}
|
||||
|
||||
if (filterString != null) {
|
||||
// ensure the current filter string is applied after a resume
|
||||
setFilterString(this.filterString);
|
||||
}
|
||||
|
||||
startUpdater();
|
||||
}
|
||||
|
||||
|
@ -329,6 +339,12 @@ public class MainActivity extends BaseActivity
|
|||
stopUpdater();
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
outState.putString("filterString", filterString);
|
||||
}
|
||||
|
||||
public void onSharedPreferenceChanged(SharedPreferences prefs, String key) {
|
||||
if (key.equals(getString(R.string.settings_key_label_size)) ||
|
||||
key.equals(getString(R.string.settings_key_label_scroll)) ||
|
||||
|
@ -455,11 +471,7 @@ public class MainActivity extends BaseActivity
|
|||
|
||||
@Override
|
||||
public boolean onQueryTextChange(String newText) {
|
||||
if (newText.isEmpty())
|
||||
adapter.filterByTags(tagsDrawerAdapter.getActiveTags());
|
||||
else
|
||||
adapter.getFilter().filter(newText);
|
||||
|
||||
setFilterString(newText);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
@ -491,6 +503,15 @@ public class MainActivity extends BaseActivity
|
|||
return true;
|
||||
}
|
||||
|
||||
private void setFilterString(String newText) {
|
||||
if (newText.isEmpty())
|
||||
adapter.filterByTags(tagsDrawerAdapter.getActiveTags());
|
||||
else
|
||||
adapter.getFilter().filter(newText);
|
||||
|
||||
this.filterString = newText;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onOptionsItemSelected(MenuItem item) {
|
||||
int id = item.getItemId();
|
||||
|
@ -594,8 +615,11 @@ public class MainActivity extends BaseActivity
|
|||
for(int i = 0; i < tagsDrawerListView.getChildCount(); i++) {
|
||||
CheckedTextView childCheckBox = (CheckedTextView) tagsDrawerListView.getChildAt(i);
|
||||
childCheckBox.setChecked(checkedTextView.isChecked());
|
||||
tagsDrawerAdapter.setTagState(childCheckBox.getText().toString(), childCheckBox.isChecked());
|
||||
settings.setTagToggle(childCheckBox.getText().toString(), childCheckBox.isChecked());
|
||||
}
|
||||
|
||||
for (String tag: tagsDrawerAdapter.getTags()) {
|
||||
tagsDrawerAdapter.setTagState(tag, checkedTextView.isChecked());
|
||||
settings.setTagToggle(tag, checkedTextView.isChecked());
|
||||
}
|
||||
|
||||
if(checkedTextView.isChecked()) {
|
||||
|
|
|
@ -31,7 +31,7 @@ public class AboutFragment extends Fragment {
|
|||
private static final String MIT_URI = GITHUB_URI + "/blob/master/LICENSE.txt";
|
||||
|
||||
private static final String AUTHOR1_GITHUB = "https://github.com/flocke";
|
||||
private static final String AUTHOR1_EXTRA = "https://paypal.me/flocke000";
|
||||
private static final String AUTHOR1_EXTRA = "https://flocke.shadowice.org/donate.html";
|
||||
|
||||
private static final String AUTHOR2_GITHUB = "https://github.com/richyhbm";
|
||||
private static final String AUTHOR2_EXTRA = "https://richyhbm.co.uk/donate";
|
||||
|
|
|
@ -29,15 +29,17 @@ import android.net.Uri;
|
|||
|
||||
import org.shadowice.flocke.andotp.Database.Entry;
|
||||
import org.shadowice.flocke.andotp.R;
|
||||
import org.shadowice.flocke.andotp.Utilities.BackupHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.EncryptionHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.FileHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.NotificationHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Settings;
|
||||
import org.shadowice.flocke.andotp.Utilities.StorageAccessHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Tools;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.StandardCharsets;
|
||||
import java.util.ArrayList;
|
||||
|
||||
|
@ -54,7 +56,7 @@ public class EncryptedBackupBroadcastReceiver extends BackupBroadcastReceiver {
|
|||
if (!canSaveBackup(context))
|
||||
return;
|
||||
|
||||
Uri savePath = Tools.buildUri(settings.getBackupDir(), FileHelper.backupFilename(context, Constants.BackupType.ENCRYPTED));
|
||||
Uri savePath = Tools.buildUri(settings.getBackupDir(), BackupHelper.backupFilename(context, Constants.BackupType.ENCRYPTED));
|
||||
|
||||
String password = settings.getBackupPasswordEnc();
|
||||
|
||||
|
@ -77,9 +79,21 @@ public class EncryptedBackupBroadcastReceiver extends BackupBroadcastReceiver {
|
|||
String plain = DatabaseHelper.entriesToString(entries);
|
||||
|
||||
try {
|
||||
SecretKey key = EncryptionHelper.generateSymmetricKeyFromPassword(password);
|
||||
int iter = EncryptionHelper.generateRandomIterations();
|
||||
byte[] salt = EncryptionHelper.generateRandom(Constants.ENCRYPTION_IV_LENGTH);
|
||||
|
||||
SecretKey key = EncryptionHelper.generateSymmetricKeyPBKDF2(password, iter, salt);
|
||||
byte[] encrypted = EncryptionHelper.encrypt(key, plain.getBytes(StandardCharsets.UTF_8));
|
||||
FileHelper.writeBytesToFile(context, savePath, encrypted);
|
||||
|
||||
byte[] iterBytes = ByteBuffer.allocate(Constants.INT_LENGTH).putInt(iter).array();
|
||||
byte[] data = new byte[Constants.INT_LENGTH + Constants.ENCRYPTION_IV_LENGTH + encrypted.length];
|
||||
|
||||
System.arraycopy(iterBytes, 0, data, 0, Constants.INT_LENGTH);
|
||||
System.arraycopy(salt, 0, data, Constants.INT_LENGTH, Constants.ENCRYPTION_IV_LENGTH);
|
||||
System.arraycopy(encrypted, 0, data, Constants.INT_LENGTH + Constants.ENCRYPTION_IV_LENGTH, encrypted.length);
|
||||
|
||||
StorageAccessHelper.saveFile(context, savePath, data);
|
||||
|
||||
NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_SUCCESS, R.string.backup_receiver_title_backup_success, savePath.getPath());
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
|
|
|
@ -29,12 +29,13 @@ import android.net.Uri;
|
|||
|
||||
import org.shadowice.flocke.andotp.Database.Entry;
|
||||
import org.shadowice.flocke.andotp.R;
|
||||
import org.shadowice.flocke.andotp.Utilities.BackupHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Constants;
|
||||
import org.shadowice.flocke.andotp.Utilities.DatabaseHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.FileHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.KeyStoreHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.NotificationHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Settings;
|
||||
import org.shadowice.flocke.andotp.Utilities.StorageAccessHelper;
|
||||
import org.shadowice.flocke.andotp.Utilities.Tools;
|
||||
|
||||
import java.util.ArrayList;
|
||||
|
@ -52,7 +53,7 @@ public class PlainTextBackupBroadcastReceiver extends BackupBroadcastReceiver {
|
|||
if (!canSaveBackup(context))
|
||||
return;
|
||||
|
||||
Uri savePath = Tools.buildUri(settings.getBackupDir(), FileHelper.backupFilename(context, Constants.BackupType.PLAIN_TEXT));
|
||||
Uri savePath = Tools.buildUri(settings.getBackupDir(), BackupHelper.backupFilename(context, Constants.BackupType.PLAIN_TEXT));
|
||||
|
||||
SecretKey encryptionKey = null;
|
||||
|
||||
|
@ -66,7 +67,7 @@ public class PlainTextBackupBroadcastReceiver extends BackupBroadcastReceiver {
|
|||
if (Tools.isExternalStorageWritable()) {
|
||||
ArrayList<Entry> entries = DatabaseHelper.loadDatabase(context, encryptionKey);
|
||||
|
||||
if (FileHelper.writeStringToFile(context, savePath, DatabaseHelper.entriesToString(entries))) {
|
||||
if (StorageAccessHelper.saveFile(context, savePath, DatabaseHelper.entriesToString(entries))) {
|
||||
NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_SUCCESS, R.string.backup_receiver_title_backup_success, savePath.getPath());
|
||||
} else {
|
||||
NotificationHelper.notify(context, Constants.NotificationChannel.BACKUP_FAILED, R.string.backup_receiver_title_backup_failed, R.string.backup_toast_export_failed);
|
||||
|
|
|
@ -0,0 +1,31 @@
|
|||
package org.shadowice.flocke.andotp.Utilities;
|
||||
|
||||
import android.content.Context;
|
||||
|
||||
public class BackupHelper {
|
||||
public static String backupFilename(Context context, Constants.BackupType type) {
|
||||
Settings settings = new Settings(context);
|
||||
switch (type) {
|
||||
case PLAIN_TEXT:
|
||||
if (settings.getIsAppendingDateTimeToBackups()) {
|
||||
return String.format(Constants.BACKUP_FILENAME_PLAIN_FORMAT, Tools.getDateTimeString());
|
||||
} else {
|
||||
return Constants.BACKUP_FILENAME_PLAIN;
|
||||
}
|
||||
case ENCRYPTED:
|
||||
if (settings.getIsAppendingDateTimeToBackups()) {
|
||||
return String.format(Constants.BACKUP_FILENAME_CRYPT_FORMAT, Tools.getDateTimeString());
|
||||
} else {
|
||||
return Constants.BACKUP_FILENAME_CRYPT;
|
||||
}
|
||||
case OPEN_PGP:
|
||||
if (settings.getIsAppendingDateTimeToBackups()) {
|
||||
return String.format(Constants.BACKUP_FILENAME_PGP_FORMAT, Tools.getDateTimeString());
|
||||
} else {
|
||||
return Constants.BACKUP_FILENAME_PGP;
|
||||
}
|
||||
}
|
||||
|
||||
return Constants.BACKUP_FILENAME_PLAIN;
|
||||
}
|
||||
}
|
|
@ -66,6 +66,7 @@ public class Constants {
|
|||
public final static int INTENT_BACKUP_SAVE_DOCUMENT_PGP = 205;
|
||||
public final static int INTENT_BACKUP_ENCRYPT_PGP = 206;
|
||||
public final static int INTENT_BACKUP_DECRYPT_PGP = 207;
|
||||
public final static int INTENT_BACKUP_OPEN_DOCUMENT_CRYPT_OLD = 208;
|
||||
|
||||
public static final int INTENT_SETTINGS_AUTHENTICATE = 300;
|
||||
|
||||
|
@ -76,6 +77,7 @@ public class Constants {
|
|||
public final static int PERMISSIONS_BACKUP_WRITE_EXPORT_CRYPT = 213;
|
||||
public final static int PERMISSIONS_BACKUP_READ_IMPORT_PGP = 214;
|
||||
public final static int PERMISSIONS_BACKUP_WRITE_EXPORT_PGP = 215;
|
||||
public final static int PERMISSIONS_BACKUP_READ_IMPORT_CRYPT_OLD = 216;
|
||||
|
||||
// Intent extras
|
||||
public final static String EXTRA_AUTH_PASSWORD_KEY = "password_key";
|
||||
|
@ -92,7 +94,9 @@ public class Constants {
|
|||
final static String ALGORITHM_ASYMMETRIC = "RSA/ECB/PKCS1Padding";
|
||||
|
||||
final static int ENCRYPTION_KEY_LENGTH = 16; // 128-bit encryption key (KeyStore-mode)
|
||||
final static int ENCRYPTION_IV_LENGTH = 12;
|
||||
public final static int ENCRYPTION_IV_LENGTH = 12;
|
||||
|
||||
public final static int INT_LENGTH = 4;
|
||||
|
||||
final static int PBKDF2_MIN_ITERATIONS = 1000;
|
||||
final static int PBKDF2_MAX_ITERATIONS = 5000;
|
||||
|
|
|
@ -89,6 +89,14 @@ public class EncryptionHelper {
|
|||
return new SecretKeySpec(data, 0, data.length, "AES");
|
||||
}
|
||||
|
||||
public static SecretKey generateSymmetricKeyPBKDF2(String password, int iter, byte[] salt)
|
||||
throws NoSuchAlgorithmException, InvalidKeySpecException {
|
||||
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA1");
|
||||
KeySpec keySpec = new PBEKeySpec(password.toCharArray(), salt, iter, Constants.PBKDF2_LENGTH);
|
||||
|
||||
return secretKeyFactory.generateSecret(keySpec);
|
||||
}
|
||||
|
||||
public static SecretKey generateSymmetricKeyFromPassword(String password)
|
||||
throws NoSuchAlgorithmException {
|
||||
MessageDigest sha = MessageDigest.getInstance("SHA-256");
|
||||
|
|
|
@ -76,9 +76,11 @@ public class EntryThumbnail {
|
|||
ComputerBase(R.drawable.thumb_computerbase),
|
||||
ConnectWiseManage(R.drawable.thumb_connectwise_manage),
|
||||
CozyCloud(R.drawable.thumb_cozycloud),
|
||||
Crowdin(R.drawable.thumb_crowdin),
|
||||
Dashlane(R.drawable.thumb_dashlane),
|
||||
Debian(R.drawable.thumb_debian),
|
||||
Degiro(R.drawable.thumb_degiro),
|
||||
Diaspora(R.drawable.thumb_diaspora),
|
||||
Digidentity(R.drawable.thumb_digidentity),
|
||||
DigitalOcean(R.drawable.thumb_digital_ocean),
|
||||
Discord(R.drawable.thumb_discord),
|
||||
|
@ -120,10 +122,13 @@ public class EntryThumbnail {
|
|||
INWX(R.drawable.thumb_inwx),
|
||||
Itchio(R.drawable.thumb_itchio),
|
||||
Jagex(R.drawable.thumb_jagex),
|
||||
Joomla(R.drawable.thumb_joomla),
|
||||
Kickstarter(R.drawable.thumb_kickstarter),
|
||||
Kraken(R.drawable.thumb_kraken),
|
||||
Kucoin(R.drawable.thumb_kucoin),
|
||||
LastPass(R.drawable.thumb_lastpass),
|
||||
Lichess(R.drawable.thumb_lichess),
|
||||
LinkedIn(R.drawable.thumb_linkedin),
|
||||
Linode(R.drawable.thumb_linode),
|
||||
Liqui(R.drawable.thumb_liqui),
|
||||
LocalBitcoins(R.drawable.thumb_localbitcoins),
|
||||
|
|
|
@ -22,59 +22,16 @@
|
|||
|
||||
package org.shadowice.flocke.andotp.Utilities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
|
||||
import java.io.BufferedReader;
|
||||
import java.io.BufferedWriter;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.InputStreamReader;
|
||||
import java.io.OutputStream;
|
||||
import java.io.OutputStreamWriter;
|
||||
|
||||
public class FileHelper {
|
||||
public static String readFileToString(Context context, Uri file) {
|
||||
StringBuilder stringBuilder = new StringBuilder();
|
||||
|
||||
try {
|
||||
InputStream inputStream = context.getContentResolver().openInputStream(file);
|
||||
BufferedReader reader = new BufferedReader(new InputStreamReader(inputStream));
|
||||
String line;
|
||||
while ((line = reader.readLine()) != null) {
|
||||
stringBuilder.append(line).append("\n");
|
||||
}
|
||||
reader.close();
|
||||
inputStream.close();
|
||||
} catch (Exception error) {
|
||||
error.printStackTrace();
|
||||
}
|
||||
|
||||
return(stringBuilder.toString());
|
||||
}
|
||||
|
||||
public static boolean writeStringToFile(Context context, Uri file, String content) {
|
||||
boolean success = true;
|
||||
|
||||
try {
|
||||
OutputStream outputStream = context.getContentResolver().openOutputStream(file);
|
||||
BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(outputStream));
|
||||
writer.write(content);
|
||||
writer.close();
|
||||
outputStream.close();
|
||||
} catch (Exception error) {
|
||||
success = false;
|
||||
error.printStackTrace();
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public static byte[] readFileToBytes(File file) throws IOException {
|
||||
class FileHelper {
|
||||
static byte[] readFileToBytes(File file) throws IOException {
|
||||
final InputStream in = new FileInputStream(file);
|
||||
try {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
|
@ -89,63 +46,10 @@ public class FileHelper {
|
|||
}
|
||||
}
|
||||
|
||||
public static void writeBytesToFile(File file, byte[] data) throws IOException {
|
||||
final OutputStream out = new FileOutputStream(file);
|
||||
try {
|
||||
static void writeBytesToFile(File file, byte[] data) throws IOException {
|
||||
try (OutputStream out = new FileOutputStream(file)) {
|
||||
out.write(data);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static byte[] readFileToBytes(Context context, Uri file) throws IOException {
|
||||
final InputStream in = context.getContentResolver().openInputStream(file);
|
||||
try {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
while ((count = in.read(buffer)) != -1) {
|
||||
bytes.write(buffer, 0, count);
|
||||
}
|
||||
return bytes.toByteArray();
|
||||
} finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static void writeBytesToFile(Context context, Uri file, byte[] data) throws IOException {
|
||||
final OutputStream out = context.getContentResolver().openOutputStream(file);
|
||||
try {
|
||||
out.write(data);
|
||||
} finally {
|
||||
out.close();
|
||||
}
|
||||
}
|
||||
|
||||
public static String backupFilename(Context context, Constants.BackupType type) {
|
||||
Settings settings = new Settings(context);
|
||||
switch (type) {
|
||||
case PLAIN_TEXT:
|
||||
if (settings.getIsAppendingDateTimeToBackups()) {
|
||||
return String.format(Constants.BACKUP_FILENAME_PLAIN_FORMAT, Tools.getDateTimeString());
|
||||
} else {
|
||||
return Constants.BACKUP_FILENAME_PLAIN;
|
||||
}
|
||||
case ENCRYPTED:
|
||||
if (settings.getIsAppendingDateTimeToBackups()) {
|
||||
return String.format(Constants.BACKUP_FILENAME_CRYPT_FORMAT, Tools.getDateTimeString());
|
||||
} else {
|
||||
return Constants.BACKUP_FILENAME_CRYPT;
|
||||
}
|
||||
case OPEN_PGP:
|
||||
if (settings.getIsAppendingDateTimeToBackups()) {
|
||||
return String.format(Constants.BACKUP_FILENAME_PGP_FORMAT, Tools.getDateTimeString());
|
||||
} else {
|
||||
return Constants.BACKUP_FILENAME_PGP;
|
||||
}
|
||||
}
|
||||
|
||||
return Constants.BACKUP_FILENAME_PLAIN;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -493,6 +493,14 @@ public class Settings {
|
|||
setBoolean(R.string.settings_key_last_used_dialog_shown, value);
|
||||
}
|
||||
|
||||
public boolean getNewBackupFormatDialogShown() {
|
||||
return getBoolean(R.string.settings_key_new_backup_format_dialog_shown, false);
|
||||
}
|
||||
|
||||
public void setNewBackupFormatDialogShown(boolean value) {
|
||||
setBoolean(R.string.settings_key_new_backup_format_dialog_shown, value);
|
||||
}
|
||||
|
||||
public boolean getAndroidBackupServiceEnabled() {
|
||||
return getBoolean(R.string.settings_key_enable_android_backup_service, true);
|
||||
}
|
||||
|
|
|
@ -0,0 +1,65 @@
|
|||
package org.shadowice.flocke.andotp.Utilities;
|
||||
|
||||
import android.content.Context;
|
||||
import android.net.Uri;
|
||||
import android.os.ParcelFileDescriptor;
|
||||
|
||||
import org.apache.commons.codec.Charsets;
|
||||
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.FileOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
|
||||
public class StorageAccessHelper {
|
||||
public static boolean saveFile(Context context, Uri file, byte[] data) {
|
||||
boolean success = true;
|
||||
|
||||
try {
|
||||
ParcelFileDescriptor pfd = context.getContentResolver().openFileDescriptor(file, "w");
|
||||
FileOutputStream fileOutputStream = new FileOutputStream(pfd.getFileDescriptor());
|
||||
|
||||
fileOutputStream.write(data);
|
||||
|
||||
fileOutputStream.close();
|
||||
pfd.close();
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
success = false;
|
||||
}
|
||||
|
||||
return success;
|
||||
}
|
||||
|
||||
public static boolean saveFile(Context context, Uri file, String data) {
|
||||
return saveFile(context, file, data.getBytes(Charsets.UTF_8));
|
||||
}
|
||||
|
||||
public static byte[] loadFile(Context context, Uri file) throws IOException {
|
||||
try (InputStream inputStream = context.getContentResolver().openInputStream(file)) {
|
||||
ByteArrayOutputStream bytes = new ByteArrayOutputStream();
|
||||
|
||||
byte[] buffer = new byte[1024];
|
||||
int count;
|
||||
|
||||
while ((count = inputStream.read(buffer)) != -1) {
|
||||
bytes.write(buffer, 0, count);
|
||||
}
|
||||
|
||||
return bytes.toByteArray();
|
||||
}
|
||||
}
|
||||
|
||||
public static String loadFileString(Context context, Uri file) {
|
||||
String result = "";
|
||||
|
||||
try {
|
||||
byte[] content = loadFile(context, file);
|
||||
result = new String(content, Charsets.UTF_8);
|
||||
} catch (IOException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
}
|
|
@ -66,9 +66,7 @@ public class TokenCalculator {
|
|||
}
|
||||
|
||||
public static String TOTP_RFC6238(byte[] secret, int period, int digits, HashAlgorithm algorithm) {
|
||||
int token = TOTP_RFC6238(secret, period, System.currentTimeMillis() / 1000, digits, algorithm);
|
||||
|
||||
return String.format("%0" + digits + "d", token);
|
||||
return Tools.formatTokenString(TOTP_RFC6238(secret, period, System.currentTimeMillis() / 1000, digits, algorithm), digits);
|
||||
}
|
||||
|
||||
public static String TOTP_Steam(byte[] secret, int period, int digits, HashAlgorithm algorithm) {
|
||||
|
@ -88,7 +86,7 @@ public class TokenCalculator {
|
|||
int fullToken = HOTP(secret, counter, algorithm);
|
||||
int div = (int) Math.pow(10, digits);
|
||||
|
||||
return String.format("%0" + digits + "d", fullToken % div);
|
||||
return Tools.formatTokenString(fullToken % div, digits);
|
||||
}
|
||||
|
||||
private static int TOTP(byte[] key, int period, long time, HashAlgorithm algorithm) {
|
||||
|
|
|
@ -34,6 +34,7 @@ import android.os.Environment;
|
|||
|
||||
import java.io.File;
|
||||
import java.text.DateFormat;
|
||||
import java.text.NumberFormat;
|
||||
import java.text.SimpleDateFormat;
|
||||
import java.util.Calendar;
|
||||
import java.util.Date;
|
||||
|
@ -97,6 +98,14 @@ public class Tools {
|
|||
}
|
||||
}
|
||||
|
||||
public static String formatTokenString(int token, int digits) {
|
||||
NumberFormat numberFormat = NumberFormat.getInstance(Locale.ENGLISH);
|
||||
numberFormat.setMinimumIntegerDigits(digits);
|
||||
numberFormat.setGroupingUsed(false);
|
||||
|
||||
return numberFormat.format(token);
|
||||
}
|
||||
|
||||
public static String formatToken(String s, int chunkSize) {
|
||||
if (chunkSize ==0 || s == null)
|
||||
return s;
|
||||
|
|
30
app/src/main/res/drawable/thumb_crowdin.xml
Normal file
30
app/src/main/res/drawable/thumb_crowdin.xml
Normal file
|
@ -0,0 +1,30 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="m82.345,86.605c-8.2,-0 -12.928,-4.254 -14.397,-12.988 2.389,0.323 4.858,0.589 7.328,0.684 1.024,7.175 4.423,10.658 8.015,12.285 -0.316,0.012 -0.632,0.019 -0.946,0.019zM67.681,64.467c0.053,-0.599 0.114,-1.24 0.144,-1.416 1.969,-10.2 18.984,-14.186 28.078,-14.186 -8.628,2.028 -17.705,5.947 -19.639,13.332 -0.182,0.704 -0.482,1.967 -0.728,3.083 -1.669,-0.169 -2.544,-0.22 -3.28,-0.263 -1.094,-0.064 -1.772,-0.103 -4.575,-0.549zM119.327,49.109c0.6,-0.035 0.664,-0.895 0.074,-1.012 -3.96,-0.781 -10.225,-1.442 -17.101,-1.442 -15.406,-0 -33.876,3.319 -36.331,16.037 -0.091,0.478 -0.275,2.893 -0.327,3.36 1.405,0.234 2.439,0.397 3.241,0.514 -0.087,0.623 -0.142,1.263 -0.154,1.925 -0.021,1.174 0.003,2.277 0.061,3.326 -0.994,-0.129 -2.003,-0.271 -3.036,-0.426 1.242,13.17 8.481,16.934 16.06,17.099 0.177,0.004 0.354,0.008 0.531,0.008 4.788,-0 9.659,-1.38 13.192,-2.683 0.573,-0.211 0.364,-1.067 -0.241,-0.984 -6.978,0.949 -17.839,-0.243 -15.416,-17.754 2.007,-14.49 29.668,-17.393 39.447,-17.968"
|
||||
android:fillColor="#2e2d2d"/>
|
||||
<path
|
||||
android:pathData="m54.119,58.836c-0.333,1.273 -0.898,3.658 -1.285,5.48 -3.173,-0.351 -5.145,-0.544 -6.776,-0.704 -2.358,-0.231 -3.812,-0.373 -6.931,-0.808 0.103,-1.178 0.246,-2.757 0.309,-3.106 3.966,-20.533 38.488,-22.75 53.147,-22.75 1.126,-0 2.234,0.012 3.322,0.033 -17.868,1.681 -37.891,6.961 -41.785,21.854zM62.347,96.564c-13.174,-0 -20.877,-7.34 -22.922,-21.827 4.254,0.604 8.691,1.116 13.103,1.234 1.654,13.614 8.745,18.654 15.936,20.122 -2.018,0.296 -4.076,0.471 -6.117,0.471zM37.578,59.339c-0.131,0.728 -0.423,4.378 -0.493,5.086 2.134,0.31 3.606,0.506 4.864,0.657 -0.103,0.827 -0.174,1.671 -0.19,2.544 -0.036,1.964 0.012,3.798 0.123,5.534 -1.513,-0.197 -3.05,-0.417 -4.621,-0.653 1.922,20.453 13.348,25.951 25.086,25.951 1.923,-0 3.854,-0.152 5.753,-0.405 6.663,-0.694 13.111,-3.011 17.313,-4.812 0.562,-0.241 0.301,-1.087 -0.299,-0.969 -9.941,1.946 -30.536,2.605 -26.469,-26.785 3.173,-22.946 48.89,-26.337 61.607,-26.836 0.596,-0.023 0.678,-0.872 0.097,-1.008 -5.767,-1.349 -16.196,-2.585 -27.767,-2.585 -23.325,-0 -51.283,5.025 -55.003,24.283"
|
||||
android:fillColor="#2e2d2d"/>
|
||||
<path
|
||||
android:pathData="m4.03,48.381c-0.004,0.017 -0.007,0.031 -0.011,0.049 0.004,-0.018 0.007,-0.031 0.011,-0.049"
|
||||
android:fillColor="#2e2d2d"/>
|
||||
<path
|
||||
android:pathData="m3.981,48.607c-0.007,0.032 -0.013,0.063 -0.02,0.097 -0.005,0.025 -0.011,0.054 -0.016,0.08 0.027,-0.128 0.052,-0.252 0.075,-0.353 -0.012,0.054 -0.025,0.115 -0.038,0.177"
|
||||
android:fillColor="#2e2d2d"/>
|
||||
<path
|
||||
android:pathData="m3.342,51.984c0.077,-0.424 0.155,-0.858 0.233,-1.28 -0.078,0.421 -0.157,0.855 -0.233,1.28"
|
||||
android:fillColor="#2e2d2d"/>
|
||||
<path
|
||||
android:pathData="m3.635,50.378c0.076,-0.405 0.149,-0.793 0.216,-1.13 -0.066,0.337 -0.14,0.725 -0.216,1.13"
|
||||
android:fillColor="#2e2d2d"/>
|
||||
<path
|
||||
android:pathData="m90.284,23.074c-35.123,-0.001 -56.907,9.135 -62.995,26.415 -0.689,1.945 -2.034,6.082 -2.853,8.85 -1.924,-0.412 -3.489,-0.736 -4.813,-1.003 -2.093,-0.424 -3.577,-0.71 -4.882,-0.954 -3.144,-0.589 -4.98,-0.756 -11.708,-2.649 0.016,-0 0.033,-0.173 0.05,-0.269 0.244,-1.383 0.615,-3.469 0.853,-4.631 0.008,-0.042 0.018,-0.09 0.026,-0.129 0.007,-0.034 0.014,-0.065 0.02,-0.097 0.022,-0.102 0.042,-0.196 0.061,-0.275 0.016,-0.07 0.031,-0.131 0.044,-0.182 0.004,-0.015 0.011,-0.043 0.014,-0.056 6.651,-23.524 37.197,-28.427 61.653,-28.427l0.001,-0c14.772,-0 29.433,1.791 41.134,4.059 -5.111,-0.367 -10.748,-0.651 -16.605,-0.651zM31.542,99.778c4.303,4.349 10.051,7.006 17.134,7.931 -3.199,0.427 -6.52,0.69 -9.844,0.69 -23.769,-0 -36.177,-12.981 -36.907,-38.596 6.419,1.61 13.631,3.231 20.734,4.08 0.166,11.396 3.151,20.101 8.882,25.894zM3.851,49.248c-0.066,0.337 -0.14,0.725 -0.216,1.13 0.076,-0.405 0.149,-0.793 0.216,-1.13zM3.575,50.704c-0.078,0.421 -0.157,0.855 -0.233,1.28 0.077,-0.425 0.155,-0.859 0.233,-1.28zM127.673,27.184c-10.52,-4.045 -36.32,-9.477 -61.923,-9.476 -28.598,0 -56.95,6.779 -63.47,29.84 -0.299,1.064 -1.216,6.538 -1.425,7.594 3.099,0.796 5.301,1.342 7.028,1.748 -0.211,1.064 -0.394,2.147 -0.516,3.271 -0.343,3.147 -0.529,6.089 -0.595,8.871 -2.218,-0.52 -4.471,-1.078 -6.772,-1.67 0,34.518 19.661,42.93 38.832,42.93 2.753,-0 5.495,-0.176 8.169,-0.471 8.591,-0.947 16.975,-3.36 22.216,-5.103 0.584,-0.194 0.389,-1.065 -0.222,-0.99 -13.817,1.708 -47.123,1.354 -36.709,-44.396 8.051,-35.347 79.495,-32.272 95.166,-31.164 0.584,0.042 0.768,-0.774 0.221,-0.984"
|
||||
android:fillColor="#2e2d2d"/>
|
||||
<path
|
||||
android:pathData="m4.1,48.095c-0.004,0.015 -0.009,0.037 -0.014,0.054 -0.013,0.05 -0.028,0.113 -0.044,0.182 -0.004,0.016 -0.008,0.033 -0.012,0.05 0.027,-0.116 0.051,-0.218 0.07,-0.286"
|
||||
android:fillColor="#2e2d2d"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/thumb_diaspora.xml
Normal file
9
app/src/main/res/drawable/thumb_diaspora.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android" xmlns:aapt="http://schemas.android.com/aapt"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128"
|
||||
android:width="160dp"
|
||||
android:height="160dp">
|
||||
<path
|
||||
android:pathData="M80.621094 117.238281C77.167969 112.230469 71.785156 104.414062 68.652344 99.867188C65.453125 95.222656 62.851562 91.601562 62.714844 91.597656C62.574219 91.597656 57.542969 98.621094 50.816406 108.21875C44.40625 117.359375 39.105469 124.839844 39.039062 124.839844C38.859375 124.839844 15.925781 108.058594 15.867188 107.882812C15.839844 107.796875 21.023438 99.890625 27.386719 90.3125C33.753906 80.734375 38.960938 72.75 38.960938 72.5625C38.960938 72.265625 36.898438 71.515625 20.71875 65.910156C10.6875 62.4375 2.382812 59.554688 2.261719 59.503906C2.09375 59.4375 3.050781 56.117188 6.324219 45.375C8.683594 37.652344 10.664062 31.273438 10.726562 31.195312C10.792969 31.117188 19.511719 34.015625 30.101562 37.636719C40.695312 41.257812 49.449219 44.21875 49.558594 44.21875C49.664062 44.21875 49.789062 44.050781 49.828125 43.84375C49.871094 43.636719 49.960938 34.269531 50.03125 23.023438C50.101562 11.777344 50.21875 2.484375 50.289062 2.367188C50.390625 2.207031 53.367188 2.160156 64.289062 2.164062C71.917969 2.164062 78.238281 2.222656 78.335938 2.289062C78.460938 2.371094 78.667969 8.554688 79.019531 22.441406C79.59375 45.242188 79.605469 45.546875 79.957031 45.546875C80.09375 45.546875 88.484375 42.628906 98.609375 39.0625C108.734375 35.492188 117.066406 32.632812 117.125 32.703125C117.332031 32.941406 125.632812 61.027344 125.53125 61.132812C125.476562 61.191406 117.027344 64.199219 106.757812 67.816406C92.628906 72.789062 88.070312 74.457031 88.03125 74.667969C88.003906 74.824219 92.863281 82.367188 99.039062 91.761719C105.121094 101.007812 110.078125 108.644531 110.050781 108.726562C109.988281 108.921875 87.230469 126.335938 87.039062 126.335938C86.960938 126.335938 84.074219 122.242188 80.621094 117.238281ZM80.621094 117.238281"
|
||||
android:fillColor="#000000" />
|
||||
</vector>
|
18
app/src/main/res/drawable/thumb_joomla.xml
Normal file
18
app/src/main/res/drawable/thumb_joomla.xml
Normal file
|
@ -0,0 +1,18 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="m22.869,64.427 l-2.389,-2.389c-7.68,-7.68 -9.899,-18.432 -7.168,-28.16C5.632,32.171 -0,25.344 -0,17.152 -0,7.765 7.68,0.085 17.067,0.085c8.533,0 15.531,6.144 16.896,14.336 9.216,-2.219 19.285,0.341 26.453,7.509l1.024,1.024 -12.629,12.629 -1.024,-1.024c-4.096,-4.096 -10.752,-4.096 -14.848,0 -4.096,4.096 -4.096,10.752 0,14.848L61.269,77.739 48.64,90.368 35.328,77.056Z"
|
||||
android:fillColor="#7ac143"/>
|
||||
<path
|
||||
android:pathData="M37.035,50.432 L50.347,37.12 62.976,24.491 65.365,22.101c7.509,-7.509 18.432,-9.899 27.989,-7.168 1.195,-8.363 8.363,-14.677 16.896,-14.677 9.387,0 17.067,7.68 17.067,17.067 0,8.704 -6.485,15.872 -14.848,16.896 2.731,9.557 0.341,20.309 -7.168,27.819l-1.024,1.024L91.989,50.432l1.024,-1.024c4.096,-4.096 4.096,-10.752 0,-14.848 -4.096,-4.096 -10.752,-4.096 -14.848,0L75.776,36.949 63.147,49.579 49.835,62.891Z"
|
||||
android:fillColor="#f9a541"/>
|
||||
<path
|
||||
android:pathData="M93.867,114.091C84.139,116.992 73.216,114.773 65.536,107.093l-1.024,-1.024 12.629,-12.629 1.024,1.024c4.096,4.096 10.752,4.096 14.848,0 4.096,-4.096 4.096,-10.752 0,-14.848L90.624,77.227 77.995,64.597 64.683,51.285 77.312,38.656 105.813,67.157c7.168,7.168 9.728,17.408 7.509,26.795C121.685,95.147 128,102.315 128,110.848c0,9.387 -7.68,17.067 -17.067,17.067 -8.533,-0.171 -15.531,-6.144 -17.067,-13.824z"
|
||||
android:fillColor="#f44321"/>
|
||||
<path
|
||||
android:pathData="m88.917,78.592 l-13.312,13.312 -12.629,12.629 -2.389,2.389c-7.339,7.339 -17.579,9.728 -26.795,7.509 -1.707,7.68 -8.533,13.312 -16.725,13.312 -9.387,0 -17.067,-7.68 -17.067,-17.067 0,-8.021 5.632,-14.848 13.141,-16.555 -2.389,-9.557 0,-19.797 7.339,-27.136l1.024,-1.024 12.629,12.629 -1.024,1.024c-4.096,4.096 -4.096,10.752 0,14.848 4.096,4.096 10.752,4.096 14.848,0L50.347,92.075 62.976,79.445 76.288,66.133Z"
|
||||
android:fillColor="#5091cd"/>
|
||||
</vector>
|
27
app/src/main/res/drawable/thumb_lichess.xml
Normal file
27
app/src/main/res/drawable/thumb_lichess.xml
Normal file
|
@ -0,0 +1,27 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="M63.944,14.372C104.029,18.189 126.934,44.912 125.025,125.081H37.221c0,-34.358 38.176,-24.814 30.54,-80.169"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="5.7263422"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="M71.579,44.912C73.03,56.021 50.392,73.048 41.039,79.27 29.586,86.905 30.273,95.838 21.951,94.54c-3.978,-3.589 5.383,-11.605 0,-11.453 -3.818,0 0.725,4.696 -3.818,7.635 -3.818,0 -15.282,3.818 -15.27,-15.27 0,-7.635 22.905,-45.811 22.905,-45.811 0,0 7.215,-7.253 7.635,-13.361 -2.787,-3.795 -1.909,-7.635 -1.909,-11.453 3.818,-3.818 11.453,9.544 11.453,9.544h7.635c0,0 2.978,-7.605 9.544,-11.453 3.818,0 3.818,11.453 3.818,11.453"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="5.7263422"
|
||||
android:fillColor="#ffffff"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
<path
|
||||
android:pathData="m16.225,73.544a1.909,1.909 0,1 1,-3.818 0,1.909 1.909,0 1,1 3.818,0zM36.965,36.323a1.909,5.726 30,1 1,-3.306 -1.909,1.909 5.726,30 1,1 3.306,1.909z"
|
||||
android:strokeLineJoin="round"
|
||||
android:strokeWidth="5.7263422"
|
||||
android:fillColor="#000000"
|
||||
android:strokeColor="#000000"
|
||||
android:strokeLineCap="round"/>
|
||||
</vector>
|
9
app/src/main/res/drawable/thumb_linkedin.xml
Normal file
9
app/src/main/res/drawable/thumb_linkedin.xml
Normal file
|
@ -0,0 +1,9 @@
|
|||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="128dp"
|
||||
android:height="128dp"
|
||||
android:viewportWidth="128"
|
||||
android:viewportHeight="128">
|
||||
<path
|
||||
android:pathData="m9.804,0c-5.191,0 -9.4,4.108 -9.4,9.171v109.659c0,5.064 4.209,9.171 9.4,9.171h108.393c5.191,0 9.4,-4.106 9.4,-9.171L127.597,9.171c0,-5.063 -4.209,-9.171 -9.4,-9.171zM29.479,21.486c6.572,0 10.62,4.316 10.745,9.987 0,5.547 -4.17,9.987 -10.869,9.987h-0.128c-6.447,0 -10.613,-4.441 -10.613,-9.987 0,-5.671 4.294,-9.987 10.866,-9.987zM86.114,47.995c12.64,0 22.119,8.26 22.119,26.014v33.142h-19.21v-30.922c0,-7.77 -2.78,-13.069 -9.731,-13.069 -5.307,0 -8.467,3.572 -9.856,7.023 -0.507,1.235 -0.633,2.961 -0.633,4.689v32.279h-19.21c0,0 0.252,-52.378 0,-57.801h19.21v8.194c2.551,-3.939 7.107,-9.548 17.312,-9.548zM68.802,57.542c-0.04,0.061 -0.089,0.13 -0.128,0.19h0.128zM19.748,49.349h19.21v57.801h-19.21z"
|
||||
android:fillColor="#006699"/>
|
||||
</vector>
|
|
@ -31,7 +31,7 @@
|
|||
android:imeOptions="actionDone"
|
||||
android:inputType="textPassword"
|
||||
android:autofillHints="password" />
|
||||
|
||||
<requestFocus/>
|
||||
</android.support.design.widget.TextInputLayout>
|
||||
|
||||
<Button
|
||||
|
|
|
@ -129,6 +129,28 @@
|
|||
|
||||
</LinearLayout>
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/button_restore_crypt_old"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
android:padding="@dimen/activity_margin"
|
||||
android:background="?android:attr/selectableItemBackground" >
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceListItem"
|
||||
android:text="@string/backup_title_import_crypt_old" />
|
||||
|
||||
<TextView
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:textAppearance="?android:attr/textAppearanceSmall"
|
||||
android:text="@string/backup_desc_import_crypt_old"/>
|
||||
|
||||
</LinearLayout>
|
||||
|
||||
<TextView
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="wrap_content"
|
||||
|
|
|
@ -275,8 +275,7 @@
|
|||
android:background="?android:attr/selectableItemBackground"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textStyle="bold"
|
||||
android:textColor="?attr/colorPaypal"
|
||||
android:text="@string/about_label_donate_paypal" />
|
||||
android:text="@string/about_label_donate" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
@ -333,8 +332,7 @@
|
|||
android:background="?android:attr/selectableItemBackground"
|
||||
android:textAppearance="?android:attr/textAppearanceMedium"
|
||||
android:textStyle="bold"
|
||||
android:textColor="@color/bitcoin"
|
||||
android:text="@string/about_label_donate_bitcoin" />
|
||||
android:text="@string/about_label_donate" />
|
||||
</LinearLayout>
|
||||
</LinearLayout>
|
||||
|
||||
|
|
|
@ -69,6 +69,6 @@
|
|||
<b>Any entries that are added will get lost.</b>\n\nTo still be able to use andOTP you can go
|
||||
to the <b>Settings</b> and switch the <b>Database encryption</b> to <b>Password / PIN</b>.</string>
|
||||
<!-- Shortcuts -->
|
||||
<string name="shortcut_name_scan_qr">Scan QR-Code</string>
|
||||
<string name="shortcut_name_scan_qr">Escaneja un codi QR</string>
|
||||
<string name="shortcut_name_enter_details">Enter details</string>
|
||||
</resources>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<!-- Buttons -->
|
||||
<string name="button_cancel">Annuler</string>
|
||||
<string name="button_enter_details">Ajouter les détails</string>
|
||||
<string name="button_scan_qr">Scanner un QR-Code</string>
|
||||
<string name="button_scan_qr">Scanner un code QR</string>
|
||||
<string name="button_save">Enregistrer</string>
|
||||
<string name="button_new_tag">Nouveau tag</string>
|
||||
<string name="button_settings">Paramètres</string>
|
||||
|
@ -47,7 +47,7 @@
|
|||
<string name="toast_auth_failed_fatal">Échec d\'authentification, fermeture d\'andOTP !</string>
|
||||
<string name="toast_copied_to_clipboard">Copié dans le presse-papier</string>
|
||||
<string name="toast_entry_exists">Cette entrée existe déjà</string>
|
||||
<string name="toast_invalid_qr_code">QR-Code invalide</string>
|
||||
<string name="toast_invalid_qr_code">Code QR invalide</string>
|
||||
<string name="toast_encryption_key_empty">Clé de chiffrement non chargée</string>
|
||||
<!-- Dialogs -->
|
||||
<string name="dialog_title_auth">Identifiez-vous</string>
|
||||
|
@ -66,6 +66,6 @@
|
|||
<string name="dialog_msg_keystore_error">Échec du chargement de la clé de chiffrement à partir de l\'armoire à clés.
|
||||
<b>Toute entrée ajoutée sera perdue.</b>\n\nPour continuer à utiliser andOTP, vous pouvez aller dans les <b>paramètres</b> pour passer de <b>Chiffrement de la base de données</b> à <b>Mot de passe / Code PIN</b>.</string>
|
||||
<!-- Shortcuts -->
|
||||
<string name="shortcut_name_scan_qr">Scanner le QR-code</string>
|
||||
<string name="shortcut_name_scan_qr">Scanner le code QR</string>
|
||||
<string name="shortcut_name_enter_details">Ajouter les détails</string>
|
||||
</resources>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<!-- Buttons -->
|
||||
<string name="button_cancel">Annulla</string>
|
||||
<string name="button_enter_details">Aggiungi dettagli</string>
|
||||
<string name="button_scan_qr">Scansiona il QR Code</string>
|
||||
<string name="button_scan_qr">Scansiona il codice QR</string>
|
||||
<string name="button_save">Salva</string>
|
||||
<string name="button_new_tag">Nuovo tag</string>
|
||||
<string name="button_settings">Impostazioni</string>
|
||||
|
@ -47,7 +47,7 @@
|
|||
<string name="toast_auth_failed_fatal">Autenticazione non riuscita, andOTP verrà chiuso!</string>
|
||||
<string name="toast_copied_to_clipboard">Copiato negli appunti</string>
|
||||
<string name="toast_entry_exists">Questa voce esiste già</string>
|
||||
<string name="toast_invalid_qr_code">QR code non valido</string>
|
||||
<string name="toast_invalid_qr_code">Codice QR non valido</string>
|
||||
<string name="toast_encryption_key_empty">Chiave di crittografia non caricata</string>
|
||||
<!-- Dialogs -->
|
||||
<string name="dialog_title_auth">Autenticazione</string>
|
||||
|
@ -69,6 +69,6 @@
|
|||
<b>Qualsiasi voce aggiunta verrà persa</b>\n\nPer continuare ad utilizzare andOTP, vai nelle
|
||||
<b>Impostazioni</b> e imposta la <b>Crittografia del database</b> su <b>Password / PIN</b>.</string>
|
||||
<!-- Shortcuts -->
|
||||
<string name="shortcut_name_scan_qr">Scansione QR-Code</string>
|
||||
<string name="shortcut_name_scan_qr">Scansione codice QR</string>
|
||||
<string name="shortcut_name_enter_details">Inserisci dettagli</string>
|
||||
</resources>
|
||||
|
|
17
app/src/main/res/values-v27/styles.xml
Executable file
17
app/src/main/res/values-v27/styles.xml
Executable file
|
@ -0,0 +1,17 @@
|
|||
<resources>
|
||||
<!-- Light application theme -->
|
||||
<style name="AppTheme" parent="AppBaseTheme">
|
||||
<item name="android:navigationBarColor">?android:attr/colorBackground</item>
|
||||
<item name="android:windowLightNavigationBar">true</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Dark" parent="AppBaseTheme.Dark">
|
||||
<item name="android:navigationBarColor">?android:attr/colorBackground</item>
|
||||
<item name="android:windowLightNavigationBar">false</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Black" parent="AppBaseTheme.Black">
|
||||
<item name="android:navigationBarColor">@color/black</item>
|
||||
<item name="android:windowLightNavigationBar">false</item>
|
||||
</style>
|
||||
</resources>
|
|
@ -4,7 +4,7 @@
|
|||
<!-- Buttons -->
|
||||
<string name="button_cancel">取消</string>
|
||||
<string name="button_enter_details">鍵入詳細資訊</string>
|
||||
<string name="button_scan_qr">掃描 QR-Code</string>
|
||||
<string name="button_scan_qr">掃描 QR code</string>
|
||||
<string name="button_save">儲存</string>
|
||||
<string name="button_new_tag">新標籤</string>
|
||||
<string name="button_settings">設定</string>
|
||||
|
|
|
@ -8,12 +8,11 @@
|
|||
|
||||
<color name="github_dark">#333333</color>
|
||||
<color name="github_light">#f5f5f5</color>
|
||||
<color name="paypal_dark">#003087</color>
|
||||
<color name="paypal_light">#009cde</color>
|
||||
<color name="bitcoin">#f7931a</color>
|
||||
|
||||
<color name="warning_red">#b71c1c</color> <!-- material_red_900 -->
|
||||
|
||||
<color name="black">#000000</color>
|
||||
|
||||
<color name="dark_thumbnail_background">#ffe0e0e0</color> <!-- material_grey_300 -->
|
||||
|
||||
<color name="fab_small_label_background">#212121</color>
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
<string name="settings_key_openpgp_key_encrypt" translatable="false">pref_openpgp_key_encrypt</string>
|
||||
<string name="settings_key_openpgp_key_sign" translatable="false">pref_openpgp_key_sign</string>
|
||||
<string name="settings_key_openpgp_verify" translatable="false">pref_openpgp_verify</string>
|
||||
<string name="settings_key_new_backup_format_dialog_shown" translatable="false">pref_new_backup_dialog_shown</string>
|
||||
|
||||
<string name="settings_key_security_backup_warning" translatable="false">pref_security_backup_warning_shown</string>
|
||||
<string name="settings_key_sort_mode" translatable="false">pref_sort_mode</string>
|
||||
|
|
|
@ -23,9 +23,7 @@
|
|||
<string name="about_label_original_app">Original App</string>
|
||||
<string name="about_label_website">Website</string>
|
||||
<string name="about_label_github" translatable="false">Github</string>
|
||||
<string name="about_label_donate_paypal">Donate (PayPal)</string>
|
||||
<string name="about_label_donate_bitcoin">Donate (Bitcoin)</string>
|
||||
|
||||
<string name="about_label_donate">Donate</string>
|
||||
<string name="about_name_author1" translatable="false">Jakob Nixdorf</string>
|
||||
<string name="about_name_author_original" translatable="false">Bruno Bierbaumer</string>
|
||||
<string name="about_name_author2" translatable="false">Richy HBM</string>
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
<string name="backup_title_export_openpgp">Backup (OpenPGP)</string>
|
||||
<string name="backup_title_import_plain">Restore (plain-text)</string>
|
||||
<string name="backup_title_import_crypt">Restore (encrypted)</string>
|
||||
<string name="backup_title_import_crypt_old">Restore (encrypted, old encryption)</string>
|
||||
<string name="backup_title_import_openpgp">Restore (OpenPGP)</string>
|
||||
|
||||
<string name="backup_title_replace">Replace existing entries</string>
|
||||
|
@ -20,6 +21,8 @@
|
|||
<string name="backup_desc_export_openpgp">Backup all accounts in an OpenPGP-encrypted JSON file</string>
|
||||
<string name="backup_desc_import_plain">Restore accounts from a plain-text JSON file</string>
|
||||
<string name="backup_desc_import_crypt">Restore accounts from a password-protected JSON file</string>
|
||||
<string name="backup_desc_import_crypt_old">Restore accounts from a password-protected JSON file
|
||||
created with an <b>andOTP version lower than 0.6.3</b></string>
|
||||
<string name="backup_desc_import_openpgp">Restore accounts from an OpenPGP-encrypted JSON file</string>
|
||||
|
||||
<string name="backup_desc_crypt_setup">Failed to load the backup password from the <b>Settings</b>,
|
||||
|
@ -61,6 +64,12 @@
|
|||
<string name="backup_receiver_custom_encryption_failed">Password/PIN based encryption not
|
||||
supported with broadcast backup</string>
|
||||
|
||||
<string name="backup_new_format_dialog_title">New encryption method</string>
|
||||
<string name="backup_new_format_dialog_msg">Since version 0.6.3 of andOTP, a <b>new and improved
|
||||
encryption method</b> is used for password-protected backups. The old backups can still be
|
||||
imported, but it is <b>highly recommended to create new backups with the improved
|
||||
encryption</b>.\n\nThis message will not be shown again.</string>
|
||||
|
||||
<!-- Notification channels -->
|
||||
<string name="notification_channel_name_backup_failed">Automatic backup failed</string>
|
||||
<string name="notification_channel_name_backup_success">Automatic backup successful</string>
|
||||
|
|
|
@ -4,7 +4,7 @@
|
|||
<!-- Buttons -->
|
||||
<string name="button_cancel">Cancel</string>
|
||||
<string name="button_enter_details">Enter details</string>
|
||||
<string name="button_scan_qr">Scan QR-Code</string>
|
||||
<string name="button_scan_qr">Scan QR code</string>
|
||||
<string name="button_save">Save</string>
|
||||
<string name="button_new_tag">New tag</string>
|
||||
<string name="button_settings">Settings</string>
|
||||
|
@ -86,6 +86,6 @@
|
|||
to the <b>Settings</b> and switch the <b>Database encryption</b> to <b>Password / PIN</b>.</string>
|
||||
|
||||
<!-- Shortcuts -->
|
||||
<string name="shortcut_name_scan_qr">Scan QR-Code</string>
|
||||
<string name="shortcut_name_scan_qr">Scan QR code</string>
|
||||
<string name="shortcut_name_enter_details">Enter details</string>
|
||||
</resources>
|
||||
|
|
|
@ -5,7 +5,6 @@
|
|||
<attr name="dialogTheme" format="reference" />
|
||||
|
||||
<attr name="colorGithub" format="reference" />
|
||||
<attr name="colorPaypal" format="reference" />
|
||||
<attr name="thumbnailBackground" format="reference" />
|
||||
|
||||
<!-- General styles -->
|
||||
|
@ -16,7 +15,7 @@
|
|||
</style>
|
||||
|
||||
<!-- Light application theme -->
|
||||
<style name="AppTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<style name="AppBaseTheme" parent="Theme.AppCompat.Light.DarkActionBar">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
@ -24,7 +23,6 @@
|
|||
<item name="windowBackground">?android:attr/colorBackground</item>
|
||||
|
||||
<item name="colorGithub">@color/github_dark</item>
|
||||
<item name="colorPaypal">@color/paypal_dark</item>
|
||||
<item name="thumbnailBackground">@android:color/transparent</item>
|
||||
|
||||
<item name="cardStyle">@style/CardViewStyle</item>
|
||||
|
@ -37,6 +35,8 @@
|
|||
<item name="about_libraries_dividerLight_openSource">@color/about_libraries_dividerLight_openSource</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme" parent="AppBaseTheme"/>
|
||||
|
||||
<style name="AppTheme.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
|
@ -51,7 +51,7 @@
|
|||
</style>
|
||||
|
||||
<!-- Dark application theme. -->
|
||||
<style name="AppTheme.Dark" parent="Theme.AppCompat">
|
||||
<style name="AppBaseTheme.Dark" parent="Theme.AppCompat">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
@ -60,7 +60,6 @@
|
|||
<item name="thumbnailBackground">@color/dark_thumbnail_background</item>
|
||||
|
||||
<item name="colorGithub">@color/github_light</item>
|
||||
<item name="colorPaypal">@color/paypal_light</item>
|
||||
|
||||
<item name="cardStyle">@style/CardViewStyle</item>
|
||||
<item name="dialogTheme">@style/DialogTheme.Dark</item>
|
||||
|
@ -72,6 +71,8 @@
|
|||
<item name="about_libraries_dividerLight_openSource">@color/about_libraries_dividerLight_openSource_dark</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Dark" parent = "AppBaseTheme.Dark"/>
|
||||
|
||||
<style name="AppTheme.Dark.NoActionBar">
|
||||
<item name="windowActionBar">false</item>
|
||||
<item name="windowNoTitle">true</item>
|
||||
|
@ -82,29 +83,30 @@
|
|||
</style>
|
||||
|
||||
<!-- Black application theme. -->
|
||||
<style name="AppTheme.Black" parent="Theme.AppCompat">
|
||||
<style name="AppBaseTheme.Black" parent="Theme.AppCompat">
|
||||
<item name="colorPrimary">@color/colorPrimary</item>
|
||||
<item name="colorPrimaryDark">@color/colorPrimaryDark</item>
|
||||
<item name="colorAccent">@color/colorAccent</item>
|
||||
|
||||
<item name="windowBackground">@android:color/black</item>
|
||||
<item name="windowBackground">@color/black</item>
|
||||
<item name="thumbnailBackground">@color/dark_thumbnail_background</item>
|
||||
|
||||
<item name="colorGithub">@color/github_light</item>
|
||||
<item name="colorPaypal">@color/paypal_light</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">@color/black</item>
|
||||
<item name="about_libraries_title_openSource">@color/about_libraries_text_openSource_dark</item>
|
||||
<item name="about_libraries_text_openSource">@color/about_libraries_text_openSource_dark</item>
|
||||
<item name="about_libraries_dividerDark_openSource">@color/about_libraries_dividerDark_openSource_dark</item>
|
||||
<item name="about_libraries_dividerLight_openSource">@color/about_libraries_dividerLight_openSource_dark</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Black" parent="AppBaseTheme.Black"/>
|
||||
|
||||
<style name="CardViewStyle.Black">
|
||||
<item name="cardBackgroundColor">@android:color/black</item>
|
||||
<item name="cardBackgroundColor">@color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="AppTheme.Black.NoActionBar">
|
||||
|
|
Loading…
Reference in a new issue