Skip to content

Commit

Permalink
feat: Support biometric authentication
Browse files Browse the repository at this point in the history
  • Loading branch information
deltazefiro committed Apr 28, 2023
1 parent f6e6e96 commit 38b413c
Show file tree
Hide file tree
Showing 8 changed files with 150 additions and 16 deletions.
3 changes: 2 additions & 1 deletion app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -113,8 +113,9 @@ dependencies {

implementation 'com.github.getActivity:XXPermissions:16.8'
implementation 'com.github.getActivity:XToast:8.9'
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'com.google.android.material:material:1.7.0'
implementation "androidx.biometric:biometric:1.1.0"
implementation 'androidx.appcompat:appcompat:1.6.1'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
implementation 'androidx.swiperefreshlayout:swiperefreshlayout:1.1.0'
Expand Down
9 changes: 9 additions & 0 deletions app/src/main/java/deltazero/amarok/PrefMgr.java
Original file line number Diff line number Diff line change
Expand Up @@ -159,4 +159,13 @@ public void setAmarokPassword(String password) {
mPrefEditor.putString("amarokPassword", password);
mPrefEditor.apply();
}

public boolean getEnableAmarokBiometricAuth() {
return mPrefs.getBoolean("enableAmarokBiometricAuth", false);
}

public void setEnableAmarokBiometricAuth(boolean enableAmarokBiometricAuth) {
mPrefEditor.putBoolean("enableAmarokBiometricAuth", enableAmarokBiometricAuth);
mPrefEditor.apply();
}
}
15 changes: 5 additions & 10 deletions app/src/main/java/deltazero/amarok/ui/MainActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@
import deltazero.amarok.R;
import deltazero.amarok.utils.AppCenterUtil;
import deltazero.amarok.utils.PermissionUtil;
import deltazero.amarok.utils.SecurityAuth;

