Implement more settings, Improve theme handling

This commit is contained in:
MrLetsplay 2023-09-22 15:27:21 +02:00
parent 9d822b2725
commit 1389afa353
Signed by: mr
SSH Key Fingerprint: SHA256:92jBH80vpXyaZHjaIl47pjRq+Yt7XGTArqQg1V7hSqg
15 changed files with 146 additions and 71 deletions

View File

@ -10,8 +10,8 @@
<option name="gradleJvm" value="jbr-17" />
<option name="modules">
<set>
<option value="/mnt/sshd/Files/Desktop/testing/android/Cringe-Authenticator" />
<option value="/mnt/sshd/Files/Desktop/testing/android/Cringe-Authenticator/app" />
<option value="$PROJECT_DIR$" />
<option value="$PROJECT_DIR$/app" />
</set>
</option>
</GradleProjectSettings>

View File

@ -2,12 +2,15 @@ package com.cringe_studios.cringe_authenticator;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import androidx.activity.result.ActivityResultLauncher;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import com.cringe_studios.cringe_authenticator.unlock.UnlockContract;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import com.cringe_studios.cringe_authenticator.util.ThemeUtil;
public class BaseActivity extends AppCompatActivity {
@ -19,6 +22,12 @@ public class BaseActivity extends AppCompatActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
registerCallbacks();
if(SettingsUtil.isScreenSecurity(this)) {
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
}
ThemeUtil.loadTheme(this);
}
private void registerCallbacks() {

View File

@ -13,9 +13,9 @@ import com.cringe_studios.cringe_authenticator.databinding.ActivityIntroBinding;
import com.cringe_studios.cringe_authenticator.unlock.UnlockActivity;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
public class IntroActivity extends AppCompatActivity {
public class IntroActivity extends BaseActivity {
private static ActivityIntroBinding binding;
private ActivityIntroBinding binding;
private MediaPlayer mMediaPlayer;

View File

@ -7,6 +7,7 @@ import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.WindowManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Toast;
@ -61,10 +62,6 @@ public class MainActivity extends BaseActivity {
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
//getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE); TODO: enable secure flag
ThemeUtil.loadTheme(this);
setLocale(SettingsUtil.getLocale(this));
qrScanner = new QRScanner();

View File

@ -3,6 +3,7 @@ package com.cringe_studios.cringe_authenticator.fragment;
import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG;
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
import android.location.SettingInjectorService;
import android.os.Build;
import android.os.Bundle;
import android.util.Log;
@ -29,6 +30,7 @@ import com.cringe_studios.cringe_authenticator.util.FabUtil;
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.Theme;
import java.util.Arrays;
import java.util.Locale;
@ -57,7 +59,7 @@ public class SettingsFragment extends NamedFragment {
localeNames[i] = locales[i].getDisplayName(locales[i]);
}
binding.settingsLanguage.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, localeNames));
binding.settingsLanguage.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, localeNames));
binding.settingsLanguage.setSelection(Arrays.asList(locales).indexOf(SettingsUtil.getLocale(requireContext())));
binding.settingsLanguage.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
@ -79,7 +81,7 @@ public class SettingsFragment extends NamedFragment {
binding.settingsEnableEncryption.setChecked(SettingsUtil.isDatabaseEncrypted(requireContext()));
binding.settingsEnableEncryption.setOnCheckedChangeListener((view, checked) -> {
if(checked) {
DialogUtil.showInputPasswordDialog(requireContext(), password -> {
DialogUtil.showInputPasswordDialog(requireContext(), password -> {
CryptoParameters params = CryptoParameters.createNew();
Log.d("Crypto", "Created new crypto params");
@ -89,6 +91,10 @@ public class SettingsFragment extends NamedFragment {
OTPDatabase.encrypt(requireContext(), key, params);
SettingsUtil.enableEncryption(requireContext(), params);
Log.d("Crypto", "DB encryption enabled");
if(BiometricUtil.isSupported(requireContext())) {
binding.settingsBiometricLock.setEnabled(true);
}
} catch (CryptoException | OTPDatabaseException e) {
throw new RuntimeException(e); // TODO
}
@ -98,18 +104,16 @@ public class SettingsFragment extends NamedFragment {
OTPDatabase.decrypt(requireContext());
SettingsUtil.disableEncryption(requireContext());
Log.d("Crypto", "DB encryption disabled");
binding.settingsBiometricLock.setChecked(false);
binding.settingsBiometricLock.setEnabled(false);
} catch (OTPDatabaseException | CryptoException e) {
throw new RuntimeException(e); // TODO
}
}
});
binding.settingsEnableIntroVideo.setChecked(SettingsUtil.isIntroVideoEnabled(requireContext()));
binding.settingsEnableIntroVideo.setOnCheckedChangeListener((view, checked) -> SettingsUtil.setEnableIntroVideo(requireContext(), checked));
if(SettingsUtil.isDatabaseEncrypted(requireContext())
&& Build.VERSION.SDK_INT >= Build.VERSION_CODES.M
&& BiometricManager.from(requireContext()).canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS) {
if(SettingsUtil.isDatabaseEncrypted(requireContext()) && BiometricUtil.isSupported(requireContext())) {
binding.settingsBiometricLock.setChecked(SettingsUtil.isBiometricEncryption(requireContext()));
binding.settingsBiometricLock.setOnCheckedChangeListener((view, checked) -> {
if(checked) {
@ -140,20 +144,32 @@ public class SettingsFragment extends NamedFragment {
// TODO: inform user
}
binding.settingsTheme.setAdapter(new ArrayAdapter<>(getContext(), android.R.layout.simple_list_item_1, SettingsUtil.THEME_NAMES.toArray(new String[0])));
binding.settingsTheme.setSelection(SettingsUtil.THEME_NAMES.indexOf(SettingsUtil.getTheme(requireContext())));
binding.settingsEnableIntroVideo.setChecked(SettingsUtil.isIntroVideoEnabled(requireContext()));
binding.settingsEnableIntroVideo.setOnCheckedChangeListener((view, checked) -> SettingsUtil.setEnableIntroVideo(requireContext(), checked));
binding.settingsScreenSecurity.setChecked(SettingsUtil.isScreenSecurity(requireContext()));
binding.settingsScreenSecurity.setOnCheckedChangeListener((view, checked) -> {
SettingsUtil.setScreenSecurity(requireContext(), checked);
requireActivity().recreate();
});
binding.settingsHideCodes.setChecked(SettingsUtil.isHideCodes(requireContext())); // TODO: implement functionality
binding.settingsHideCodes.setOnCheckedChangeListener((view, checked) -> SettingsUtil.setHideCodes(requireContext(), checked));
String[] themeNames = new String[Theme.values().length];
for(int i = 0; i < Theme.values().length; i++) {
themeNames[i] = getResources().getString(Theme.values()[i].getName());
}
binding.settingsTheme.setAdapter(new ArrayAdapter<>(requireContext(), android.R.layout.simple_list_item_1, themeNames));
binding.settingsTheme.setSelection(SettingsUtil.getTheme(requireContext()).ordinal());
binding.settingsTheme.setOnItemSelectedListener(new AdapterView.OnItemSelectedListener() {
@Override
public void onItemSelected(AdapterView<?> parent, View view, int position, long id) {
String theme = SettingsUtil.THEME_NAMES.get(position);
if(theme.equals(SettingsUtil.getTheme(requireContext()))) return;
Theme theme = Theme.values()[position];
if(theme == SettingsUtil.getTheme(requireContext())) return;
SettingsUtil.setTheme(requireContext(), theme);
Integer themeID = SettingsUtil.THEMES.get(theme);
if(themeID == null) return;
requireActivity().setTheme(themeID);
requireActivity().recreate();
}

View File

@ -31,6 +31,7 @@ import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.databinding.ActivityQrScannerBinding;
import com.cringe_studios.cringe_authenticator.model.OTPData;
import com.cringe_studios.cringe_authenticator.model.OTPMigrationPart;
import com.cringe_studios.cringe_authenticator.util.SettingsUtil;
import com.cringe_studios.cringe_authenticator.util.StyledDialogBuilder;
import com.cringe_studios.cringe_authenticator.util.ThemeUtil;
import com.google.common.util.concurrent.ListenableFuture;
@ -61,10 +62,6 @@ public class QRScannerActivity extends AppCompatActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
getWindow().setFlags(WindowManager.LayoutParams.FLAG_SECURE, WindowManager.LayoutParams.FLAG_SECURE);
ThemeUtil.loadTheme(this);
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && checkSelfPermission(Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {
requestPermissions(new String[] {Manifest.permission.CAMERA}, 1234);
}

View File

@ -8,6 +8,8 @@ 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.WindowManager;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
@ -16,6 +18,7 @@ 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.MainActivity;
import com.cringe_studios.cringe_authenticator.R;
import com.cringe_studios.cringe_authenticator.crypto.BiometricKey;
@ -40,7 +43,7 @@ import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
public class UnlockActivity extends AppCompatActivity {
public class UnlockActivity extends BaseActivity {
private static final long LOCK_TIMEOUT = 10000;
@ -50,8 +53,6 @@ public class UnlockActivity extends AppCompatActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeUtil.loadTheme(this);
if(!SettingsUtil.isDatabaseEncrypted(this)) {
success();
return;
@ -76,6 +77,8 @@ public class UnlockActivity extends AppCompatActivity {
binding.unlockBiometrics.setOnClickListener(view -> BiometricUtil.promptBiometricAuth(this, onSuccess, () -> {}));
BiometricUtil.promptBiometricAuth(this, onSuccess, () -> {});
}else {
binding.unlockBiometrics.setVisibility(View.GONE);
}
binding.unlockButton.setOnClickListener(view -> {

View File

@ -4,6 +4,7 @@ import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.util.Log;
import android.view.WindowManager;
import android.widget.Toast;
import androidx.annotation.Nullable;
@ -29,8 +30,6 @@ public class URIHandlerActivity extends BaseActivity {
protected void onCreate(@Nullable Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
ThemeUtil.loadTheme(this);
Intent intent = getIntent();
if(intent == null) {
finishAndRemoveTask();

View File

@ -4,6 +4,7 @@ import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRON
import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL;
import android.content.Context;
import android.os.Build;
import androidx.annotation.NonNull;
import androidx.biometric.BiometricManager;
@ -18,7 +19,7 @@ import java.util.concurrent.Executor;
public class BiometricUtil {
public static boolean isSupported(Context context) {
return BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS;
return Build.VERSION.SDK_INT >= Build.VERSION_CODES.M && BiometricManager.from(context).canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL) == BiometricManager.BIOMETRIC_SUCCESS;
}
public static void promptBiometricAuth(FragmentActivity context, Runnable success, Runnable failure) {

View File

@ -6,6 +6,7 @@ import android.util.Base64;
import com.cringe_studios.cringe_authenticator.R;
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.google.gson.Gson;
@ -21,21 +22,6 @@ import java.util.Map;
public class SettingsUtil {
public static final Map<String, Integer> THEMES;
public static final List<String> THEME_NAMES;
static {
Map<String, Integer> themes = new LinkedHashMap<>();
themes.put("Blue/Green", R.style.Theme_CringeAuthenticator_Blue_Green);
themes.put("Red/Blue", R.style.Theme_CringeAuthenticator_Red_Blue);
themes.put("Pink/Green", R.style.Theme_CringeAuthenticator_Pink_Green);
themes.put("Blue/Yellow", R.style.Theme_CringeAuthenticator_Blue_Yellow);
themes.put("Green/Yellow", R.style.Theme_CringeAuthenticator_Green_Yellow);
themes.put("Orange/Turquoise", R.style.Theme_CringeAuthenticator_Orange_Turquoise);
THEMES = Collections.unmodifiableMap(themes);
THEME_NAMES = Collections.unmodifiableList(new ArrayList<>(THEMES.keySet()));
}
public static String
GROUPS_PREFS_NAME = "groups",
GENERAL_PREFS_NAME = "general";
@ -130,8 +116,6 @@ public class SettingsUtil {
ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).edit()
.putBoolean("encryption", false)
.remove("encryption.parameters")
.remove("encryption.biometric")
.remove("encryption.biometric.key")
.apply();
}
@ -172,13 +156,36 @@ public class SettingsUtil {
return ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getBoolean("enableIntroVideo", true);
}
public static void setTheme(Context ctx, String theme) {
public static void setScreenSecurity(Context ctx, boolean screenSecurity) {
SharedPreferences prefs = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE);
prefs.edit().putString("theme", theme).apply();
prefs.edit().putBoolean("screenSecurity", screenSecurity).apply();
}
public static String getTheme(Context ctx) {
return ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getString("theme", THEME_NAMES.get(0));
public static boolean isScreenSecurity(Context ctx) {
return ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getBoolean("screenSecurity", true);
}
public static void setHideCodes(Context ctx, boolean hideCodes) {
SharedPreferences prefs = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE);
prefs.edit().putBoolean("hideCodes", hideCodes).apply();
}
public static boolean isHideCodes(Context ctx) {
return ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getBoolean("hideCodes", false);
}
public static void setTheme(Context ctx, Theme theme) {
SharedPreferences prefs = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE);
prefs.edit().putString("theme", theme.name()).apply();
}
public static Theme getTheme(Context ctx) {
String themeId = ctx.getSharedPreferences(GENERAL_PREFS_NAME, Context.MODE_PRIVATE).getString("theme", Theme.BLUE_GREEN.name());
try {
return Theme.valueOf(themeId);
}catch(IllegalArgumentException e) {
return Theme.BLUE_GREEN;
}
}
public static void setLocale(Context ctx, Locale locale) {

View File

@ -0,0 +1,39 @@
package com.cringe_studios.cringe_authenticator.util;
import androidx.annotation.StringRes;
import androidx.annotation.StyleRes;
import com.cringe_studios.cringe_authenticator.R;
public enum Theme {
BLUE_GREEN(R.string.theme_blue_green, R.style.Theme_CringeAuthenticator_Blue_Green),
RED_BLUE(R.string.theme_red_blue, R.style.Theme_CringeAuthenticator_Red_Blue),
PINK_GREEN(R.string.theme_pink_green, R.style.Theme_CringeAuthenticator_Pink_Green),
BLUE_YELLOW(R.string.theme_blue_yellow, R.style.Theme_CringeAuthenticator_Blue_Yellow),
GREEN_YELLOW(R.string.theme_green_yellow, R.style.Theme_CringeAuthenticator_Green_Yellow),
ORANGE_TURQUOISE(R.string.theme_orange_turquoise, R.style.Theme_CringeAuthenticator_Orange_Turquoise),
;
@StringRes
private final int name;
@StyleRes
private final int style;
Theme(@StringRes int name, @StyleRes int style) {
this.name = name;
this.style = style;
}
@StringRes
public int getName() {
return name;
}
@StyleRes
public int getStyle() {
return style;
}
}

View File

@ -8,12 +8,8 @@ import com.cringe_studios.cringe_authenticator.R;
public class ThemeUtil {
public static void loadTheme(AppCompatActivity activity) {
Integer themeID = SettingsUtil.THEMES.get(SettingsUtil.getTheme(activity));
if(themeID != null) {
activity.setTheme(themeID);
}else {
activity.setTheme(R.style.Theme_CringeAuthenticator_Blue_Green);
}
Theme theme = SettingsUtil.getTheme(activity);
activity.setTheme(theme.getStyle());
// TODO: use settings
//AppCompatDelegate.setDefaultNightMode(AppCompatDelegate.MODE_NIGHT_NO);

View File

@ -29,12 +29,6 @@
android:layout_height="wrap_content"
android:text="@string/enable_encryption" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/settings_enable_intro_video"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_enable_intro_video" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/settings_biometric_lock"
android:layout_width="match_parent"
@ -42,13 +36,19 @@
android:text="@string/settings_biometric_lock" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/screen_security"
android:id="@+id/settings_enable_intro_video"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/settings_enable_intro_video" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/settings_screen_security"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/screen_security" />
<androidx.appcompat.widget.SwitchCompat
android:id="@+id/hide_codes"
android:id="@+id/settings_hide_codes"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/hide_codes" />
@ -66,7 +66,6 @@
android:paddingBottom="10dp" />
<TextView
android:id="@+id/textView"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:text="@string/appearance" />

View File

@ -74,4 +74,10 @@
<string name="fragment_home">Zuhause</string>
<string name="fragment_settings">Einstellungen</string>
<string name="fragment_about">Über</string>
<string name="theme_blue_green">Blau/Grün</string>
<string name="theme_red_blue">Rot/Blau</string>
<string name="theme_pink_green">Pink/Grün</string>
<string name="theme_blue_yellow">Blau/Gelb</string>
<string name="theme_green_yellow">Grün/Gelb</string>
<string name="theme_orange_turquoise">Orange/Türkis</string>
</resources>

View File

@ -76,4 +76,10 @@
<item>Rename</item>
<item>Delete</item>
</string-array>
<string name="theme_blue_green">Blue/Green</string>
<string name="theme_red_blue">Red/Blue</string>
<string name="theme_pink_green">Pink/Green</string>
<string name="theme_blue_yellow">Blue/Yellow</string>
<string name="theme_green_yellow">Green/Yellow</string>
<string name="theme_orange_turquoise">Orange/Turquoise</string>
</resources>