如何在用户最小化应用程序但在跨活动移动时使指纹验证无效?

问题描述 投票:0回答:2

我有一个应用程序,其中使用指纹锁来保护该应用程序在未经指纹验证的情况下不被使用。 问题是验证仅在应用程序首次启动时进行;因此,首次启动后,在再次启动应用程序之前不再进行验证, 但我希望当前验证在应用程序中的不同活动之间移动时有效,但如果用户最小化应用程序或手机屏幕关闭时,它应该无效,以便用户在最小化后再次验证指纹并恢复回到应用程序。

我尝试使用基本活动并覆盖 onPause 和 onResume,但即使在跨活动移动时也会调用这些方法,这不是我想要的。

android android-lifecycle fingerprint
2个回答
1
投票

您可以使用系统广播意图

ACTION_SCREEN_ON
ACTION_SCREEN_OFF
和/或
ACTION_USER_PRESENT
来实现您的目标。检查 https://developer.android.com/reference/android/content/Intent 了解更多信息。

为此,请注册为广播接收者,以便在您的主要活动

onCreate
中接收这些通知,在
onDestroy
中取消注册。您可以使用这些通知来设置指示是否需要身份验证的标志。

示例代码:

    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
    .
    .
    .
        // register to receive appropriate events

        IntentFilter intentUserPresent = new IntentFilter(Intent.ACTION_USER_PRESENT);
        intentUserPresent.addAction(Intent.ACTION_USER_PRESENT);
        registerReceiver(userActionReceiver, intentUserPresent);
        
    .
    .
    .
    }
    

    // create a BroadcastReceiver to receive the notifications
    private BroadcastReceiver userActionReceiver = new BroadcastReceiver()
    {
        @Override
        public void onReceive(Context context, Intent intent)
        {
            Log.d("MyLog", String.format("MainActivity received broadcaast %s", intent.toString()));

            if (intent.getAction() == Intent.ACTION_USER_PRESENT)
            {
               // set flag indicating re-authentication is needed
            }
        }
    };
    
    @Override
    protected void onDestroy()
    {
        // make sure to un-register the receiver!
        unregisterReceiver(userActionReceiver);
        super.onDestroy();
    }

这不会检测到用户“最小化应用程序”,但不清楚为什么您会认为在这种情况下需要重新身份验证。生命周期事件 onStart/onStop 可以检测 Activity 的背景/前景。


0
投票

几周前解决了这个问题,当时我正在处理一个与我提出问题时正在处理的项目完全不同的项目。 我很高兴分享我的解决方法。

最小化应用程序三秒或灭屏后应用程序被锁定;只要在应用程序的首选项设置中启用指纹开关即可。

首先,我创建了一个 BaseActivity 类,所有其他项目活动都从该类继承,除了直接从 AppCompatActivity() 继承的 Fingerprint 活动类之外

package com.domainName.appName.ui.activities

import android.content.Intent
import android.os.Bundle
import android.os.Handler
import android.util.Log
import androidx.appcompat.app.AppCompatActivity


open class BaseActivity : AppCompatActivity() {
private var securitySet=false
private var backPressed=false
private var goingToFingerprint=false
companion object{
private var handler:Handler?=null
}
   override fun 
  onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState)
    prefSaver.save(prefFileName, "launched_new_activity",sValue = true,saveImmediately=true)
    if(handler==null){
        handler= Handler(mainLooper)
    }      

  handler?.removeCallbacksAndMessages(null)
    
    }

override fun onDestroy() {
    super.onDestroy()        
handler?.removeCallbacksAndMessages(null)
   }

override fun onStart() {
    handler?.removeCallbacksAndMessages(null)
    goingToFingerprint=false
     securitySet=prefChecker.check(prefFileName,"fingerprint_security_switch",false)
    super.onStart()
}
override fun onPause() {
    super.onPause()
    prefSaver.save(prefFileName,"launched_new_activity",sValue = false,saveImmediately=true)
    if(securitySet && !backPressed){
       if(!goingToFingerprint){postLock()}
    }
}

