Fix locale on older Android, Clean up code

This commit is contained in:
MrLetsplay 2023-09-27 15:40:45 +02:00
parent 494f9d66fc
commit 2e88d021bb
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
28 changed files with 147 additions and 194 deletions

View File

@ -2,7 +2,6 @@ package com.cringe_studios.cringe_authenticator;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager; import android.view.WindowManager;
import androidx.activity.result.ActivityResultLauncher; import androidx.activity.result.ActivityResultLauncher;
@ -50,7 +49,7 @@ public class BaseActivity extends AppCompatActivity {
public void setLocale(Locale locale) { public void setLocale(Locale locale) {
Locale.setDefault(locale); Locale.setDefault(locale);
Configuration config = new Configuration(); Configuration config = new Configuration();
config.locale = locale; config.setLocale(locale);
getResources().updateConfiguration(config, getResources().getDisplayMetrics()); getResources().updateConfiguration(config, getResources().getDisplayMetrics());
} }

View File

@ -7,8 +7,6 @@ import android.os.Bundle;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast; import android.widget.Toast;
import androidx.appcompat.app.AppCompatActivity;
import com.cringe_studios.cringe_authenticator.databinding.ActivityIntroBinding; import com.cringe_studios.cringe_authenticator.databinding.ActivityIntroBinding;
import com.cringe_studios.cringe_authenticator.unlock.UnlockActivity; import com.cringe_studios.cringe_authenticator.unlock.UnlockActivity;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil; import com.cringe_studios.cringe_authenticator.util.SettingsUtil;

View File

@ -1,8 +1,8 @@
package com.cringe_studios.cringe_authenticator; package com.cringe_studios.cringe_authenticator;
import android.content.res.Configuration;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.Menu; import android.view.Menu;
import android.view.MenuItem; import android.view.MenuItem;
import android.view.View; import android.view.View;
@ -32,14 +32,12 @@ import com.cringe_studios.cringe_authenticator.scanner.QRScannerContract;
import com.cringe_studios.cringe_authenticator.util.DialogUtil; import com.cringe_studios.cringe_authenticator.util.DialogUtil;
import com.cringe_studios.cringe_authenticator.util.NavigationUtil; import com.cringe_studios.cringe_authenticator.util.NavigationUtil;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase; import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import com.cringe_studios.cringe_authenticator.util.StyledDialogBuilder; import com.cringe_studios.cringe_authenticator.util.StyledDialogBuilder;
import com.cringe_studios.cringe_authenticator.util.ThemeUtil; import com.cringe_studios.cringe_authenticator.util.ThemeUtil;
import com.cringe_studios.cringe_authenticator_library.OTPType; import com.cringe_studios.cringe_authenticator_library.OTPType;
import com.google.mlkit.vision.common.InputImage; import com.google.mlkit.vision.common.InputImage;
import java.io.IOException; import java.io.IOException;
import java.util.Locale;
public class MainActivity extends BaseActivity { public class MainActivity extends BaseActivity {
@ -61,7 +59,7 @@ public class MainActivity extends BaseActivity {
private boolean fullyLaunched; private boolean fullyLaunched;
private boolean lockOnPause = true; private boolean lockOnStop = true;
@Override @Override
protected void onCreate(Bundle savedInstanceState) { protected void onCreate(Bundle savedInstanceState) {
@ -70,7 +68,7 @@ public class MainActivity extends BaseActivity {
qrScanner = new QRScanner(); qrScanner = new QRScanner();
startQRCodeScan = registerForActivityResult(new QRScannerContract(), obj -> { startQRCodeScan = registerForActivityResult(new QRScannerContract(), obj -> {
lockOnPause = true; lockOnStop = true;
if(obj == null) return; // Cancelled if(obj == null) return; // Cancelled
@ -98,19 +96,13 @@ public class MainActivity extends BaseActivity {
} }
if(code.isMigrationPart()) { if(code.isMigrationPart()) {
new StyledDialogBuilder(this) // TODO: duplicated from QRScannerActivity DialogUtil.showYesNo(this, R.string.qr_scanner_migration_title, R.string.qr_scanner_migration_message, () -> {
.setTitle(R.string.qr_scanner_migration_title) Fragment fragment = NavigationUtil.getCurrentFragment(this);
.setMessage(R.string.qr_scanner_migration_message) if (fragment instanceof GroupFragment) {
.setPositiveButton(R.string.yes, (d, which) -> { GroupFragment frag = (GroupFragment) fragment;
Fragment fragment = NavigationUtil.getCurrentFragment(this); for (OTPData dt : code.getOTPs()) frag.addOTP(dt);
if (fragment instanceof GroupFragment) { }
GroupFragment frag = (GroupFragment) fragment; }, null);
for (OTPData dt : code.getOTPs()) frag.addOTP(dt);
}
})
.setNegativeButton(R.string.no, (d, which) -> {})
.show()
.setCanceledOnTouchOutside(false);
}else { }else {
Fragment fragment = NavigationUtil.getCurrentFragment(this); Fragment fragment = NavigationUtil.getCurrentFragment(this);
if (fragment instanceof GroupFragment) { if (fragment instanceof GroupFragment) {
@ -149,7 +141,7 @@ public class MainActivity extends BaseActivity {
private void launchApp() { private void launchApp() {
fullyLaunched = true; fullyLaunched = true;
lockOnPause = true; lockOnStop = true;
binding = ActivityMainBinding.inflate(getLayoutInflater()); binding = ActivityMainBinding.inflate(getLayoutInflater());
setContentView(binding.getRoot()); setContentView(binding.getRoot());
@ -225,7 +217,7 @@ public class MainActivity extends BaseActivity {
} }
public void scanCode(MenuItem item) { public void scanCode(MenuItem item) {
lockOnPause = false; lockOnStop = false;
startQRCodeScan.launch(null); startQRCodeScan.launch(null);
} }
@ -324,18 +316,18 @@ public class MainActivity extends BaseActivity {
} }
public void promptPickBackupFileSave(String name, Consumer<Uri> callback) { public void promptPickBackupFileSave(String name, Consumer<Uri> callback) {
this.lockOnPause = false; this.lockOnStop = false;
this.pickBackupFileSaveCallback = uri -> { this.pickBackupFileSaveCallback = uri -> {
lockOnPause = true; lockOnStop = true;
callback.accept(uri); callback.accept(uri);
}; };
pickBackupFileSave.launch(name); pickBackupFileSave.launch(name);
} }
public void promptPickBackupFileLoad(Consumer<Uri> callback) { public void promptPickBackupFileLoad(Consumer<Uri> callback) {
this.lockOnPause = false; this.lockOnStop = false;
this.pickBackupFileLoadCallback = uri -> { this.pickBackupFileLoadCallback = uri -> {
lockOnPause = true; lockOnStop = true;
callback.accept(uri); callback.accept(uri);
}; };
pickBackupFileLoad.launch(new String[]{"application/json"}); pickBackupFileLoad.launch(new String[]{"application/json"});
@ -343,14 +335,15 @@ public class MainActivity extends BaseActivity {
@Override @Override
public void recreate() { public void recreate() {
lockOnPause = false; lockOnStop = false;
super.recreate(); super.recreate();
} }
@Override @Override
protected void onStop() { protected void onStop() {
super.onStop(); super.onStop();
if(lockOnPause) OTPDatabase.unloadDatabase(); Log.d("STOP", lockOnStop+"");
if(lockOnStop) OTPDatabase.unloadDatabase();
} }
@Override @Override

View File

@ -1,16 +1,12 @@
package com.cringe_studios.cringe_authenticator.backup; package com.cringe_studios.cringe_authenticator.backup;
import android.util.Base64; import android.util.Base64;
import android.util.Log;
import com.cringe_studios.cringe_authenticator.crypto.CryptoException; import com.cringe_studios.cringe_authenticator.crypto.CryptoException;
import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters; import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters;
import com.cringe_studios.cringe_authenticator.model.OTPData;
import com.cringe_studios.cringe_authenticator.proto.OTPMigration;
import com.cringe_studios.cringe_authenticator.util.BackupException; import com.cringe_studios.cringe_authenticator.util.BackupException;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase; import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException; import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;

View File

@ -1,7 +1,5 @@
package com.cringe_studios.cringe_authenticator.backup; package com.cringe_studios.cringe_authenticator.backup;
import org.jetbrains.annotations.Contract;
public class BackupGroup { public class BackupGroup {
public String id; public String id;

View File

@ -9,21 +9,14 @@ import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters;
import com.cringe_studios.cringe_authenticator.util.BackupException; import com.cringe_studios.cringe_authenticator.util.BackupException;
import com.cringe_studios.cringe_authenticator.util.IOUtil; import com.cringe_studios.cringe_authenticator.util.IOUtil;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase; import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil; import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import com.google.gson.JsonArray;
import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException; import com.google.gson.JsonSyntaxException;
import org.json.JSONObject;
import java.io.IOException; import java.io.IOException;
import java.io.InputStream; import java.io.InputStream;
import java.io.OutputStream; import java.io.OutputStream;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat; import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date; import java.util.Date;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;

View File

@ -1,15 +1,12 @@
package com.cringe_studios.cringe_authenticator.crypto; package com.cringe_studios.cringe_authenticator.crypto;
import android.content.Context;
import android.os.Build; import android.os.Build;
import android.security.keystore.KeyGenParameterSpec; import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProperties; import android.security.keystore.KeyProperties;
import android.util.Log;
import androidx.annotation.RequiresApi; import androidx.annotation.RequiresApi;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase; import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import org.bouncycastle.crypto.generators.Argon2BytesGenerator; import org.bouncycastle.crypto.generators.Argon2BytesGenerator;
import org.bouncycastle.crypto.params.Argon2Parameters; import org.bouncycastle.crypto.params.Argon2Parameters;
@ -25,7 +22,6 @@ import java.security.NoSuchProviderException;
import java.security.SecureRandom; import java.security.SecureRandom;
import java.security.UnrecoverableKeyException; import java.security.UnrecoverableKeyException;
import java.security.cert.CertificateException; import java.security.cert.CertificateException;
import java.util.Arrays;
import java.util.UUID; import java.util.UUID;
import javax.crypto.BadPaddingException; import javax.crypto.BadPaddingException;

View File

@ -20,8 +20,6 @@ import com.cringe_studios.cringe_authenticator.util.DialogUtil;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase; import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException; import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil; import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import com.cringe_studios.cringe_authenticator.util.StyledDialogBuilder;
import com.cringe_studios.cringe_authenticator_library.OTPType;
import java.util.List; import java.util.List;
@ -153,11 +151,12 @@ public class GroupFragment extends NamedFragment {
for(OTPListItem item : items) { for(OTPListItem item : items) {
OTPData data = item.getOTPData(); OTPData data = item.getOTPData();
OTPDatabase.getLoadedDatabase().addOTP(group, data); OTPDatabase.getLoadedDatabase().addOTP(group, data);
OTPDatabase.saveDatabase(requireContext(), SettingsUtil.getCryptoParameters(requireContext()));
otpListAdapter.remove(data); otpListAdapter.remove(data);
} }
OTPDatabase.saveDatabase(requireContext(), SettingsUtil.getCryptoParameters(requireContext()));
saveOTPs(); saveOTPs();
otpListAdapter.finishEditing();
} catch (OTPDatabaseException | CryptoException e) { } catch (OTPDatabaseException | CryptoException e) {
DialogUtil.showErrorDialog(requireContext(), e.toString()); DialogUtil.showErrorDialog(requireContext(), e.toString());
} }
@ -171,19 +170,14 @@ public class GroupFragment extends NamedFragment {
List<OTPListItem> items = otpListAdapter.getSelectedCodes(); List<OTPListItem> items = otpListAdapter.getSelectedCodes();
new StyledDialogBuilder(requireContext()) DialogUtil.showYesNoCancel(requireContext(), R.string.otp_delete_title, R.string.otp_delete_message, () -> {
.setTitle(R.string.otp_delete_title) for(OTPListItem item : items) {
.setMessage(R.string.otp_delete_message) otpListAdapter.remove(item.getOTPData());
.setPositiveButton(R.string.yes, (d, w) -> { }
for(OTPListItem item : items) {
otpListAdapter.remove(item.getOTPData());
}
saveOTPs(); saveOTPs();
finishEditing(); finishEditing();
}) }, null, null);
.setNegativeButton(R.string.no, (d, w) -> {})
.show();
} }
public boolean isEditing() { public boolean isEditing() {

View File

@ -18,7 +18,6 @@ import com.cringe_studios.cringe_authenticator.util.NavigationUtil;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase; import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException; import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil; import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import com.cringe_studios.cringe_authenticator.util.StyledDialogBuilder;
import com.google.android.material.bottomsheet.BottomSheetDialogFragment; import com.google.android.material.bottomsheet.BottomSheetDialogFragment;
import java.util.List; import java.util.List;
@ -91,18 +90,13 @@ public class MenuDrawerFragment extends BottomSheetDialogFragment {
public void removeSelectedGroups() { public void removeSelectedGroups() {
if(!groupListAdapter.isEditing()) return; if(!groupListAdapter.isEditing()) return;
new StyledDialogBuilder(requireContext()) DialogUtil.showYesNo(requireContext(), R.string.group_delete_title, R.string.group_delete_message, () -> {
.setTitle(R.string.group_delete_title) for(GroupListItem item : groupListAdapter.getSelectedGroups()) {
.setMessage(R.string.group_delete_message) removeGroup(item.getGroupId());
.setPositiveButton(R.string.yes, (d, w) -> { }
for(GroupListItem item : groupListAdapter.getSelectedGroups()) {
removeGroup(item.getGroupId());
}
groupListAdapter.finishEditing(); groupListAdapter.finishEditing();
}) }, null);
.setNegativeButton(R.string.no, (d, w) -> {})
.show();
} }
public void removeGroup(String group) { public void removeGroup(String group) {

View File

@ -15,6 +15,7 @@ import androidx.annotation.Nullable;
import com.cringe_studios.cringe_authenticator.MainActivity; import com.cringe_studios.cringe_authenticator.MainActivity;
import com.cringe_studios.cringe_authenticator.R; import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.backup.BackupData; import com.cringe_studios.cringe_authenticator.backup.BackupData;
import com.cringe_studios.cringe_authenticator.backup.BackupUtil;
import com.cringe_studios.cringe_authenticator.crypto.BiometricKey; import com.cringe_studios.cringe_authenticator.crypto.BiometricKey;
import com.cringe_studios.cringe_authenticator.crypto.Crypto; import com.cringe_studios.cringe_authenticator.crypto.Crypto;
import com.cringe_studios.cringe_authenticator.crypto.CryptoException; import com.cringe_studios.cringe_authenticator.crypto.CryptoException;
@ -22,7 +23,6 @@ import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters;
import com.cringe_studios.cringe_authenticator.databinding.FragmentSettingsBinding; import com.cringe_studios.cringe_authenticator.databinding.FragmentSettingsBinding;
import com.cringe_studios.cringe_authenticator.util.Appearance; import com.cringe_studios.cringe_authenticator.util.Appearance;
import com.cringe_studios.cringe_authenticator.util.BackupException; import com.cringe_studios.cringe_authenticator.util.BackupException;
import com.cringe_studios.cringe_authenticator.backup.BackupUtil;
import com.cringe_studios.cringe_authenticator.util.BiometricUtil; import com.cringe_studios.cringe_authenticator.util.BiometricUtil;
import com.cringe_studios.cringe_authenticator.util.DialogUtil; import com.cringe_studios.cringe_authenticator.util.DialogUtil;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase; import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
@ -66,7 +66,6 @@ public class SettingsFragment extends NamedFragment {
if(locale.equals(SettingsUtil.getLocale(requireContext()))) return; if(locale.equals(SettingsUtil.getLocale(requireContext()))) return;
SettingsUtil.setLocale(requireContext(), locale); SettingsUtil.setLocale(requireContext(), locale);
//((MainActivity) requireActivity()).setLocale(locale);
requireActivity().recreate(); requireActivity().recreate();
} }
@ -79,6 +78,8 @@ public class SettingsFragment extends NamedFragment {
binding.settingsEnableEncryption.setChecked(SettingsUtil.isDatabaseEncrypted(requireContext())); binding.settingsEnableEncryption.setChecked(SettingsUtil.isDatabaseEncrypted(requireContext()));
binding.settingsEnableEncryption.setOnCheckedChangeListener((view, checked) -> { binding.settingsEnableEncryption.setOnCheckedChangeListener((view, checked) -> {
if(checked) { if(checked) {
if(SettingsUtil.isDatabaseEncrypted(requireContext())) return;
DialogUtil.showSetPasswordDialog(requireContext(), password -> { DialogUtil.showSetPasswordDialog(requireContext(), password -> {
CryptoParameters params = CryptoParameters.createNew(); CryptoParameters params = CryptoParameters.createNew();
Log.d("Crypto", "Created new crypto params"); Log.d("Crypto", "Created new crypto params");
@ -94,24 +95,29 @@ public class SettingsFragment extends NamedFragment {
binding.settingsBiometricLock.setEnabled(true); binding.settingsBiometricLock.setEnabled(true);
} }
} catch (CryptoException | OTPDatabaseException e) { } catch (CryptoException | OTPDatabaseException e) {
throw new RuntimeException(e); // TODO DialogUtil.showErrorDialog(requireContext(), "Failed to enable encryption", e);
} }
}, null); }, () -> view.setChecked(false));
}else { }else {
try { if(!SettingsUtil.isDatabaseEncrypted(requireContext())) return;
OTPDatabase.decrypt(requireContext());
SettingsUtil.disableEncryption(requireContext());
Log.d("Crypto", "DB encryption disabled");
binding.settingsBiometricLock.setChecked(false); DialogUtil.showYesNo(requireContext(), R.string.disable_encryption_title, R.string.disable_encryption_message, () -> {
binding.settingsBiometricLock.setEnabled(false); try {
} catch (OTPDatabaseException | CryptoException e) { OTPDatabase.decrypt(requireContext());
throw new RuntimeException(e); // TODO SettingsUtil.disableEncryption(requireContext());
} Log.d("Crypto", "DB encryption disabled");
binding.settingsBiometricLock.setChecked(false);
binding.settingsBiometricLock.setEnabled(false);
} catch (OTPDatabaseException | CryptoException e) {
DialogUtil.showErrorDialog(requireContext(), "Failed to disable encryption", e);
}
}, () -> view.setChecked(true));
} }
}); });
if(SettingsUtil.isDatabaseEncrypted(requireContext()) && BiometricUtil.isSupported(requireContext())) { boolean biometricSupported = BiometricUtil.isSupported(requireContext());
if(SettingsUtil.isDatabaseEncrypted(requireContext()) && biometricSupported) {
binding.settingsBiometricLock.setChecked(SettingsUtil.isBiometricEncryption(requireContext())); binding.settingsBiometricLock.setChecked(SettingsUtil.isBiometricEncryption(requireContext()));
binding.settingsBiometricLock.setOnCheckedChangeListener((view, checked) -> { binding.settingsBiometricLock.setOnCheckedChangeListener((view, checked) -> {
if(checked) { if(checked) {
@ -289,7 +295,6 @@ public class SettingsFragment extends NamedFragment {
private void loadBackup(Uri uri, SecretKey key, CryptoParameters parameters) throws BackupException, OTPDatabaseException, CryptoException { private void loadBackup(Uri uri, SecretKey key, CryptoParameters parameters) throws BackupException, OTPDatabaseException, CryptoException {
BackupData data = BackupUtil.loadBackup(requireContext(), uri); BackupData data = BackupUtil.loadBackup(requireContext(), uri);
OTPDatabase db = data.loadDatabase(key, parameters); OTPDatabase db = data.loadDatabase(key, parameters);
//DialogUtil.showErrorDialog(requireContext(), "Success: " + db);
// TODO: prompt user that all current data will be deleted // TODO: prompt user that all current data will be deleted
OTPDatabase.promptLoadDatabase(requireActivity(), () -> { OTPDatabase.promptLoadDatabase(requireActivity(), () -> {
OTPDatabase oldDatabase = OTPDatabase.getLoadedDatabase(); OTPDatabase oldDatabase = OTPDatabase.getLoadedDatabase();

View File

@ -1,7 +1,5 @@
package com.cringe_studios.cringe_authenticator.grouplist; package com.cringe_studios.cringe_authenticator.grouplist;
import android.app.Activity;
import android.app.Application;
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
@ -9,19 +7,13 @@ import android.view.LayoutInflater;
import android.view.ViewGroup; import android.view.ViewGroup;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.util.Consumer; import androidx.core.util.Consumer;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;
import com.cringe_studios.cringe_authenticator.BaseActivity;
import com.cringe_studios.cringe_authenticator.MainActivity;
import com.cringe_studios.cringe_authenticator.databinding.MenuItemBinding; import com.cringe_studios.cringe_authenticator.databinding.MenuItemBinding;
import com.cringe_studios.cringe_authenticator.otplist.OTPListItem;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil; import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import org.bouncycastle.jcajce.provider.symmetric.ARC4;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Collections; import java.util.Collections;
import java.util.List; import java.util.List;

View File

@ -1,10 +1,8 @@
package com.cringe_studios.cringe_authenticator.grouplist; package com.cringe_studios.cringe_authenticator.grouplist;
import android.content.res.ColorStateList;
import android.content.res.TypedArray; import android.content.res.TypedArray;
import android.graphics.Color; import android.graphics.Color;
import android.graphics.drawable.ColorDrawable; import android.graphics.drawable.ColorDrawable;
import android.os.Build;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;

View File

@ -6,7 +6,6 @@ import com.cringe_studios.cringe_authenticator_library.OTPException;
import com.cringe_studios.cringe_authenticator_library.OTPType; import com.cringe_studios.cringe_authenticator_library.OTPType;
import java.io.Serializable; import java.io.Serializable;
import java.util.Objects;
public class OTPData implements Serializable { public class OTPData implements Serializable {
@ -102,17 +101,4 @@ public class OTPData implements Serializable {
} }
} }
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (o == null || getClass() != o.getClass()) return false;
OTPData otpData = (OTPData) o;
return digits == otpData.digits && period == otpData.period && counter == otpData.counter && checksum == otpData.checksum && Objects.equals(name, otpData.name) && Objects.equals(issuer, otpData.issuer) && type == otpData.type && Objects.equals(secret, otpData.secret) && algorithm == otpData.algorithm;
}
@Override
public int hashCode() {
return Objects.hash(name, issuer, type, secret, algorithm, digits, period, counter, checksum);
}
} }

View File

@ -3,14 +3,12 @@ package com.cringe_studios.cringe_authenticator.otplist;
import android.content.Context; import android.content.Context;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.core.util.Consumer;
import androidx.recyclerview.widget.ItemTouchHelper; import androidx.recyclerview.widget.ItemTouchHelper;
import androidx.recyclerview.widget.RecyclerView; import androidx.recyclerview.widget.RecyclerView;

View File

@ -2,7 +2,6 @@ package com.cringe_studios.cringe_authenticator.scanner;
import com.cringe_studios.cringe_authenticator.model.OTPData; import com.cringe_studios.cringe_authenticator.model.OTPData;
import com.cringe_studios.cringe_authenticator.model.OTPMigrationPart; import com.cringe_studios.cringe_authenticator.model.OTPMigrationPart;
import com.cringe_studios.cringe_authenticator.proto.OTPMigration;
public class DetectedCode { public class DetectedCode {

View File

@ -3,7 +3,6 @@ package com.cringe_studios.cringe_authenticator.scanner;
import android.net.Uri; import android.net.Uri;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.core.util.Consumer; import androidx.core.util.Consumer;
import com.cringe_studios.cringe_authenticator.model.OTPMigrationPart; import com.cringe_studios.cringe_authenticator.model.OTPMigrationPart;

View File

@ -8,13 +8,11 @@ import android.media.Image;
import android.os.Build; import android.os.Build;
import android.os.Bundle; import android.os.Bundle;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.WindowManager;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.OptIn; import androidx.annotation.OptIn;
import androidx.appcompat.app.AppCompatActivity;
import androidx.camera.core.Camera; import androidx.camera.core.Camera;
import androidx.camera.core.CameraSelector; import androidx.camera.core.CameraSelector;
import androidx.camera.core.ExperimentalGetImage; import androidx.camera.core.ExperimentalGetImage;
@ -32,9 +30,7 @@ import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.databinding.ActivityQrScannerBinding; import com.cringe_studios.cringe_authenticator.databinding.ActivityQrScannerBinding;
import com.cringe_studios.cringe_authenticator.model.OTPData; import com.cringe_studios.cringe_authenticator.model.OTPData;
import com.cringe_studios.cringe_authenticator.model.OTPMigrationPart; import com.cringe_studios.cringe_authenticator.model.OTPMigrationPart;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil; import com.cringe_studios.cringe_authenticator.util.DialogUtil;
import com.cringe_studios.cringe_authenticator.util.StyledDialogBuilder;
import com.cringe_studios.cringe_authenticator.util.ThemeUtil;
import com.google.common.util.concurrent.ListenableFuture; import com.google.common.util.concurrent.ListenableFuture;
import com.google.mlkit.vision.common.InputImage; import com.google.mlkit.vision.common.InputImage;
@ -157,22 +153,16 @@ public class QRScannerActivity extends BaseActivity {
} }
if(part.getBatchIndex() == 0) { if(part.getBatchIndex() == 0) {
new StyledDialogBuilder(this) DialogUtil.showYesNo(this, R.string.qr_scanner_migration_title, R.string.qr_scanner_migration_message, () -> {
.setTitle(R.string.qr_scanner_migration_title) if(part.getBatchSize() == 1) {
.setMessage(R.string.qr_scanner_migration_message) success(part.getOTPs());
.setPositiveButton(R.string.yes, (d, which) -> { }else {
if(part.getBatchSize() == 1) { currentCodes.addAll(Arrays.asList(part.getOTPs()));
success(part.getOTPs()); lastPart = part;
}else { Toast.makeText(this, getString(R.string.qr_scanner_migration_part, part.getBatchIndex()+ 1, part.getBatchSize()), Toast.LENGTH_LONG).show();
currentCodes.addAll(Arrays.asList(part.getOTPs())); process = true;
lastPart = part; }
Toast.makeText(this, getString(R.string.qr_scanner_migration_part, part.getBatchIndex()+ 1, part.getBatchSize()), Toast.LENGTH_LONG).show(); }, null);
process = true;
}
})
.setNegativeButton(R.string.no, (d, which) -> cancel())
.show()
.setCanceledOnTouchOutside(false);
}else { }else {
currentCodes.addAll(Arrays.asList(part.getOTPs())); currentCodes.addAll(Arrays.asList(part.getOTPs()));
Toast.makeText(this, getString(R.string.qr_scanner_migration_part, part.getBatchIndex()+ 1, part.getBatchSize()), Toast.LENGTH_LONG).show(); Toast.makeText(this, getString(R.string.qr_scanner_migration_part, part.getBatchIndex()+ 1, part.getBatchSize()), Toast.LENGTH_LONG).show();

View File

@ -1,26 +1,13 @@
package com.cringe_studios.cringe_authenticator.unlock; package com.cringe_studios.cringe_authenticator.unlock;
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
import android.content.Intent; import android.content.Intent;
import android.os.Bundle; import android.os.Bundle;
import android.security.KeyStoreParameter;
import android.security.keystore.KeyGenParameterSpec;
import android.security.keystore.KeyProtection;
import android.view.View; import android.view.View;
import android.view.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.biometric.BiometricManager;
import androidx.biometric.BiometricPrompt;
import androidx.core.content.ContextCompat;
import com.cringe_studios.cringe_authenticator.BaseActivity; import com.cringe_studios.cringe_authenticator.BaseActivity;
import com.cringe_studios.cringe_authenticator.MainActivity; import com.cringe_studios.cringe_authenticator.MainActivity;
import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.crypto.BiometricKey; import com.cringe_studios.cringe_authenticator.crypto.BiometricKey;
import com.cringe_studios.cringe_authenticator.crypto.Crypto; import com.cringe_studios.cringe_authenticator.crypto.Crypto;
import com.cringe_studios.cringe_authenticator.crypto.CryptoException; import com.cringe_studios.cringe_authenticator.crypto.CryptoException;
@ -32,21 +19,11 @@ import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil; import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import com.cringe_studios.cringe_authenticator.util.ThemeUtil; import com.cringe_studios.cringe_authenticator.util.ThemeUtil;
import java.io.IOException;
import java.security.KeyStore;
import java.security.KeyStoreException;
import java.security.NoSuchAlgorithmException;
import java.security.cert.CertificateException;
import java.util.concurrent.Executor;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey; import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec; import javax.crypto.spec.SecretKeySpec;
public class UnlockActivity extends BaseActivity { public class UnlockActivity extends BaseActivity {
private static final long LOCK_TIMEOUT = 10000;
private ActivityUnlockBinding binding; private ActivityUnlockBinding binding;
@Override @Override

View File

@ -8,9 +8,6 @@ import androidx.activity.result.contract.ActivityResultContract;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import com.cringe_studios.cringe_authenticator.scanner.QRScannerActivity;
import com.cringe_studios.cringe_authenticator.scanner.ScannerResult;
public class UnlockContract extends ActivityResultContract<Void, Boolean> { public class UnlockContract extends ActivityResultContract<Void, Boolean> {
@NonNull @NonNull
@Override @Override

View File

@ -3,26 +3,21 @@ package com.cringe_studios.cringe_authenticator.urihandler;
import android.content.Intent; import android.content.Intent;
import android.net.Uri; import android.net.Uri;
import android.os.Bundle; import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast; import android.widget.Toast;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.appcompat.app.AppCompatActivity;
import com.cringe_studios.cringe_authenticator.BaseActivity; import com.cringe_studios.cringe_authenticator.BaseActivity;
import com.cringe_studios.cringe_authenticator.R; import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.crypto.CryptoException; import com.cringe_studios.cringe_authenticator.crypto.CryptoException;
import com.cringe_studios.cringe_authenticator.model.OTPData; import com.cringe_studios.cringe_authenticator.model.OTPData;
import com.cringe_studios.cringe_authenticator.unlock.UnlockContract;
import com.cringe_studios.cringe_authenticator.util.DialogUtil; import com.cringe_studios.cringe_authenticator.util.DialogUtil;
import com.cringe_studios.cringe_authenticator.util.OTPDatabase; import com.cringe_studios.cringe_authenticator.util.OTPDatabase;
import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException; import com.cringe_studios.cringe_authenticator.util.OTPDatabaseException;
import com.cringe_studios.cringe_authenticator.util.OTPParser; import com.cringe_studios.cringe_authenticator.util.OTPParser;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil; import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import com.cringe_studios.cringe_authenticator.util.StyledDialogBuilder; import com.cringe_studios.cringe_authenticator.util.StyledDialogBuilder;
import com.cringe_studios.cringe_authenticator.util.ThemeUtil;
public class URIHandlerActivity extends BaseActivity { public class URIHandlerActivity extends BaseActivity {

View File

@ -1,14 +1,13 @@
package com.cringe_studios.cringe_authenticator.util; package com.cringe_studios.cringe_authenticator.util;
import android.content.Context; import android.content.Context;
import android.text.InputType;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.View; import android.view.View;
import android.widget.ArrayAdapter; import android.widget.ArrayAdapter;
import android.widget.Button; import android.widget.Button;
import android.widget.EditText;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.StringRes;
import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AlertDialog;
import androidx.core.util.Consumer; import androidx.core.util.Consumer;
@ -16,6 +15,7 @@ import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.databinding.DialogCreateGroupBinding; import com.cringe_studios.cringe_authenticator.databinding.DialogCreateGroupBinding;
import com.cringe_studios.cringe_authenticator.databinding.DialogInputCodeHotpBinding; import com.cringe_studios.cringe_authenticator.databinding.DialogInputCodeHotpBinding;
import com.cringe_studios.cringe_authenticator.databinding.DialogInputCodeTotpBinding; import com.cringe_studios.cringe_authenticator.databinding.DialogInputCodeTotpBinding;
import com.cringe_studios.cringe_authenticator.databinding.DialogInputPasswordBinding;
import com.cringe_studios.cringe_authenticator.databinding.DialogSetPasswordBinding; import com.cringe_studios.cringe_authenticator.databinding.DialogSetPasswordBinding;
import com.cringe_studios.cringe_authenticator.model.OTPData; import com.cringe_studios.cringe_authenticator.model.OTPData;
import com.cringe_studios.cringe_authenticator_library.OTPAlgorithm; import com.cringe_studios.cringe_authenticator_library.OTPAlgorithm;
@ -306,11 +306,11 @@ public class DialogUtil {
} }
public static void showInputPasswordDialog(Context context, Consumer<String> callback, Runnable onCancel) { public static void showInputPasswordDialog(Context context, Consumer<String> callback, Runnable onCancel) {
EditText passwordField = new EditText(context); DialogInputPasswordBinding binding = DialogInputPasswordBinding.inflate(LayoutInflater.from(context));
passwordField.setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
AlertDialog dialog = new StyledDialogBuilder(context) AlertDialog dialog = new StyledDialogBuilder(context)
.setTitle("Input Password") .setTitle("Input Password")
.setView(passwordField) // TODO: better layout .setView(binding.inputPassword)
.setPositiveButton("Ok", (d, which) -> {}) .setPositiveButton("Ok", (d, which) -> {})
.setNegativeButton(R.string.cancel, (d, which) -> { if(onCancel != null) onCancel.run(); }) .setNegativeButton(R.string.cancel, (d, which) -> { if(onCancel != null) onCancel.run(); })
.setOnCancelListener(d -> { if(onCancel != null) onCancel.run(); }) .setOnCancelListener(d -> { if(onCancel != null) onCancel.run(); })
@ -319,17 +319,56 @@ public class DialogUtil {
dialog.setOnShowListener(d -> { dialog.setOnShowListener(d -> {
Button okButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE); Button okButton = dialog.getButton(AlertDialog.BUTTON_POSITIVE);
okButton.setOnClickListener(v -> { okButton.setOnClickListener(v -> {
if(passwordField.getText().length() == 0) { if(binding.inputPassword.getText().length() == 0) {
DialogUtil.showErrorDialog(context, "You need to enter a password"); DialogUtil.showErrorDialog(context, "You need to enter a password");
return; return;
} }
dialog.dismiss(); dialog.dismiss();
callback.accept(passwordField.getText().toString()); callback.accept(binding.inputPassword.getText().toString());
}); });
}); });
dialog.show(); dialog.show();
} }
public static void showYesNo(Context context, @StringRes int title, @StringRes int message, @StringRes int yesText, @StringRes int noText, Runnable yes, Runnable no) {
new StyledDialogBuilder(context)
.setTitle(title)
.setMessage(message)
.setPositiveButton(yesText, (d, w) -> {
if(yes != null) yes.run();
})
.setNegativeButton(noText, (d, w) -> {
if(no != null) no.run();
})
.show()
.setCanceledOnTouchOutside(false);
}
public static void showYesNo(Context context, @StringRes int title, @StringRes int message, Runnable yes, Runnable no) {
showYesNo(context, title, message, R.string.yes, R.string.no, yes, no);
}
public static void showYesNoCancel(Context context, @StringRes int title, @StringRes int message, @StringRes int yesText, @StringRes int noText, @StringRes int cancelText, Runnable yes, Runnable no, Runnable cancel) {
new StyledDialogBuilder(context)
.setTitle(title)
.setMessage(message)
.setPositiveButton(yesText, (d, w) -> {
if(yes != null) yes.run();
})
.setNegativeButton(noText, (d, w) -> {
if(no != null) no.run();
})
.setNeutralButton(cancelText, (d, w) -> d.cancel())
.setOnCancelListener(d -> {
if(cancel != null) cancel.run();
})
.show();
}
public static void showYesNoCancel(Context context, @StringRes int title, @StringRes int message, Runnable yes, Runnable no, Runnable cancel) {
showYesNoCancel(context, title, message, R.string.yes, R.string.no, R.string.cancel, yes, no, cancel);
}
} }

View File

@ -6,7 +6,6 @@ import androidx.appcompat.app.ActionBar;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.Fragment; import androidx.fragment.app.Fragment;
import androidx.fragment.app.FragmentManager; import androidx.fragment.app.FragmentManager;
import androidx.fragment.app.FragmentTransaction;
import com.cringe_studios.cringe_authenticator.R; import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.fragment.MenuDrawerFragment; import com.cringe_studios.cringe_authenticator.fragment.MenuDrawerFragment;

View File

@ -2,25 +2,18 @@ package com.cringe_studios.cringe_authenticator.util;
import android.app.Activity; import android.app.Activity;
import android.content.Context; import android.content.Context;
import android.util.Log;
import androidx.appcompat.app.AppCompatActivity;
import androidx.core.util.Consumer;
import com.cringe_studios.cringe_authenticator.BaseActivity; import com.cringe_studios.cringe_authenticator.BaseActivity;
import com.cringe_studios.cringe_authenticator.crypto.Crypto; import com.cringe_studios.cringe_authenticator.crypto.Crypto;
import com.cringe_studios.cringe_authenticator.crypto.CryptoException; import com.cringe_studios.cringe_authenticator.crypto.CryptoException;
import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters; import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters;
import com.cringe_studios.cringe_authenticator.model.OTPData; import com.cringe_studios.cringe_authenticator.model.OTPData;
import com.cringe_studios.cringe_authenticator.unlock.UnlockContract;
import com.google.gson.JsonObject; import com.google.gson.JsonObject;
import com.google.gson.JsonSyntaxException; import com.google.gson.JsonSyntaxException;
import java.io.File; import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream; import java.io.FileOutputStream;
import java.io.IOException; import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.charset.StandardCharsets; import java.nio.charset.StandardCharsets;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;

View File

@ -2,24 +2,16 @@ package com.cringe_studios.cringe_authenticator.util;
import android.content.Context; import android.content.Context;
import android.content.SharedPreferences; import android.content.SharedPreferences;
import android.util.Base64;
import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.backup.BackupGroup; import com.cringe_studios.cringe_authenticator.backup.BackupGroup;
import com.cringe_studios.cringe_authenticator.crypto.BiometricKey; import com.cringe_studios.cringe_authenticator.crypto.BiometricKey;
import com.cringe_studios.cringe_authenticator.crypto.CryptoException;
import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters; import com.cringe_studios.cringe_authenticator.crypto.CryptoParameters;
import com.google.gson.Gson; import com.google.gson.Gson;
import org.bouncycastle.jcajce.provider.symmetric.ARC4;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections;
import java.util.LinkedHashMap;
import java.util.List; import java.util.List;
import java.util.Locale; import java.util.Locale;
import java.util.Map;
public class SettingsUtil { public class SettingsUtil {

View File

@ -1,8 +1,6 @@
package com.cringe_studios.cringe_authenticator.util; package com.cringe_studios.cringe_authenticator.util;
import android.annotation.SuppressLint;
import android.content.res.Configuration; import android.content.res.Configuration;
import android.util.Log;
import android.view.View; import android.view.View;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;

View File

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="utf-8"?>
<androidx.constraintlayout.widget.ConstraintLayout xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:padding="16dp">
<TextView
android:id="@+id/input_password_text"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:text="@string/enter_password"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toTopOf="parent" />
<EditText
android:id="@+id/input_password"
android:layout_width="0dp"
android:layout_height="wrap_content"
android:ems="10"
android:hint="@string/password"
android:inputType="textPassword"
app:layout_constraintEnd_toEndOf="parent"
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@+id/input_password_text" />
</androidx.constraintlayout.widget.ConstraintLayout>

View File

@ -93,4 +93,7 @@
<string name="backups">Backups</string> <string name="backups">Backups</string>
<string name="create_backup">Backup erstellen</string> <string name="create_backup">Backup erstellen</string>
<string name="load_backup">Backup laden</string> <string name="load_backup">Backup laden</string>
<string name="enter_password">Passwort eingeben</string>
<string name="disable_encryption_title">Verschlüsselung deaktivieren</string>
<string name="disable_encryption_message">Willst du wirklich die Verschlüsselung deaktivieren?</string>
</resources> </resources>

View File

@ -94,4 +94,7 @@
<string name="backups">Backups</string> <string name="backups">Backups</string>
<string name="create_backup">Create backup</string> <string name="create_backup">Create backup</string>
<string name="load_backup">Load backup</string> <string name="load_backup">Load backup</string>
<string name="enter_password">Enter Password</string>
<string name="disable_encryption_title">Disable encryption</string>
<string name="disable_encryption_message">Do you really want to disable encryption?</string>
</resources> </resources>