From b529ca110ca30198eb9752006333e156d6cce567 Mon Sep 17 00:00:00 2001 From: IndusAryan Date: Tue, 19 Dec 2023 20:28:28 +0530 Subject: [PATCH 1/3] add biometric fingerprint authentication --- app/build.gradle.kts | 16 ++- app/src/main/AndroidManifest.xml | 2 + .../lagradost/cloudstream3/MainActivity.kt | 15 ++ .../utils/BiometricAuthenticator.kt | 132 ++++++++++++++++++ app/src/main/res/drawable/ic_fingerprint.xml | 11 ++ app/src/main/res/values/strings.xml | 2 + app/src/main/res/xml/settings_account.xml | 30 ++-- 7 files changed, 183 insertions(+), 25 deletions(-) create mode 100644 app/src/main/java/com/lagradost/cloudstream3/utils/BiometricAuthenticator.kt create mode 100644 app/src/main/res/drawable/ic_fingerprint.xml diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bae407fa..31d04311 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -159,15 +159,15 @@ dependencies { // Android Core & Lifecycle implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.appcompat:appcompat:1.6.1") - implementation("androidx.navigation:navigation-ui-ktx:2.7.5") + implementation("androidx.navigation:navigation-ui-ktx:2.7.6") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2") - implementation("androidx.navigation:navigation-fragment-ktx:2.7.5") + implementation("androidx.navigation:navigation-fragment-ktx:2.7.6") // Design & UI implementation("jp.wasabeef:glide-transformations:4.3.0") implementation("androidx.preference:preference-ktx:1.2.1") - implementation("com.google.android.material:material:1.10.0") + implementation("com.google.android.material:material:1.11.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") @@ -200,8 +200,10 @@ dependencies { implementation("com.github.albfernandez:juniversalchardet:2.4.0") // Subtitle Decoding // Crash Reports (AcraApplication.kt) - implementation("ch.acra:acra-core:5.11.2") - implementation("ch.acra:acra-toast:5.11.2") + implementation("ch.acra:acra-core:5.11.3") + implementation("ch.acra:acra-toast:5.11.3") + + implementation ("androidx.biometric:biometric:1.2.0-alpha05") // UI Stuff implementation("com.facebook.shimmer:shimmer:0.5.0") // Shimmering Effect (Loading Skeleton) @@ -224,8 +226,8 @@ dependencies { Level 25 or Less. */ // Downloading & Networking - implementation("androidx.work:work-runtime:2.8.1") - implementation("androidx.work:work-runtime-ktx:2.8.1") + implementation("androidx.work:work-runtime:2.9.0") + implementation("androidx.work:work-runtime-ktx:2.9.0") implementation("com.github.Blatzar:NiceHttp:0.4.4") // HTTP Lib } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index e665c3bc..d1d6dba6 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -15,6 +15,8 @@ + + diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 2819ab98..71d6e952 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -126,6 +126,9 @@ import com.lagradost.cloudstream3.utils.AppUtils.loadSearchResult import com.lagradost.cloudstream3.utils.AppUtils.setDefaultFocus import com.lagradost.cloudstream3.utils.BackupUtils.backup import com.lagradost.cloudstream3.utils.BackupUtils.setUpBackup +import com.lagradost.cloudstream3.utils.BiometricAuthenticator +import com.lagradost.cloudstream3.utils.BiometricAuthenticator.isTruePhone +import com.lagradost.cloudstream3.utils.BiometricAuthenticator.promptInfo import com.lagradost.cloudstream3.utils.Coroutines.ioSafe import com.lagradost.cloudstream3.utils.Coroutines.main import com.lagradost.cloudstream3.utils.DataStore.getKey @@ -1068,6 +1071,9 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { app.initClient(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) + + + val errorFile = filesDir.resolve("last_error") if (errorFile.exists() && errorFile.isFile) { lastError = errorFile.readText(Charset.defaultCharset()) @@ -1151,6 +1157,15 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { changeStatusBarState(isEmulatorSettings()) + val authEnabled = settingsManager.getBoolean( + getString(R.string.biometric_enabled_key), false) + + if (isTruePhone() && authEnabled) { + BiometricAuthenticator.initializeBiometrics(this@MainActivity) + BiometricAuthenticator.checkBiometricAvailability(this@MainActivity) + BiometricAuthenticator.biometricPrompt.authenticate(promptInfo) + } + // Automatically enable jsdelivr if cant connect to raw.githubusercontent.com if (this.getKey(getString(R.string.jsdelivr_proxy_key)) == null && isNetworkAvailable()) { main { diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BiometricAuthenticator.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BiometricAuthenticator.kt new file mode 100644 index 00000000..0759277d --- /dev/null +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BiometricAuthenticator.kt @@ -0,0 +1,132 @@ +package com.lagradost.cloudstream3.utils + +import android.content.Context +import android.os.Build +import android.util.Log +import android.widget.Toast +import androidx.biometric.BiometricManager +import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_STRONG +import androidx.biometric.BiometricManager.Authenticators.BIOMETRIC_WEAK +import androidx.biometric.BiometricManager.Authenticators.DEVICE_CREDENTIAL +import androidx.biometric.BiometricPrompt +import androidx.core.content.ContextCompat +import com.lagradost.cloudstream3.AcraApplication.Companion.context +import com.lagradost.cloudstream3.MainActivity +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isEmulatorSettings +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTrueTvSettings +import com.lagradost.cloudstream3.ui.settings.SettingsFragment.Companion.isTvSettings + +object BiometricAuthenticator { + + private lateinit var biometricManager: BiometricManager + lateinit var biometricPrompt: BiometricPrompt + lateinit var promptInfo: BiometricPrompt.PromptInfo + + fun initializeBiometrics(activity: MainActivity) { + val executor = ContextCompat.getMainExecutor(activity) + biometricManager = BiometricManager.from(activity) + + biometricPrompt = BiometricPrompt(activity, executor, + object : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + super.onAuthenticationError(errorCode, errString) + Toast.makeText(context?.applicationContext, "Authentication error: $errString", Toast.LENGTH_SHORT).show() + // Handle error as needed + } + + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + super.onAuthenticationSucceeded(result) + Toast.makeText(context?.applicationContext, "Authentication succeeded!", Toast.LENGTH_SHORT).show() + // Handle authentication success + } + + override fun onAuthenticationFailed() { + super.onAuthenticationFailed() + Toast.makeText(context?.applicationContext, "Authentication failed", Toast.LENGTH_SHORT).show() + // Handle authentication failure + } + }) + + promptInfo = BiometricPrompt.PromptInfo.Builder() + .setTitle("Biometric login for my app") + .setSubtitle("Log in using your biometric credential") + //.setNegativeButtonText("Use account password") + .setAllowedAuthenticators(BIOMETRIC_WEAK or BIOMETRIC_STRONG or DEVICE_CREDENTIAL) + .build() + } + + fun checkBiometricAvailability(context: Context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + when (biometricManager.canAuthenticate(BIOMETRIC_WEAK or BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) { + BiometricManager.BIOMETRIC_SUCCESS -> + Toast.makeText( + context, + "Biometric authentication is available", + Toast.LENGTH_SHORT + ).show() + + BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> + Toast.makeText( + context, + "This device doesn't support biometric authentication", + Toast.LENGTH_SHORT + ).show() + + BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> + Toast.makeText( + context, + "Biometric authentication is currently unavailable", + Toast.LENGTH_SHORT + ).show() + + BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> + Toast.makeText( + context, + "No biometric credentials are enrolled", + Toast.LENGTH_SHORT + ).show() + + BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> { + TODO() + } + + BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> { + TODO() + } + + BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> { + TODO() + } + } + } + + else { + when (biometricManager.canAuthenticate(BIOMETRIC_WEAK or BIOMETRIC_STRONG)) { + BiometricManager.BIOMETRIC_SUCCESS -> + Log.d("Cs3Auth", "App can authenticate using biometrics.") + BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> + Log.e("Cs3Auth", "No biometric features available on this device.") + BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> + Log.e("Cs3Auth", "Biometric features are currently unavailable.") + BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> + Log.e("Cs3Auth", "Biometric features are currently unavailable.") + + BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> { + TODO() + } + + BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> { + TODO() + } + + BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> { + TODO() + } + } + } + } + + fun isTruePhone(): Boolean { + return !isTrueTvSettings() && !isTvSettings() && context?.isEmulatorSettings() != true + } +} \ No newline at end of file diff --git a/app/src/main/res/drawable/ic_fingerprint.xml b/app/src/main/res/drawable/ic_fingerprint.xml new file mode 100644 index 00000000..5c96e5a5 --- /dev/null +++ b/app/src/main/res/drawable/ic_fingerprint.xml @@ -0,0 +1,11 @@ + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index ce660a67..8e9140ec 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -67,6 +67,7 @@ enable_nsfw_on_providers_key skip_startup_account_select_key enable_skip_op_from_database + biometric_key %d %s | %s %s • %s @@ -733,4 +734,5 @@ Logged in as %s Skip account selection at startup Use Default Account + Use Fingerprint Authentication diff --git a/app/src/main/res/xml/settings_account.xml b/app/src/main/res/xml/settings_account.xml index ec882088..f31c205c 100644 --- a/app/src/main/res/xml/settings_account.xml +++ b/app/src/main/res/xml/settings_account.xml @@ -1,11 +1,5 @@ - - + - - - - - - - - - - - + + + \ No newline at end of file From 0d76753df0f4f5c3e8f76b49beb9fcb6e312a02b Mon Sep 17 00:00:00 2001 From: IndusAryan Date: Tue, 19 Dec 2023 21:59:09 +0530 Subject: [PATCH 2/3] add logging and preference --- .idea/gradle.xml | 6 +- app/build.gradle.kts | 17 +++-- app/src/main/AndroidManifest.xml | 2 - .../lagradost/cloudstream3/MainActivity.kt | 1 + .../utils/BiometricAuthenticator.kt | 67 +++++++++++-------- app/src/main/res/values/strings.xml | 4 +- app/src/main/res/xml/settings_account.xml | 4 +- 7 files changed, 53 insertions(+), 48 deletions(-) diff --git a/.idea/gradle.xml b/.idea/gradle.xml index c5c0ff3b..0897082f 100644 --- a/.idea/gradle.xml +++ b/.idea/gradle.xml @@ -4,17 +4,15 @@ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index 31d04311..183b8697 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -159,15 +159,15 @@ dependencies { // Android Core & Lifecycle implementation("androidx.core:core-ktx:1.12.0") implementation("androidx.appcompat:appcompat:1.6.1") - implementation("androidx.navigation:navigation-ui-ktx:2.7.6") + implementation("androidx.navigation:navigation-ui-ktx:2.7.5") implementation("androidx.lifecycle:lifecycle-livedata-ktx:2.6.2") implementation("androidx.lifecycle:lifecycle-viewmodel-ktx:2.6.2") - implementation("androidx.navigation:navigation-fragment-ktx:2.7.6") + implementation("androidx.navigation:navigation-fragment-ktx:2.7.5") // Design & UI implementation("jp.wasabeef:glide-transformations:4.3.0") implementation("androidx.preference:preference-ktx:1.2.1") - implementation("com.google.android.material:material:1.11.0") + implementation("com.google.android.material:material:1.10.0") implementation("androidx.constraintlayout:constraintlayout:2.1.4") implementation("androidx.swiperefreshlayout:swiperefreshlayout:1.1.0") @@ -200,16 +200,15 @@ dependencies { implementation("com.github.albfernandez:juniversalchardet:2.4.0") // Subtitle Decoding // Crash Reports (AcraApplication.kt) - implementation("ch.acra:acra-core:5.11.3") - implementation("ch.acra:acra-toast:5.11.3") - - implementation ("androidx.biometric:biometric:1.2.0-alpha05") + implementation("ch.acra:acra-core:5.11.2") + implementation("ch.acra:acra-toast:5.11.2") // UI Stuff implementation("com.facebook.shimmer:shimmer:0.5.0") // Shimmering Effect (Loading Skeleton) implementation("androidx.palette:palette-ktx:1.0.0") // Palette For Images -> Colors implementation("androidx.tvprovider:tvprovider:1.0.0") implementation("com.github.discord:OverlappingPanels:0.1.5") // Gestures + implementation ("androidx.biometric:biometric:1.2.0-alpha05") // Fingerprint Authentication implementation("com.github.rubensousa:previewseekbar-media3:1.1.1.0") // SeekBar Preview // Extensions & Other Libs @@ -226,8 +225,8 @@ dependencies { Level 25 or Less. */ // Downloading & Networking - implementation("androidx.work:work-runtime:2.9.0") - implementation("androidx.work:work-runtime-ktx:2.9.0") + implementation("androidx.work:work-runtime:2.8.1") + implementation("androidx.work:work-runtime-ktx:2.8.1") implementation("com.github.Blatzar:NiceHttp:0.4.4") // HTTP Lib } diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index d1d6dba6..a23ef725 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -14,9 +14,7 @@ - - diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 71d6e952..2d3e3153 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -1157,6 +1157,7 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { changeStatusBarState(isEmulatorSettings()) + /** Biometric Stuff **/ val authEnabled = settingsManager.getBoolean( getString(R.string.biometric_enabled_key), false) diff --git a/app/src/main/java/com/lagradost/cloudstream3/utils/BiometricAuthenticator.kt b/app/src/main/java/com/lagradost/cloudstream3/utils/BiometricAuthenticator.kt index 0759277d..fd7aa525 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/utils/BiometricAuthenticator.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/utils/BiometricAuthenticator.kt @@ -26,29 +26,28 @@ object BiometricAuthenticator { val executor = ContextCompat.getMainExecutor(activity) biometricManager = BiometricManager.from(activity) - biometricPrompt = BiometricPrompt(activity, executor, - object : BiometricPrompt.AuthenticationCallback() { + biometricPrompt = BiometricPrompt(activity, executor, object : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { super.onAuthenticationError(errorCode, errString) Toast.makeText(context?.applicationContext, "Authentication error: $errString", Toast.LENGTH_SHORT).show() - // Handle error as needed + activity.finish() } override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { super.onAuthenticationSucceeded(result) - Toast.makeText(context?.applicationContext, "Authentication succeeded!", Toast.LENGTH_SHORT).show() - // Handle authentication success + Log.d("Cs3Auth", "Biometric succeeded.") } override fun onAuthenticationFailed() { super.onAuthenticationFailed() Toast.makeText(context?.applicationContext, "Authentication failed", Toast.LENGTH_SHORT).show() - // Handle authentication failure + activity.finish() } }) promptInfo = BiometricPrompt.PromptInfo.Builder() - .setTitle("Biometric login for my app") + .setTitle("CloudStream") .setSubtitle("Log in using your biometric credential") //.setNegativeButtonText("Use account password") .setAllowedAuthenticators(BIOMETRIC_WEAK or BIOMETRIC_STRONG or DEVICE_CREDENTIAL) @@ -56,28 +55,19 @@ object BiometricAuthenticator { } fun checkBiometricAvailability(context: Context) { + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R) { + + // Strong and credential bundle cannot be checked at same time in API < 11 when (biometricManager.canAuthenticate(BIOMETRIC_WEAK or BIOMETRIC_STRONG or DEVICE_CREDENTIAL)) { BiometricManager.BIOMETRIC_SUCCESS -> - Toast.makeText( - context, - "Biometric authentication is available", - Toast.LENGTH_SHORT - ).show() + Log.d("Cs3Auth", "App can authenticate.") BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> - Toast.makeText( - context, - "This device doesn't support biometric authentication", - Toast.LENGTH_SHORT - ).show() + Log.d("Cs3Auth", "No biometric sensor found.") BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> - Toast.makeText( - context, - "Biometric authentication is currently unavailable", - Toast.LENGTH_SHORT - ).show() + Log.d("Cs3Auth", "Biometric authentication is currently unavailable.") BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> Toast.makeText( @@ -87,21 +77,31 @@ object BiometricAuthenticator { ).show() BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> { - TODO() + Toast.makeText( + context, + "Please update your software and security patches.", + Toast.LENGTH_SHORT + ).show() } BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> { - TODO() + Toast.makeText( + context, + "Please update your software and security patches.", + Toast.LENGTH_SHORT + ).show() } BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> { - TODO() + Log.d("Cs3Auth", "Unknown error encountered(Biometric data failed).") } } } else { + when (biometricManager.canAuthenticate(BIOMETRIC_WEAK or BIOMETRIC_STRONG)) { + BiometricManager.BIOMETRIC_SUCCESS -> Log.d("Cs3Auth", "App can authenticate using biometrics.") BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> @@ -112,21 +112,30 @@ object BiometricAuthenticator { Log.e("Cs3Auth", "Biometric features are currently unavailable.") BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> { - TODO() + Toast.makeText( + context, + "Please update your software and security patches.", + Toast.LENGTH_SHORT + ).show() } BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> { - TODO() + Toast.makeText( + context, + "Please update your software and security patches.", + Toast.LENGTH_SHORT + ).show() } BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> { - TODO() + Log.d("Cs3Auth", "Unknown error encountered(Biometric data failed).") } } } } + // yes, this feature is phone exclusive fun isTruePhone(): Boolean { return !isTrueTvSettings() && !isTvSettings() && context?.isEmulatorSettings() != true } -} \ No newline at end of file +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 8e9140ec..7acf738e 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -240,7 +240,7 @@ Error backing up %s Search Library - Accounts + Accounts and Security Updates and backup Info Advanced Search @@ -734,5 +734,5 @@ Logged in as %s Skip account selection at startup Use Default Account - Use Fingerprint Authentication + Use Fingerprint authentication diff --git a/app/src/main/res/xml/settings_account.xml b/app/src/main/res/xml/settings_account.xml index f31c205c..d0316f96 100644 --- a/app/src/main/res/xml/settings_account.xml +++ b/app/src/main/res/xml/settings_account.xml @@ -23,9 +23,9 @@ android:key="@string/skip_startup_account_select_key" android:title="@string/skip_startup_account_select_pref" /> - From 24eb6d6d5d88ef4ee7cd9a221fc6a598e5685a7e Mon Sep 17 00:00:00 2001 From: IndusAryan Date: Tue, 19 Dec 2023 22:03:24 +0530 Subject: [PATCH 3/3] lol --- app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt | 3 --- 1 file changed, 3 deletions(-) diff --git a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt index 2d3e3153..e650700c 100644 --- a/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt +++ b/app/src/main/java/com/lagradost/cloudstream3/MainActivity.kt @@ -1071,9 +1071,6 @@ class MainActivity : AppCompatActivity(), ColorPickerDialogListener { app.initClient(this) val settingsManager = PreferenceManager.getDefaultSharedPreferences(this) - - - val errorFile = filesDir.resolve("last_error") if (errorFile.exists() && errorFile.isFile) { lastError = errorFile.readText(Charset.defaultCharset())