public class MainActivity extends AppCompatActivity {

Expand All @@ -50,16 +51,10 @@ protected void onCreate(Bundle savedInstanceState) {
prefMgr = hider.prefMgr;

// Show security check fragment
if (prefMgr.getAmarokPassword() != null) {
new SecurityFragment()
.setOnVerifiedCallback(succeed -> {
if (succeed) initUi();
else finish();
})
.show(getSupportFragmentManager(), null);
} else {
initUi();
}
new SecurityAuth(this, succeed -> {
if (succeed) initUi();
else finish();
}).authenticate();
}

public void changeStatus(View view) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@
import deltazero.amarok.R;
import deltazero.amarok.utils.HashUtil;

public class SecurityFragment extends BottomSheetDialogFragment {
public class PasswordAuthFragment extends BottomSheetDialogFragment {

private OnVerifiedCallback onVerifiedCallback;
private TextInputEditText etPassword;
private TextInputLayout tilPassword;
private PrefMgr prefMgr;

interface OnVerifiedCallback {
public void onVerified(boolean succeed);
public interface OnVerifiedCallback {
void onVerified(boolean succeed);
}

@Nullable
Expand Down Expand Up @@ -65,7 +65,7 @@ public void afterTextChanged(Editable s) {
return fragmentView;
}

public SecurityFragment setOnVerifiedCallback(OnVerifiedCallback onVerifiedCallback) {
public PasswordAuthFragment setOnVerifiedCallback(OnVerifiedCallback onVerifiedCallback) {
this.onVerifiedCallback = onVerifiedCallback;
return this;
}
Expand Down
11 changes: 10 additions & 1 deletion app/src/main/java/deltazero/amarok/ui/SettingsActivity.java
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public class SettingsActivity extends AppCompatActivity {
private PrefMgr prefMgr;
private Context context;
private String appVersionName;
private MaterialSwitch swAnalytics, swAutoUpdate, swPanicButton, swQuickHideNotification, swAppLock;
private MaterialSwitch swAnalytics, swAutoUpdate, swPanicButton, swQuickHideNotification, swAppLock, swBiometricAuth;
private MaterialToolbar tbToolBar;
private TextView tvCurrAppHider;
private TextView tvCurrFileHider;
Expand Down Expand Up @@ -61,6 +61,7 @@ protected void onCreate(Bundle savedInstanceState) {
tvCurrFileHider = findViewById(R.id.settings_tv_curr_file_hider);
tvCurrVer = findViewById(R.id.settings_tv_curr_ver);
swAppLock = findViewById(R.id.settings_sw_amarok_lock);
swBiometricAuth = findViewById(R.id.settings_sw_biometric_auth);
swQuickHideNotification = findViewById(R.id.settings_sw_quick_hide_notification);
swPanicButton = findViewById(R.id.settings_sw_panic_button);
swAnalytics = findViewById(R.id.settings_sw_analytics);
Expand Down Expand Up @@ -114,9 +115,14 @@ public void onDenied(List<String> permissions, boolean never) {
.show(getSupportFragmentManager(), null);
} else {
prefMgr.setAmarokPassword(null);
updateUI();
}
});

swBiometricAuth.setOnCheckedChangeListener((buttonView, isChecked) -> {
prefMgr.setEnableAmarokBiometricAuth(isChecked);
});

swPanicButton.setOnCheckedChangeListener((buttonView, isChecked) -> {
if (!buttonView.isPressed())
return; // Triggered by setCheck
Expand Down Expand Up @@ -178,9 +184,12 @@ private void updateUI() {
tvCurrVer.setText(getString(R.string.check_update_description, appVersionName));

swAppLock.setChecked(prefMgr.getAmarokPassword() != null);
swBiometricAuth.setChecked(prefMgr.getEnableAmarokBiometricAuth());
swQuickHideNotification.setChecked(prefMgr.getEnableQuickHideService());
swPanicButton.setChecked(prefMgr.getEnablePanicButton());

swPanicButton.setEnabled(prefMgr.getEnableQuickHideService());
swBiometricAuth.setEnabled(prefMgr.getAmarokPassword() != null);

if (AppCenterUtil.isAvailable()) {
swAnalytics.setChecked(AppCenterUtil.isAnalyticsEnabled());
Expand Down
69 changes: 69 additions & 0 deletions app/src/main/java/deltazero/amarok/utils/SecurityAuth.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
package deltazero.amarok.utils;

import androidx.annotation.NonNull;
import androidx.biometric.BiometricPrompt;
import androidx.fragment.app.FragmentActivity;

import deltazero.amarok.PrefMgr;
import deltazero.amarok.R;
import deltazero.amarok.ui.PasswordAuthFragment;

public class SecurityAuth {

public interface SecurityAuthCallback {
void onSecurityAuthCallback(boolean succeed);
}

private SecurityAuthCallback callback;
private FragmentActivity activity;
private PrefMgr prefMgr;

public SecurityAuth(FragmentActivity activity, SecurityAuthCallback callback) {
this.callback = callback;
this.activity = activity;
prefMgr = new PrefMgr(activity);
}

public void authenticate() {
if (prefMgr.getAmarokPassword() != null) {
if (prefMgr.getEnableAmarokBiometricAuth()) biometricAuthenticate();
else passwordAuthenticate();
} else {
callback.onSecurityAuthCallback(true);
}
}

private void passwordAuthenticate() {
assert prefMgr.getAmarokPassword() != null;
new PasswordAuthFragment()
.setOnVerifiedCallback(succeed -> callback.onSecurityAuthCallback(succeed))
.show(activity.getSupportFragmentManager(), null);
}

private void biometricAuthenticate() {

var biometricPrompt = new BiometricPrompt(activity, new BiometricPrompt.AuthenticationCallback() {
@Override
public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) {
passwordAuthenticate();
}

@Override
public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) {
callback.onSecurityAuthCallback(true);
}

@Override
public void onAuthenticationFailed() {
super.onAuthenticationFailed();
}
});

var promptInfo = new BiometricPrompt.PromptInfo.Builder()
.setTitle(activity.getString(R.string.unlock_required))
.setNegativeButtonText(activity.getString(android.R.string.cancel))
.build();

biometricPrompt.authenticate(promptInfo);
}
}
49 changes: 49 additions & 0 deletions app/src/main/res/layout/activity_settings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,55 @@

</RelativeLayout>

<RelativeLayout
android:layout_width="match_parent"
android:layout_height="wrap_content">

<ImageView
android:id="@+id/settings_iv_biometric_auth"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_centerVertical="true"
android:layout_marginStart="24dp"
android:src="@drawable/ic_null"
app:tint="?attr/colorOnSurface" />

<LinearLayout
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:layout_centerVertical="true"
android:layout_marginVertical="22dp"
android:layout_marginStart="18dp"
android:layout_marginEnd="10dp"
android:layout_toStartOf="@id/settings_sw_biometric_auth"
android:layout_toEndOf="@id/settings_iv_biometric_auth"
android:orientation="vertical">

<TextView
style="@style/TextAppearance.Material3.TitleSmall"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/biometric_auth" />

<TextView
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:text="@string/biometric_auth_description"
android:textSize="12sp" />

</LinearLayout>

<com.google.android.material.materialswitch.MaterialSwitch
android:id="@+id/settings_sw_biometric_auth"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_centerVertical="true"
android:layout_marginEnd="24dp" />

</RelativeLayout>

<com.google.android.material.textview.MaterialTextView
style="@style/TextAppearance.Material3.TitleSmall"
android:layout_width="wrap_content"
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -130,4 +130,6 @@
<string name="set_password">Set password</string>
<string name="password_length_error">Password must be between 3 and 15 characters.</string>
<string name="password_mismatch_error">Passwords do not match.</string>
<string name="biometric_auth">Biometric authentication</string>
<string name="biometric_auth_description">Use biometric authentication of the device to access Amarok when available.</string>
</resources>

0 comments on commit 38b413c

Please sign in to comment.