From d98e23a1e59c76b8a623eba78612471ece05b68c Mon Sep 17 00:00:00 2001 From: David Creswick Date: Sun, 5 Jan 2025 16:18:01 -0600 Subject: [PATCH 01/25] delete temporary export file when finished --- .../java/com/beemdevelopment/aegis/ui/tasks/ExportTask.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/ExportTask.java b/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/ExportTask.java index 590e05d0..79943ac2 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/ExportTask.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/tasks/ExportTask.java @@ -34,6 +34,8 @@ public class ExportTask extends ProgressDialogTask return null; } catch (IOException e) { return e; + } finally { + boolean ignored = params.getFile().delete(); } } From 919e6854e821430af3c68832f07825edefa7428c Mon Sep 17 00:00:00 2001 From: David Creswick Date: Sun, 5 Jan 2025 16:31:49 -0600 Subject: [PATCH 02/25] add test for html exports Do some basic tests of the html export code. - Make sure that a file was created. - Make sure that the file can be parsed by an xml parser without error. - Make sure that the document tag is "html". --- .../aegis/BackupExportTest.java | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/app/src/androidTest/java/com/beemdevelopment/aegis/BackupExportTest.java b/app/src/androidTest/java/com/beemdevelopment/aegis/BackupExportTest.java index 9f15019b..e0151395 100644 --- a/app/src/androidTest/java/com/beemdevelopment/aegis/BackupExportTest.java +++ b/app/src/androidTest/java/com/beemdevelopment/aegis/BackupExportTest.java @@ -61,13 +61,20 @@ import org.junit.Test; import org.junit.rules.RuleChain; import org.junit.rules.TestRule; import org.junit.runner.RunWith; +import org.xmlpull.v1.XmlPullParser; +import org.xmlpull.v1.XmlPullParserException; +import org.xmlpull.v1.XmlPullParserFactory; import java.io.File; import java.io.FileInputStream; import java.io.IOException; import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.Reader; +import java.nio.charset.StandardCharsets; import java.util.Collection; import java.util.List; +import java.util.Locale; import javax.crypto.Cipher; import javax.crypto.SecretKey; @@ -183,7 +190,9 @@ public class BackupExportTest extends AegisTest { onView(withText(R.string.export_format_html)).inRoot(RootMatchers.isPlatformPopup()).perform(click()); onView(withId(android.R.id.button1)).perform(click()); onView(withId(R.id.checkbox_accept)).perform(click()); - doExport(); + File file = doExport(); + + checkHtmlExport(file); } @Test @@ -196,7 +205,9 @@ public class BackupExportTest extends AegisTest { onView(withText(R.string.export_format_html)).inRoot(RootMatchers.isPlatformPopup()).perform(click()); onView(withId(android.R.id.button1)).perform(click()); onView(withId(R.id.checkbox_accept)).perform(click()); - doExport(); + File file = doExport(); + + checkHtmlExport(file); } @Test @@ -380,6 +391,26 @@ public class BackupExportTest extends AegisTest { checkReadEntries(entries); } + private void checkHtmlExport(File file) { + try (InputStream inStream = new FileInputStream(file)) { + Reader inReader = new InputStreamReader(inStream, StandardCharsets.UTF_8); + XmlPullParserFactory factory = XmlPullParserFactory.newInstance(); + XmlPullParser parser = factory.newPullParser(); + parser.setInput(inReader); + while (parser.getEventType() != XmlPullParser.START_TAG) { + parser.next(); + } + if (!parser.getName().toLowerCase(Locale.ROOT).equals("html")) { + throw new RuntimeException("not an html document!"); + } + while (parser.getEventType() != XmlPullParser.END_DOCUMENT) { + parser.next(); + } + } catch (IOException | XmlPullParserException e) { + throw new RuntimeException("Unable to read html export file", e); + } + } + private void checkReadEntries(Collection entries) { List vectors = VaultEntries.get(); assertEquals(vectors.size(), entries.size()); From a4812c530d1d3a915a074b8f376143d20188dbfd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Sch=C3=A4ttgen?= Date: Wed, 22 Jan 2025 18:33:54 +0100 Subject: [PATCH 03/25] Fix obstructing snackbar padding --- .../aegis/ui/ImportEntriesActivity.java | 36 ++++++++++++++++--- .../res/layout/activity_import_entries.xml | 1 - 2 files changed, 31 insertions(+), 6 deletions(-) diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java index 9a5cec40..da04bc3c 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/ImportEntriesActivity.java @@ -52,6 +52,7 @@ import java.util.stream.Collectors; public class ImportEntriesActivity extends AegisActivity { private View _view; private Menu _menu; + private RecyclerView _entriesView; private ImportEntriesAdapter _adapter; private FabScrollHelper _fabScrollHelper; @@ -74,8 +75,8 @@ public class ImportEntriesActivity extends AegisActivity { bar.setDisplayHomeAsUpEnabled(true); _adapter = new ImportEntriesAdapter(); - RecyclerView entriesView = findViewById(R.id.list_entries); - entriesView.addOnScrollListener(new RecyclerView.OnScrollListener() { + _entriesView = findViewById(R.id.list_entries); + _entriesView.addOnScrollListener(new RecyclerView.OnScrollListener() { @Override public void onScrolled(@NonNull RecyclerView recyclerView, int dx, int dy) { super.onScrolled(recyclerView, dx, dy); @@ -84,9 +85,9 @@ public class ImportEntriesActivity extends AegisActivity { }); LinearLayoutManager layoutManager = new LinearLayoutManager(this); - entriesView.setLayoutManager(layoutManager); - entriesView.setAdapter(_adapter); - entriesView.setNestedScrollingEnabled(false); + _entriesView.setLayoutManager(layoutManager); + _entriesView.setAdapter(_adapter); + _entriesView.setNestedScrollingEnabled(false); FloatingActionButton fab = findViewById(R.id.fab); fab.setOnClickListener(v -> { @@ -358,6 +359,31 @@ public class ImportEntriesActivity extends AegisActivity { _adapter.setCheckboxStates(duplicateEntries, false); Snackbar snackbar = Snackbar.make(_view, getResources().getQuantityString(R.plurals.import_duplicate_toast, duplicateEntries.size(), duplicateEntries.size()), Snackbar.LENGTH_INDEFINITE); + snackbar.addCallback(new Snackbar.Callback() { + @Override + public void onShown(Snackbar sb) { + int snackbarHeight = sb.getView().getHeight(); + + _entriesView.setPadding( + _entriesView.getPaddingLeft(), + _entriesView.getPaddingTop(), + _entriesView.getPaddingRight(), + _entriesView.getPaddingBottom() + snackbarHeight * 2 + ); + } + + @Override + public void onDismissed(Snackbar sb, int event) { + int snackbarHeight = sb.getView().getHeight(); + + _entriesView.setPadding( + _entriesView.getPaddingLeft(), + _entriesView.getPaddingTop(), + _entriesView.getPaddingRight(), + _entriesView.getPaddingBottom() - snackbarHeight * 2 + ); + } + }); snackbar.setAction(R.string.undo, new View.OnClickListener() { @Override public void onClick(View v) { diff --git a/app/src/main/res/layout/activity_import_entries.xml b/app/src/main/res/layout/activity_import_entries.xml index da32516c..deba9ac1 100644 --- a/app/src/main/res/layout/activity_import_entries.xml +++ b/app/src/main/res/layout/activity_import_entries.xml @@ -23,7 +23,6 @@ android:id="@+id/list_entries" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingBottom="60dp" android:clipToPadding="false" android:scrollbars="vertical" app:layout_behavior="@string/appbar_scrolling_view_behavior"/> From 3d50ab1b653e57f5478810ac3a9097e8b9809b3f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Sch=C3=A4ttgen?= Date: Wed, 22 Jan 2025 22:06:57 +0100 Subject: [PATCH 04/25] Improve color contrast on hidden codes --- .../java/com/beemdevelopment/aegis/ui/views/EntryHolder.java | 5 ++++- app/src/main/res/values/attrs.xml | 1 + app/src/main/res/values/themes.xml | 4 ++++ 3 files changed, 9 insertions(+), 1 deletion(-) diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java index ba86e9cd..cca83dfd 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryHolder.java @@ -369,9 +369,11 @@ public class EntryHolder extends RecyclerView.ViewHolder { public void hideCode() { String code = getOtp(); String hiddenText = code.replaceAll("\\S", Character.toString(HIDDEN_CHAR)); + stopExpirationAnimation(); + updateTextViewWithDots(_profileCode, hiddenText, code); updateTextViewWithDots(_nextProfileCode, hiddenText, code); - stopExpirationAnimation(); + _hidden = true; } @@ -384,6 +386,7 @@ public class EntryHolder extends RecyclerView.ViewHolder { float dotsWidth = paint.measureText(hiddenCode); float scaleFactor = codeWidth / dotsWidth; scaleFactor = (float)(Math.round(scaleFactor * 10.0) / 10.0); + textView.setTextColor(MaterialColors.getColor(textView, R.attr.colorCodeHidden)); // If scale is higher or equal to 0.8, do nothing and proceed with the normal text rendering if (scaleFactor >= 0.8) { diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 0c24c596..8baea4f4 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -8,6 +8,7 @@ + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index 7ae582e9..d135ec24 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -62,6 +62,7 @@ @color/aegis_theme_light_success @color/aegis_theme_light_onSurfaceDim ?attr/colorPrimary + ?attr/colorOutlineVariant ?attr/colorSurfaceVariant ?attr/colorOnSurfaceVariant @@ -132,6 +133,7 @@ @color/aegis_theme_dark_success @color/aegis_theme_dark_onSurfaceDim ?attr/colorPrimary + ?attr/colorOutlineVariant ?attr/colorSurfaceVariant ?attr/colorOnSurfaceVariant @@ -159,6 +161,7 @@ #000000 #000000 @android:color/white + #2F2F2F @android:color/white @@ -179,6 +182,7 @@ #000000 #000000 @android:color/white + #2F2F2F @android:color/white From e8d712ec7189282dbd5aaeaacf0b506ef1cd9636 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Fri, 24 Jan 2025 14:34:41 +0100 Subject: [PATCH 05/25] Flush temporary export file before starting ExportTask The previous logic was not an issue because FileOutputStream is unbuffered, but still, this is more correct. --- .../preferences/ImportExportPreferencesFragment.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/ImportExportPreferencesFragment.java b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/ImportExportPreferencesFragment.java index f544bf6f..b995b235 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/ImportExportPreferencesFragment.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/fragments/preferences/ImportExportPreferencesFragment.java @@ -518,11 +518,10 @@ public class ImportExportPreferencesFragment extends PreferencesFragment { file = File.createTempFile(VaultRepository.FILENAME_PREFIX_EXPORT + "-", ".json", getExportCacheDir()); outStream = new FileOutputStream(file); cb.exportVault(outStream); - - new ExportTask(requireContext(), new ExportResultListener()).execute(getLifecycle(), new ExportTask.Params(file, uri)); } catch (VaultRepositoryException | IOException e) { e.printStackTrace(); Dialogs.showErrorDialog(requireContext(), R.string.exporting_vault_error, e); + return; } finally { try { if (outStream != null) { @@ -532,6 +531,8 @@ public class ImportExportPreferencesFragment extends PreferencesFragment { e.printStackTrace(); } } + + new ExportTask(requireContext(), new ExportResultListener()).execute(getLifecycle(), new ExportTask.Params(file, uri)); }, _exportFilter); _exportFilter = null; } From 78ee38ba7d60794944f7ee463c468e22dbbd7fe0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Sch=C3=A4ttgen?= Date: Sun, 1 Dec 2024 23:08:05 +0100 Subject: [PATCH 06/25] Add ability to multiselect groups --- .../beemdevelopment/aegis/Preferences.java | 4 ++++ .../aegis/ui/MainActivity.java | 23 ++++++++----------- app/src/main/res/layout/activity_main.xml | 3 +-- app/src/main/res/values/strings.xml | 2 ++ app/src/main/res/xml/preferences_behavior.xml | 7 ++++++ 5 files changed, 24 insertions(+), 15 deletions(-) diff --git a/app/src/main/java/com/beemdevelopment/aegis/Preferences.java b/app/src/main/java/com/beemdevelopment/aegis/Preferences.java index 95f3ed4c..6e86f4f7 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/Preferences.java +++ b/app/src/main/java/com/beemdevelopment/aegis/Preferences.java @@ -86,6 +86,10 @@ public class Preferences { return _prefs.getBoolean("pref_tap_to_reveal", false); } + public boolean isGroupMultiselectEnabled() { + return _prefs.getBoolean("pref_groups_multiselect", false); + } + public boolean isEntryHighlightEnabled() { return _prefs.getBoolean("pref_highlight_entry", false); } diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java index 1dc5cbd5..29ac2c69 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java @@ -274,6 +274,7 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene private void initializeGroups() { _groupChip.removeAllViews(); + _groupChip.setSingleSelection(!_prefs.isGroupMultiselectEnabled()); for (VaultGroup group : _groups) { addChipTo(_groupChip, new VaultGroupModel(group)); @@ -313,29 +314,24 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene } chip.setOnCheckedChangeListener((group1, isChecked) -> { - Set groupFilter = new HashSet<>(); if (_actionMode != null) { _actionMode.finish(); } setSaveChipVisibility(true); - if (!isChecked) { - group1.setChecked(false); + // Reset group filter if last checked group gets unchecked + if (!isChecked && _groupFilter.size() == 1) { + Set groupFilter = new HashSet<>(); + + chipGroup.clearCheck(); _groupFilter = groupFilter; _entryListView.setGroupFilter(groupFilter); return; } - Object chipTag = group1.getTag(); - if (chipTag == GroupPlaceholderType.NO_GROUP) { - groupFilter.add(null); - } else { - groupFilter = getGroupFilter(chipGroup); - } - - _groupFilter = groupFilter; - _entryListView.setGroupFilter(groupFilter); + _groupFilter = getGroupFilter(chipGroup); + _entryListView.setGroupFilter(_groupFilter); }); chipGroup.addView(chip); @@ -368,8 +364,10 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene private static Set getGroupFilter(ChipGroup chipGroup) { return chipGroup.getCheckedChipIds().stream() + .filter(Objects::nonNull) .map(i -> { Chip chip = chipGroup.findViewById(i); + if (chip.getTag() instanceof VaultGroupModel) { VaultGroupModel group = (VaultGroupModel) chip.getTag(); return group.getUUID(); @@ -377,7 +375,6 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene return null; }) - .filter(Objects::nonNull) .collect(Collectors.toSet()); } diff --git a/app/src/main/res/layout/activity_main.xml b/app/src/main/res/layout/activity_main.xml index 7a288e1e..b2ca2d00 100644 --- a/app/src/main/res/layout/activity_main.xml +++ b/app/src/main/res/layout/activity_main.xml @@ -39,8 +39,7 @@ android:id="@+id/groupChipGroup" android:layout_width="match_parent" android:layout_height="wrap_content" - app:selectionRequired="true" - app:singleSelection="true"/> + app:selectionRequired="true"/> diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 2725c77f..b2ec52ff 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -373,6 +373,8 @@ Highlight tokens when tapped Make tokens easier to distinguish from each other by temporarily highlighting them when tapped + Multiselect groups + Allow the selection of multiple groups at the same time Minimize on copy Minimize the app after copying a token Copy tokens to the clipboard diff --git a/app/src/main/res/xml/preferences_behavior.xml b/app/src/main/res/xml/preferences_behavior.xml index ac2fa0d2..46f636f4 100644 --- a/app/src/main/res/xml/preferences_behavior.xml +++ b/app/src/main/res/xml/preferences_behavior.xml @@ -26,6 +26,13 @@ android:title="@string/pref_copy_behavior_title" app:iconSpaceReserved="false"/> + + Date: Mon, 24 Feb 2025 13:41:42 +0100 Subject: [PATCH 07/25] Update divider decoration when filter/sort changes This fixes an issue where the item decoration may be wrong in some cases. For example, adding a new entry to the bottom of the list may not update the decoration of the item that was previously the last one in the list. To reproduce, use this vault: https://alexbakker.me/u/mov4455gp5.json. Start without a group filter, apply sorting based on Issuer (A to Z) and enable group multiselect. Then: - Tap the "Test" chip - Tap the "Test2" chip - Tap the "No group" chip - Notice that the offset between the last 2 entries looks wrong: https://alexbakker.me/u/nedcyiro2q.png Probably introduced in 9131cae944f354c95873aa1c257e87b0c9b1f210. --- .../main/java/com/beemdevelopment/aegis/ui/MainActivity.java | 2 +- .../com/beemdevelopment/aegis/ui/views/EntryListView.java | 4 +++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java index 0a06c0e8..f1b27f21 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/MainActivity.java @@ -220,8 +220,8 @@ public class MainActivity extends AegisActivity implements EntryListView.Listene _entryListView.setPauseFocused(_prefs.isPauseFocusedEnabled()); _entryListView.setTapToReveal(_prefs.isTapToRevealEnabled()); _entryListView.setTapToRevealTime(_prefs.getTapToRevealTime()); - _entryListView.setSortCategory(_prefs.getCurrentSortCategory(), false); _entryListView.setViewMode(_prefs.getCurrentViewMode()); + _entryListView.setSortCategory(_prefs.getCurrentSortCategory(), false); _entryListView.setCopyBehavior(_prefs.getCopyBehavior()); _entryListView.setSearchBehaviorMask(_prefs.getSearchBehaviorMask()); _prefGroupFilter = _prefs.getGroupFilter(); diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java index 3d359290..ff6ff97a 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/views/EntryListView.java @@ -200,6 +200,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener { _adapter.setGroupFilter(groups); _touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed()); updateEmptyState(); + updateDividerDecoration(); } public void setIsLongPressDragEnabled(boolean enabled) { @@ -232,6 +233,7 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener { public void setSortCategory(SortCategory sortCategory, boolean apply) { _adapter.setSortCategory(sortCategory, apply); _touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed()); + updateDividerDecoration(); } public void setUsageCounts(Map usageCounts) { @@ -253,8 +255,8 @@ public class EntryListView extends Fragment implements EntryAdapter.Listener { public void setSearchFilter(String search) { _adapter.setSearchFilter(search); _touchCallback.setIsLongPressDragEnabled(_adapter.isDragAndDropAllowed()); - updateEmptyState(); + updateDividerDecoration(); } public void setSelectedEntry(VaultEntry entry) { From fbfdd50069db6ec720b3a3d20bb1eced9dd3b6dd Mon Sep 17 00:00:00 2001 From: jahway603 Date: Tue, 15 Apr 2025 18:54:46 -0400 Subject: [PATCH 08/25] [FAQ] clarified about which password to use when importing your vault to resolve Issue #1636 --- FAQ.md | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/FAQ.md b/FAQ.md index 815490aa..6d514574 100644 --- a/FAQ.md +++ b/FAQ.md @@ -86,6 +86,14 @@ Another common setup is to configure Aegis to back up to a folder on local storage of your device and then have a separate app (like [Syncthing](https://syncthing.net/)) sync that folder anywhere you want. +## Encrypted Backups + +### Why do I not get prompted to enter an encryption password when exporting? + +Aegis uses the same password you have configured to encrypt your vault as the +password which is used when exporting and importing your vault; so when prompted, +you will enter that when importing your vault. + ## Importing ### When importing from Authenticator Plus, an error is shown claiming that Accounts.txt is missing From c81e08bf1feb72e48f105abdca56c9f24ae5caa4 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Wed, 23 Apr 2025 15:51:49 +0200 Subject: [PATCH 09/25] Fall back to default values in the FreeOTP importer --- .../aegis/importers/FreeOtpImporter.java | 6 +++--- .../aegis/importers/DatabaseImporterTest.java | 9 +++++++++ .../aegis/importers/freeotp_v2_null_algo.xml | Bin 0 -> 1066 bytes 3 files changed, 12 insertions(+), 3 deletions(-) create mode 100644 app/src/test/resources/com/beemdevelopment/aegis/importers/freeotp_v2_null_algo.xml diff --git a/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java b/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java index 8b96299b..0266ca95 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java +++ b/app/src/main/java/com/beemdevelopment/aegis/importers/FreeOtpImporter.java @@ -298,8 +298,8 @@ public class FreeOtpImporter extends DatabaseImporter { private static VaultEntry convertEntry(JSONObject obj) throws DatabaseImporterEntryException { try { String type = obj.getString("type").toLowerCase(Locale.ROOT); - String algo = obj.getString("algo"); - int digits = obj.getInt("digits"); + String algo = obj.optString("algo", OtpInfo.DEFAULT_ALGORITHM); + int digits = obj.optInt("digits", OtpInfo.DEFAULT_DIGITS); byte[] secret = toBytes(obj.getJSONArray("secret")); String issuer = obj.getString("issuerExt"); @@ -308,7 +308,7 @@ public class FreeOtpImporter extends DatabaseImporter { OtpInfo info; switch (type) { case "totp": - int period = obj.getInt("period"); + int period = obj.optInt("period", TotpInfo.DEFAULT_PERIOD); if (issuer.equals("Steam")) { info = new SteamInfo(secret, algo, digits, period); } else { diff --git a/app/src/test/java/com/beemdevelopment/aegis/importers/DatabaseImporterTest.java b/app/src/test/java/com/beemdevelopment/aegis/importers/DatabaseImporterTest.java index 4742eabd..1457d118 100644 --- a/app/src/test/java/com/beemdevelopment/aegis/importers/DatabaseImporterTest.java +++ b/app/src/test/java/com/beemdevelopment/aegis/importers/DatabaseImporterTest.java @@ -257,6 +257,15 @@ public class DatabaseImporterTest { checkImportedEntries(entries); } + @Test + public void testImportFreeOtpV2NullAlgo() throws IOException, DatabaseImporterException, OtpInfoException { + List entries = importEncrypted(FreeOtpImporter.class, "freeotp_v2_null_algo.xml", encryptedState -> { + final char[] password = "test".toCharArray(); + return ((FreeOtpImporter.EncryptedState) encryptedState).decrypt(password); + }); + checkImportedEntries(entries); + } + @Test public void testImportFreeOtpPlus() throws IOException, DatabaseImporterException, OtpInfoException { List entries = importPlain(FreeOtpPlusImporter.class, "freeotp_plus.json"); diff --git a/app/src/test/resources/com/beemdevelopment/aegis/importers/freeotp_v2_null_algo.xml b/app/src/test/resources/com/beemdevelopment/aegis/importers/freeotp_v2_null_algo.xml new file mode 100644 index 0000000000000000000000000000000000000000..dc890c5c66d0c0ba93b80a9e815c512863d94c7c GIT binary patch literal 1066 zcma)*O>Wab7=}%&Qp%1E2N+nPnbu>EJ$5#wv?;BsLZoJgD=~?kCe%NXJ*A1F+=xpc zPQaEsa0lMc4ivE>QsVLVKi~7_%TH&gshww^%xANIXJ`5TnQ5k%X1=rglAJzo&j^%W=ad>gFG7OLg@+`H zi}Ca$O7eLKdyF83cVF|6`|dRjGBC9%GMm*64FWGJQCYM_M(88DZ5wi>VQ$YH<4Wmr z^yn;}&tc&*Y)cNS+pKJ_e^!|B)!C6u*qW`a^Xh+=DJ-R1r@vyern$@N{=#ChCs|oB zzA>Gbf0=SK%5p?pnx-ldVwdv_ufzAl0X5w#OoQuPWJ|lXyJE#repA&mJ1r11cyn=b z>fPgQGie|_R4Dr8xL(d}mLiMwkklbX27nUJ2b>h%c?l-51R9Mc2O*CL8nM8tuzH}< zn_c>_Mnnsg@QlbzXNA23eS4Ok=A;Qi4`LFE09X(K%E;q@E3x;OvIsu12s7vb1$+90ZI iq*GjpMuIj28V}b~vfG>+bM3Y?utYe(L`Z$TUi<>?SPG*6 literal 0 HcmV?d00001 From afa1fbd3ae8ef36b266a4032be1c75abac0ffcb8 Mon Sep 17 00:00:00 2001 From: Alexander Bakker Date: Wed, 23 Apr 2025 16:20:34 +0200 Subject: [PATCH 10/25] Specify country code for the Portuguese translation --- app/src/main/res/values/arrays.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/app/src/main/res/values/arrays.xml b/app/src/main/res/values/arrays.xml index 17ac962e..bf4c1c64 100644 --- a/app/src/main/res/values/arrays.xml +++ b/app/src/main/res/values/arrays.xml @@ -100,7 +100,7 @@ lt fa pl - pt + pt_PT pt_BR ro ru From 03c00d51ba1f7f4dbea151bda33c96253489ccfd Mon Sep 17 00:00:00 2001 From: mimi89999 Date: Wed, 19 Mar 2025 09:43:56 +0100 Subject: [PATCH 11/25] Add Crowdin config file and update crowdin-cli Co-authored-by: Alexander Bakker --- .crowdin/config.example.yml | 10 ---------- .github/workflows/crowdin.yml | 14 ++++---------- crowdin.yml | 14 ++++++++++++++ 3 files changed, 18 insertions(+), 20 deletions(-) delete mode 100644 .crowdin/config.example.yml create mode 100644 crowdin.yml diff --git a/.crowdin/config.example.yml b/.crowdin/config.example.yml deleted file mode 100644 index 4ef788a3..00000000 --- a/.crowdin/config.example.yml +++ /dev/null @@ -1,10 +0,0 @@ -project_id: "372633" -api_token: "" -base_path: "../app/src/main" -base_url: "https://api.crowdin.com" -preserve_hierarchy: true - -files: -- source: "res/values/strings.xml" - dest: "strings.xml" - translation: "res/values-%android_code%/%original_file_name%" diff --git a/.github/workflows/crowdin.yml b/.github/workflows/crowdin.yml index e0fd559c..e7898362 100644 --- a/.github/workflows/crowdin.yml +++ b/.github/workflows/crowdin.yml @@ -13,19 +13,13 @@ jobs: - uses: actions/checkout@v4 - name: Install crowdin-cli run: | - wget https://github.com/crowdin/crowdin-cli/releases/download/3.7.2/crowdin-cli.zip - echo "ee9f838b819ccedc33c9b2537055e5ba7d7934561b24df1e1a6274cbd6e27f2d crowdin-cli.zip" | sha256sum -c + wget https://github.com/crowdin/crowdin-cli/releases/download/4.6.1/crowdin-cli.zip + echo "7afd70de3a747ac631a5bad7866008163ae1d50c4606b5773f0b90a5481ffde2 crowdin-cli.zip" | sha256sum -c unzip crowdin-cli.zip -d crowdin-cli - name: Upload to Crowdin env: - CROWDIN_TOKEN: "${{ secrets.CROWDIN_TOKEN }}" + CROWDIN_PERSONAL_TOKEN: "${{ secrets.CROWDIN_TOKEN }}" run: | - java -jar ./crowdin-cli/3.7.2/crowdin-cli.jar upload sources \ + java -jar ./crowdin-cli/4.6.1/crowdin-cli.jar upload sources \ --no-progress \ - --token "$CROWDIN_TOKEN" \ - --project-id 372633 \ - --base-path app/src/main \ - --source res/values/strings.xml \ - --translation "res/values-%android_code%/%original_file_name%" \ - --dest strings.xml \ --branch master diff --git a/crowdin.yml b/crowdin.yml new file mode 100644 index 00000000..59912b50 --- /dev/null +++ b/crowdin.yml @@ -0,0 +1,14 @@ +project_id: "372633" +preserve_hierarchy: true +base_path: "app/src/main" +base_url: "https://api.crowdin.com" +api_token_env: "CROWDIN_PERSONAL_TOKEN" +files: + - type: "android" + source: "res/values/strings.xml" + dest: "strings.xml" + translation: "res/values-%android_code%/%original_file_name%" + - type: "android" + source: "res/values-v29/strings.xml" + dest: "strings-v29.xml" + translation: "res/values-%android_code%-v29/%original_file_name%" From 3818f9408d8ae1831cfee1a8bdd53c1572f1321a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Michael=20Sch=C3=A4ttgen?= Date: Mon, 24 Feb 2025 19:39:41 +0100 Subject: [PATCH 12/25] Add brightness slider for transfer activity --- .../aegis/ui/TransferEntriesActivity.java | 31 ++++++++++++-- .../main/res/layout/activity_share_entry.xml | 42 ++++++++++++------- app/src/main/res/values/strings.xml | 1 + 3 files changed, 55 insertions(+), 19 deletions(-) diff --git a/app/src/main/java/com/beemdevelopment/aegis/ui/TransferEntriesActivity.java b/app/src/main/java/com/beemdevelopment/aegis/ui/TransferEntriesActivity.java index a4deae57..a0559d7a 100644 --- a/app/src/main/java/com/beemdevelopment/aegis/ui/TransferEntriesActivity.java +++ b/app/src/main/java/com/beemdevelopment/aegis/ui/TransferEntriesActivity.java @@ -10,6 +10,7 @@ import android.graphics.Color; import android.os.Build; import android.os.Bundle; import android.os.PersistableBundle; +import android.provider.Settings; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; @@ -47,6 +48,8 @@ public class TransferEntriesActivity extends AegisActivity { private Button _previousButton; private Button _copyButton; private int _currentEntryCount = 1; + private float _deviceBrightness; + private boolean _isMaxBrightnessSet = false; @Override protected void onCreate(Bundle savedInstanceState) { @@ -146,13 +149,33 @@ public class TransferEntriesActivity extends AegisActivity { _qrImage.getViewTreeObserver().removeOnGlobalLayoutListener(this); } }); + + _deviceBrightness = getSystemBrightness(); + _qrImage.setOnClickListener(v -> { + if (!_isMaxBrightnessSet) { + setBrightness(1f); + _isMaxBrightnessSet = true; + } else { + setBrightness(_deviceBrightness); + _isMaxBrightnessSet = false; + } + }); } - @Override - public void onAttachedToWindow() { - // Max brightness to make the QR codes easier to scan + private float getSystemBrightness() { + int brightness = 0; + try { + brightness = Settings.System.getInt(getContentResolver(), Settings.System.SCREEN_BRIGHTNESS); + } catch (Settings.SettingNotFoundException e) { + e.printStackTrace(); + } + + return brightness / 255f; + } + + private void setBrightness(float brightnessAmount) { WindowManager.LayoutParams attrs = getWindow().getAttributes(); - attrs.screenBrightness = 1.0f; + attrs.screenBrightness = brightnessAmount; getWindow().setAttributes(attrs); } diff --git a/app/src/main/res/layout/activity_share_entry.xml b/app/src/main/res/layout/activity_share_entry.xml index 3050c056..455f3356 100644 --- a/app/src/main/res/layout/activity_share_entry.xml +++ b/app/src/main/res/layout/activity_share_entry.xml @@ -25,8 +25,8 @@ android:id="@+id/layoutShareEntry" android:layout_width="match_parent" android:layout_height="match_parent" - android:paddingHorizontal="30dp" - android:layout_marginTop="?attr/actionBarSize"> + android:layout_marginTop="?attr/actionBarSize" + android:paddingHorizontal="30dp"> + app:layout_constraintStart_toStartOf="parent" + app:layout_constraintTop_toTopOf="parent" /> + app:layout_constraintTop_toTopOf="parent" + app:layout_constraintVertical_bias="0.3" + app:shapeAppearanceOverlay="@style/ShapeAppearanceOverlay.Aegis.ImageView.Rounded" /> + app:layout_constraintTop_toBottomOf="@+id/ivQrCode" + tools:text="Issuer" /> + app:layout_constraintTop_toBottomOf="@+id/tvIssuer" + tools:text="Accountname" />