diff --git a/app/build.gradle b/app/build.gradle index b07f0e7b..3cc8fd32 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -54,14 +54,14 @@ configurations { } dependencies { - def lifecycle_version = '2.3.0-rc01' + def lifecycle_version = '2.3.0' def markwon_version = '4.6.1' def work_version = "2.5.0" def acra = "5.7.0" implementation fileTree(include: ['*.jar'], dir: 'libs') implementation 'androidx.appcompat:appcompat:1.3.0-beta01' - implementation 'com.google.android.material:material:1.3.0-alpha03' // Upgrading to rc01 results in failing builds + implementation 'com.google.android.material:material:1.3.0' implementation 'androidx.constraintlayout:constraintlayout:2.0.4' implementation "androidx.legacy:legacy-support-v4:1.0.0" implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycle_version" @@ -112,5 +112,6 @@ dependencies { implementation "org.codeberg.gitnex-garage:emoji-java:v5.1.2" implementation "org.codeberg.gitnex:tea4j:1.0.1" coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:1.1.1" + implementation 'androidx.biometric:biometric:1.1.0' } diff --git a/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java b/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java index 0a3657aa..fcec209f 100644 --- a/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/BaseActivity.java @@ -77,7 +77,6 @@ public abstract class BaseActivity extends AppCompatActivity { } AppUtil.setAppLocale(getResources(), tinyDB.getString("locale")); - } } diff --git a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java index 61cff226..b02a2740 100644 --- a/app/src/main/java/org/mian/gitnex/activities/MainActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/MainActivity.java @@ -18,6 +18,8 @@ import androidx.appcompat.app.ActionBarDrawerToggle; import androidx.appcompat.app.AlertDialog; import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.widget.Toolbar; +import androidx.biometric.BiometricPrompt; +import androidx.core.content.ContextCompat; import androidx.core.content.res.ResourcesCompat; import androidx.core.view.GravityCompat; import androidx.drawerlayout.widget.DrawerLayout; @@ -58,6 +60,7 @@ import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.Version; import java.util.ArrayList; import java.util.List; +import java.util.concurrent.Executor; import eightbitlab.com.blurview.BlurView; import eightbitlab.com.blurview.RenderScriptBlur; import retrofit2.Call; @@ -158,6 +161,43 @@ public class MainActivity extends BaseActivity implements NavigationView.OnNavig break; } + // biometric auth + if(tinyDB.getBoolean("biometricStatus")) { + + Executor executor = ContextCompat.getMainExecutor(this); + + BiometricPrompt biometricPrompt = new BiometricPrompt(this, executor, new BiometricPrompt.AuthenticationCallback() { + + @Override + public void onAuthenticationError(int errorCode, @NonNull CharSequence errString) { + + super.onAuthenticationError(errorCode, errString); + + // Authentication error, close the app + if(errorCode == BiometricPrompt.ERROR_USER_CANCELED || + errorCode == BiometricPrompt.ERROR_NEGATIVE_BUTTON) { + + finish(); + } + } + + // Authentication succeeded, continue to app + @Override public void onAuthenticationSucceeded(@NonNull BiometricPrompt.AuthenticationResult result) { super.onAuthenticationSucceeded(result); } + + // Authentication failed, close the app + @Override public void onAuthenticationFailed() { super.onAuthenticationFailed(); } + + }); + + BiometricPrompt.PromptInfo biometricPromptBuilder = new BiometricPrompt.PromptInfo.Builder() + .setTitle(getString(R.string.biometricAuthTitle)) + .setSubtitle(getString(R.string.biometricAuthSubTitle)) + .setNegativeButtonText(getString(R.string.cancelButton)).build(); + + biometricPrompt.authenticate(biometricPromptBuilder); + + } + toolbarTitle.setTypeface(myTypeface); setSupportActionBar(toolbar); diff --git a/app/src/main/java/org/mian/gitnex/activities/SettingsSecurityActivity.java b/app/src/main/java/org/mian/gitnex/activities/SettingsSecurityActivity.java index ad86d2ab..0e81ccb0 100644 --- a/app/src/main/java/org/mian/gitnex/activities/SettingsSecurityActivity.java +++ b/app/src/main/java/org/mian/gitnex/activities/SettingsSecurityActivity.java @@ -1,7 +1,9 @@ package org.mian.gitnex.activities; +import android.app.KeyguardManager; import android.content.Context; import android.content.Intent; +import android.os.Build; import android.os.Bundle; import android.util.Log; import android.view.View; @@ -9,6 +11,8 @@ import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.TextView; import androidx.appcompat.app.AlertDialog; +import androidx.biometric.BiometricManager; +import com.google.android.material.switchmaterial.SwitchMaterial; import org.apache.commons.io.FileUtils; import org.mian.gitnex.R; import org.mian.gitnex.databinding.ActivitySettingsSecurityBinding; @@ -16,6 +20,8 @@ import org.mian.gitnex.helpers.Toasty; import org.mian.gitnex.helpers.ssl.MemorizingTrustManager; import java.io.File; import java.io.IOException; +import static androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG; +import static androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL; /** * Author M M Arif @@ -53,6 +59,8 @@ public class SettingsSecurityActivity extends BaseActivity { LinearLayout cacheSizeImagesFrame = activitySettingsSecurityBinding.cacheSizeImagesSelectionFrame; LinearLayout clearCacheFrame = activitySettingsSecurityBinding.clearCacheSelectionFrame; + SwitchMaterial switchBiometric = activitySettingsSecurityBinding.switchBiometric; + if(!tinyDB.getString("cacheSizeStr").isEmpty()) { cacheSizeDataSelected.setText(tinyDB.getString("cacheSizeStr")); @@ -73,6 +81,70 @@ public class SettingsSecurityActivity extends BaseActivity { cacheSizeImagesSelectedChoice = tinyDB.getInt("cacheSizeImagesId"); } + switchBiometric.setChecked(tinyDB.getBoolean("biometricStatus")); + + // biometric switcher + switchBiometric.setOnCheckedChangeListener((buttonView, isChecked) -> { + + if(Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + + if(isChecked) { + + BiometricManager biometricManager = BiometricManager.from(ctx); + KeyguardManager keyguardManager = (KeyguardManager) ctx.getSystemService(Context.KEYGUARD_SERVICE); + + if (!keyguardManager.isDeviceSecure()) { + + switch(biometricManager.canAuthenticate(BIOMETRIC_STRONG | DEVICE_CREDENTIAL)) { + + case BiometricManager.BIOMETRIC_SUCCESS: + + tinyDB.putBoolean("biometricStatus", true); + Toasty.success(appCtx, getResources().getString(R.string.settingsSave)); + break; + case BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE: + case BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED: + case BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED: + case BiometricManager.BIOMETRIC_STATUS_UNKNOWN: + + tinyDB.putBoolean("biometricStatus", false); + switchBiometric.setChecked(false); + Toasty.error(appCtx, getResources().getString(R.string.biometricNotSupported)); + break; + case BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE: + + tinyDB.putBoolean("biometricStatus", false); + switchBiometric.setChecked(false); + Toasty.error(appCtx, getResources().getString(R.string.biometricNotAvailable)); + break; + case BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED: + + tinyDB.putBoolean("biometricStatus", false); + switchBiometric.setChecked(false); + Toasty.info(appCtx, getResources().getString(R.string.enrollBiometric)); + break; + } + } + else { + + tinyDB.putBoolean("biometricStatus", true); + Toasty.success(appCtx, getResources().getString(R.string.settingsSave)); + } + } + else { + + tinyDB.putBoolean("biometricStatus", false); + Toasty.success(appCtx, getResources().getString(R.string.settingsSave)); + } + } + else { + + tinyDB.putBoolean("biometricStatus", false); + Toasty.success(appCtx, getResources().getString(R.string.biometricNotSupported)); + } + + }); + // clear cache setter File cacheDir = appCtx.getCacheDir(); clearCacheSelected.setText(FileUtils.byteCountToDisplaySize((int) FileUtils.sizeOfDirectory(cacheDir))); diff --git a/app/src/main/java/org/mian/gitnex/core/MainApplication.java b/app/src/main/java/org/mian/gitnex/core/MainApplication.java index f786007d..ceed209a 100644 --- a/app/src/main/java/org/mian/gitnex/core/MainApplication.java +++ b/app/src/main/java/org/mian/gitnex/core/MainApplication.java @@ -123,5 +123,12 @@ public class MainApplication extends Application { tinyDB.putInt("pollingDelayMinutes", StaticGlobalVariables.defaultPollingDelay); } + + // disable biometric by default + if(tinyDB.getString("biometricStatusInit").isEmpty()) { + + tinyDB.putBoolean("biometricStatus", false); + tinyDB.putString("biometricStatusInit", "yes"); + } } } diff --git a/app/src/main/res/layout/activity_add_collaborator_to_repository.xml b/app/src/main/res/layout/activity_add_collaborator_to_repository.xml index df68bdb9..ab0394e0 100644 --- a/app/src/main/res/layout/activity_add_collaborator_to_repository.xml +++ b/app/src/main/res/layout/activity_add_collaborator_to_repository.xml @@ -42,11 +42,12 @@ - diff --git a/app/src/main/res/layout/activity_add_new_team_member.xml b/app/src/main/res/layout/activity_add_new_team_member.xml index cb09ba62..95ca58e1 100644 --- a/app/src/main/res/layout/activity_add_new_team_member.xml +++ b/app/src/main/res/layout/activity_add_new_team_member.xml @@ -42,12 +42,13 @@ - - - - diff --git a/app/src/main/res/layout/activity_file_diff.xml b/app/src/main/res/layout/activity_file_diff.xml index 9c465288..2268bb74 100644 --- a/app/src/main/res/layout/activity_file_diff.xml +++ b/app/src/main/res/layout/activity_file_diff.xml @@ -61,11 +61,12 @@ - diff --git a/app/src/main/res/layout/activity_file_view.xml b/app/src/main/res/layout/activity_file_view.xml index aff93f71..57e080b2 100644 --- a/app/src/main/res/layout/activity_file_view.xml +++ b/app/src/main/res/layout/activity_file_view.xml @@ -45,11 +45,12 @@ - - - - - - - + + + + + + + + - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_issues.xml b/app/src/main/res/layout/fragment_issues.xml index e8237bd6..fb1aa9ec 100644 --- a/app/src/main/res/layout/fragment_issues.xml +++ b/app/src/main/res/layout/fragment_issues.xml @@ -20,19 +20,21 @@ - - - - - - - - - - diff --git a/app/src/main/res/layout/fragment_organizations.xml b/app/src/main/res/layout/fragment_organizations.xml index d14654ef..e7a0ae12 100644 --- a/app/src/main/res/layout/fragment_organizations.xml +++ b/app/src/main/res/layout/fragment_organizations.xml @@ -19,11 +19,12 @@ - - - - - - - - diff --git a/app/src/main/res/layout/fragment_repositories.xml b/app/src/main/res/layout/fragment_repositories.xml index a3b7ea53..91ca3bc7 100644 --- a/app/src/main/res/layout/fragment_repositories.xml +++ b/app/src/main/res/layout/fragment_repositories.xml @@ -21,11 +21,12 @@ - - - - - - @style/customOverflowMenuStyle @color/lightThemeInputBackground @style/inputsMaterialComponentCorner + @style/WindowAnimationTransition @@ -79,6 +80,7 @@ @style/customOverflowMenuStyle @color/retroThemeInputBackground @style/inputsMaterialComponentCorner + @style/WindowAnimationTransition diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index c920d761..41bc002c 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -275,6 +275,7 @@ Choose what screen should be loaded if the app cannot handle external links. It will redirect you automatically. N/A Select Default Link Handler Screen + Biometric Support No more data available @@ -733,5 +734,12 @@ White on Grey Dark on White + Biometric Authentication + Unlock using your biometric credentials + No biometric features available on this device + Biometric features are currently unavailable + Enroll biometric from phone settings + Login ID \'%s\' copied to clipboard + diff --git a/app/src/main/res/values/themes.xml b/app/src/main/res/values/themes.xml index a2af303a..58880a3f 100644 --- a/app/src/main/res/values/themes.xml +++ b/app/src/main/res/values/themes.xml @@ -38,6 +38,7 @@ @style/customOverflowMenuStyle @color/inputBackground @style/inputsMaterialComponentCorner + @style/WindowAnimationTransition @@ -78,6 +79,7 @@ @style/customOverflowMenuStyle @color/lightThemeInputBackground @style/inputsMaterialComponentCorner + @style/WindowAnimationTransition @@ -118,6 +120,7 @@ @style/customOverflowMenuStyle @color/retroThemeInputBackground @style/inputsMaterialComponentCorner + @style/WindowAnimationTransition @@ -158,6 +161,7 @@ @style/customOverflowMenuStyle @color/inputBackground @style/inputsMaterialComponentCorner + @style/WindowAnimationTransition @@ -271,6 +275,11 @@ ?attr/iconsColor + +