override fun onResume() {
    super.onResume()
    backPressed=false
    if(prefChecker.check(prefFileName,"app_locked",false)&&securitySet){
        prefSaver.save(prefFileName,"launched_new_activity",sValue = true,saveImmediately=true)
            goingToFingerprint=true
        startActivity(Intent(this,FingerprintActivity::class.java))
    }
}

override fun onBackPressed() {
    backPressed=true
    super.onBackPressed()
}
private fun postLock(){
handler?.removeCallbacksAndMessages(null)
handler?.postDelayed({
      if(!prefChecker.check(prefFileName,"launched_new_activity",false)) {
          prefSaver.save(prefFileName,"app_locked",sValue = true,saveImmediately=true)
      }
},3000)
}

}

我创建了一个 InitialActivity,它在 Android 清单中设置了 noHistory 模式,并充当应用程序启动器活动。 InitialActivity 也继承自 BaseActivity,并且只需在 MainActivity 活动的 onCreate 方法中调用 startActivity() 即可。

InitialActivity 以这种方式覆盖 onPause :

override fun onPause() {
    if(prefChecker.check(prefFileName,"fingerprint_security_switch",false)) {
        prefSaver.save(prefFileName,"app_locked",sValue = true,true)
/*setting this so app is locked on First Launch if the fingerprint switch is on.
The fingerprint switch is turned on and off from the SettingsActivity */
    }
    super.onPause()
    }

那么FingerprintActivity是这样实现的:

    package com.domainName.appName.ui.activities
import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.os.Bundle
import android.view.Gravity
import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.biometric.BiometricManager
import androidx.biometric.BiometricPrompt
import androidx.biometric.BiometricPrompt.PromptInfo
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat


class FingerprintActivity : AppCompatActivity() {

   private lateinit var executor: Executor
   var biometricPrompt: BiometricPrompt? = null
var promptInfo: PromptInfo? = null
var notifIntentLabel: String? = null
var finishImmediately=false
override fun 
onCreate(savedInstanceState: Bundle?) {
    super.onCreate(savedInstanceState);setContentView(R.layout.fingerprint_main)

    executor = ContextCompat.getMainExecutor(this)
    
    finishImmediately = intent.getBooleanExtra("finishImmediately",false)
    if(finishImmediately)finish()

    biometricPrompt = BiometricPrompt(this,
        executor, object : BiometricPrompt.AuthenticationCallback() {
            override fun onAuthenticationError(errorCode: Int, errString: CharSequence) {
                super.onAuthenticationError(errorCode, errString)
                    biometricPrompt?.cancelAuthentication()
                val intent=Intent(this@FingerprintActivity,FingerprintActivity::class.java)
                intent.flags = Intent.FLAG_ACTIVITY_CLEAR_TASK
                intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
                intent.putExtra("finishImmediately",true)
                startActivity(intent)
            }

            override fun onAuthenticationSucceeded(
                result: BiometricPrompt.AuthenticationResult
            ) {
                super.onAuthenticationSucceeded(result)
                prefSaver.save(prefFileName,"app_locked",sValue = false,true)
                finish()
            }
        })
    promptInfo = PromptInfo.Builder()
        .setTitle("AppName Biometric Login")
        .setSubtitle("\t\tLog in using your fingerprint")
        .setNegativeButtonText("Cancel")
        .build()
    val biometricManager = BiometricManager.from(this)
    when (biometricManager.canAuthenticate(BiometricManager.Authenticators.BIOMETRIC_WEAK)) {
        BiometricManager.BIOMETRIC_ERROR_NO_HARDWARE -> showToast("No fingerprint hardware detected on this device.")
        BiometricManager.BIOMETRIC_ERROR_HW_UNAVAILABLE -> showToast("Fingerprint hardware features are currently unavailable on this device.")
        BiometricManager.BIOMETRIC_ERROR_NONE_ENROLLED -> showToast("There is no any fingerprint credential associated with your account.\nPlease go to your phone settings to enrol fingerprints.")
        BiometricManager.BIOMETRIC_SUCCESS -> authenticateUser()
        BiometricManager.BIOMETRIC_ERROR_SECURITY_UPDATE_REQUIRED -> showToast("Fingerprint feature on device requires updating and is therefore currently unavailable.")
        BiometricManager.BIOMETRIC_ERROR_UNSUPPORTED -> showToast("Fingerprint features not supported on device")
        BiometricManager.BIOMETRIC_STATUS_UNKNOWN -> showToast("Fingerprint features status unknown")
    }
}

private fun showToast(txt_to_show: String) {
    val t = Toast.makeText(this, txt_to_show, Toast.LENGTH_SHORT)
    t.setGravity(Gravity.CENTER or Gravity.CENTER_HORIZONTAL, 0, 0)
    t.show()
}

private fun authenticateUser() {
    if(!finishImmediately){
        if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.USE_FINGERPRINT
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            promptInfo?.let{ biometricPrompt?.authenticate(it)}
        } else if (ActivityCompat.checkSelfPermission(
                this,
                Manifest.permission.USE_BIOMETRIC
            ) == PackageManager.PERMISSION_GRANTED
        ) {
            promptInfo?.let{ biometricPrompt?.authenticate(it)}
        } else {
            showToast("Please grant the AppName app the necessary permissions to use fingerprint hardware.")
        }
    }
}
}

