From 88927341206333532c62510ed6ca5e040835a1f2 Mon Sep 17 00:00:00 2001 From: Anton Tananaev Date: Tue, 25 Oct 2022 17:26:34 -0700 Subject: [PATCH] Support biometric login --- app/build.gradle | 5 +- app/src/google/AndroidManifest.xml | 3 +- .../java/org/traccar/manager/MainFragment.kt | 12 +++ .../org/traccar/manager/SecurityManager.kt | 75 +++++++++++++++++++ app/src/main/res/values/strings.xml | 2 + build.gradle | 2 +- 6 files changed, 94 insertions(+), 5 deletions(-) create mode 100644 app/src/main/java/org/traccar/manager/SecurityManager.kt diff --git a/app/build.gradle b/app/build.gradle index df11bc7..b2597c4 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -2,12 +2,12 @@ apply plugin: 'com.android.application' apply plugin: 'kotlin-android' android { - compileSdkVersion 32 + compileSdkVersion 33 defaultConfig { applicationId 'org.traccar.manager' minSdkVersion 19 - targetSdkVersion 32 + targetSdkVersion 33 versionCode 33 versionName '3.2' multiDexEnabled true @@ -29,6 +29,7 @@ dependencies { implementation 'androidx.localbroadcastmanager:localbroadcastmanager:1.1.0' implementation 'androidx.preference:preference-ktx:1.2.0' implementation 'androidx.core:core-ktx:1.8.0' + implementation 'androidx.biometric:biometric-ktx:1.2.0-alpha05' implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" googleImplementation platform('com.google.firebase:firebase-bom:29.0.3') googleImplementation 'com.google.firebase:firebase-core' diff --git a/app/src/google/AndroidManifest.xml b/app/src/google/AndroidManifest.xml index 468e5b7..4438557 100644 --- a/app/src/google/AndroidManifest.xml +++ b/app/src/google/AndroidManifest.xml @@ -1,8 +1,7 @@ + xmlns:tools="http://schemas.android.com/tools"> 6) { + SecurityManager.saveToken(activity, message.substring(6)) + } broadcastManager.sendBroadcast(Intent(EVENT_LOGIN)) + } else if (message.startsWith("authentication")) { + SecurityManager.readToken(activity) { token -> + if (token != null) { + val code = "handleLoginToken && handleLoginToken('$token')" + webView.evaluateJavascript(code, null) + } + } + } else if (message.startsWith("logout")) { + SecurityManager.deleteToken(activity) } else if (message.startsWith("server")) { val url = message.substring(7) PreferenceManager.getDefaultSharedPreferences(activity) diff --git a/app/src/main/java/org/traccar/manager/SecurityManager.kt b/app/src/main/java/org/traccar/manager/SecurityManager.kt new file mode 100644 index 0000000..d68e43d --- /dev/null +++ b/app/src/main/java/org/traccar/manager/SecurityManager.kt @@ -0,0 +1,75 @@ +/* + * Copyright 2022 Anton Tananaev (anton@traccar.org) + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ +package org.traccar.manager + +import android.app.Activity +import androidx.biometric.BiometricPrompt +import androidx.core.content.ContextCompat +import androidx.fragment.app.FragmentActivity +import androidx.preference.PreferenceManager + +object SecurityManager { + + fun saveToken(activity: Activity, token: String) { + PreferenceManager.getDefaultSharedPreferences(activity) + .edit().putString(KEY_TOKEN, token).apply() + } + + fun readToken(activity: Activity, onResult: (String?) -> Unit) { + val token = PreferenceManager.getDefaultSharedPreferences(activity) + .getString(KEY_TOKEN, null) + if (token != null) { + authenticate(activity) { + onResult(if (it) token else null) + } + } + onResult(null) + } + + fun deleteToken(activity: Activity) { + PreferenceManager.getDefaultSharedPreferences(activity) + .edit().remove(KEY_TOKEN).apply() + } + + private fun authenticate(activity: Activity, onResult: (Boolean) -> Unit) { + val executor = ContextCompat.getMainExecutor(activity) + val biometricPrompt = BiometricPrompt( + activity as FragmentActivity, + executor, + object : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + onResult(false) + } + + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + onResult(true) + } + + override fun onAuthenticationFailed() { + onResult(false) + } + }, + ) + val promptInfo = BiometricPrompt.PromptInfo.Builder() + .setTitle(activity.getString(R.string.biometric_title)) + .setNegativeButtonText(activity.getString(R.string.biometric_cancel)) + .build() + activity.runOnUiThread { biometricPrompt.authenticate(promptInfo) } + } + + private const val KEY_TOKEN = "managerToken" + +} diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index feea781..1d7c2a6 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -7,4 +7,6 @@ File Browser Default App requires location permission to show current location on the map + Biometric login + Use account password diff --git a/build.gradle b/build.gradle index 14053b0..663fcf5 100644 --- a/build.gradle +++ b/build.gradle @@ -5,7 +5,7 @@ buildscript { mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:7.2.2' + classpath 'com.android.tools.build:gradle:7.3.1' classpath 'com.google.gms:google-services:4.3.13' classpath 'com.google.firebase:firebase-crashlytics-gradle:2.9.1' classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlin_version"