prefSaver 和 prefChecker 基本上就是这个类。这些名称用于描述目的。该类是这样实现的:

package com.domainName.appName.utils

import android.annotation.SuppressLint
import android.content.Context;
import android.content.SharedPreferences;

class PrefCheckerOrSaver(private val context:Context?){
fun <T>check(sFile: String,keyS: String,defaultS: T):T{
    if(context==null)return defaultS
    val sPref:SharedPreferences= context.getSharedPreferences(sFile,Context.MODE_PRIVATE)
    return when(defaultS){
        is String->sPref.getString(keyS,defaultS) as T
        is Int-> sPref.getInt(keyS,defaultS) as T
        is Long-> sPref.getLong(keyS,defaultS) as T
        is Boolean-> sPref.getBoolean(keyS,defaultS) as T
        else-> defaultS
    }
}
@SuppressLint("ApplySharedPref")
fun save(sFile:String, sKey:String, sValue:Any, saveImmediately:Boolean=false){
    if(context==null)return
    val sharedPreferences:SharedPreferences = context.getSharedPreferences(sFile, Context.MODE_PRIVATE);
    val editor:SharedPreferences.Editor = sharedPreferences.edit();
    when(sValue) {
        is String-> editor.putString(sKey, sValue)
        is Int->editor.putInt(sKey, sValue)
        is Long-> editor.putLong(sKey, sValue)
        is Boolean->editor.putBoolean(sKey, sValue)
    }
    if(saveImmediately){editor.commit()}
    else{ editor.apply()}
}
}

应用程序清单中的权限

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android" xmlns:tools="http://schemas.android.com/tools"
package="com.domainName.appName">

<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.USE_BIOMETRIC" />

<application
    android:name=".MainApplication"
    android:allowBackup="false"
    android:icon="${appIcon}"
    android:label="@string/app_name"
    android:roundIcon="${appIconRound}"
    android:resizeableActivity="true"
    android:supportsRtl="false"
    android:theme="@style/ThemeAppMain"
    tools:targetApi="n"
    >

    <activity
        android:name=".ui.activities.InitialActivity"
        android:label="@string/app_name"
        android:exported="true"
        android:noHistory="true"
        >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />
            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
<activity android:name=".ui.activities.BaseActivity" />
<activity android:name=".ui.activities.MainActivity" android:launchMode="singleTask"/>

<activity android:name=".ui.activities.SettingsActivity" />

<activity android:name=".ui.activities.FingerprintActivity"
android:launchMode="singleTask"
android:noHistory="true"
/>
</application>

</manifest>
© www.soinside.com 2019 - 2024. All rights reserved.