Android: Comment définir le code d'accès / PIN pour mon application uniquement?
Je travaille sur une application dans laquelle je dois définir la fonction de sécurité en définissant le code d'accès / mot de passe / code PIN pour l'utilisateur afin que chaque fois qu'il ouvre l'application en arrière-plan, l'application demande un mot de passe / code secret / code PIN. J'ai lu quelques articles / solutions comme celui-ci mais ils ne m'ont pas aidé. La même fonctionnalité que nous avons trouvée dans les applications bancaires normales dans lesquelles chaque fois que vous ouvrez une application, ils vous demanderont un code d'accès / une empreinte digitale.
J'ai déjà configuré la logique pour enregistrer le code / la broche dans les préférences partagées, mais je ne sais pas quand le demander. Je sais que nous ne pouvons pas remplacer l'écran de démarrage par l'activité du code / code PIN, car parfois à partir de l'écran d'accueil / MainActivity de l'application, l'utilisateur appuie sur le bouton d'accueil et lorsqu'il ouvre à nouveau l'application à partir d'applications récentes, l'application doit demander un code / code PIN pour reprendre utilisation de l'application.
Toute aide serait appréciée.
Réponses
C'est une question intéressante, je vais partager ma réflexion sur cette question et donner une solution également.
Terminologie:
Type de verrouillage d'application: un nom générique pour pin / code PIN / mot de passe / code d'accès, etc. (dans la section suivante, j'utiliserai le nom de la broche pour démontrer)
PinActivity: un écran où les utilisateurs saisissent leur code PIN pour se vérifier
Récit:
Pour les applications qui exigent que les utilisateurs saisissent le code PIN, ils veulent généralement s'assurer que les informations sensibles ne sont pas divulguées / volées par d'autres personnes. Nous classerons donc les activités de l'application en 2 groupes.
Activités normales: ne contient aucune information sensible, généralement avant que les utilisateurs ne se connectent à l'application, telles que SplashActivity, LoginActivity, RegistrationActivity, PinActivity, etc.
Activités sécurisées: contiennent des informations sensibles, généralement après la connexion des utilisateurs, telles que MainActivity, HomeActivity, UserInfoActivity, etc.
Conditions:
Pour les activités sécurisées, nous devons nous assurer que les utilisateurs saisissent toujours leur code PIN avant de visualiser le contenu en affichant le PinActivity. Cette activité sera présentée dans les scénarios suivants:
[1] Lorsque les utilisateurs ouvrent une activité sécurisée depuis une activité normale, telle que de SplashActivity à MainActivity
[2] Lorsque les utilisateurs ouvrent une activité sécurisée en appuyant sur Notifications, comme ils tapent sur une notification pour ouvrir MainActivity
[3] Lorsque les utilisateurs tapent sur l'application à partir de l'écran Récents
[4] Lorsque l'application démarre une activité sécurisée à partir d'un autre endroit comme les services, le récepteur de diffusion, etc.
La mise en oeuvre:
Pour les cas [1] [2] et [4], avant de démarrer une activité sécurisée, nous ajouterons un supplément à l'intention d'origine. Je vais créer un fichier nommé IntentUtils.kt
IntentUtils.kt
const val EXTRA_IS_PIN_REQUIRED = "EXTRA_IS_PIN_REQUIRED"
fun Intent.secured(): Intent {
return this.apply {
putExtra(EXTRA_IS_PIN_REQUIRED, true)
}
}
Utilisez cette classe à partir d'activités normales, de notifications, de services, etc.
startActivity(Intent(this, MainActivity::class.java).secured())
Pour le cas [3], j'utiliserai 2 API:
ProcessLifecycleOwner : pour détecter si l'application passe en arrière-plan. Un scénario typique est lorsque les utilisateurs cliquent sur la touche Accueil / Menu sur leurs appareils.
ActivityLifecycleCallbacks : pour détecter si une activité est reprise en s'appuyant sur la méthode onActivityResumed (activité) .
Je crée d'abord une activité de base, toute activité normale doit s'étendre à partir de cette classe
BaseActivity.kt
open class BaseActivity : AppCompatActivity() {
// This method indicates that a pin is required if
// users want to see the content inside.
open fun isPinRequired() = false
}
Deuxièmement, je crée une activité sécurisée, toutes les activités sécurisées doivent s'étendre de cette classe
SecuredActivity.kt
open class SecuredActivity : BaseActivity() {
override fun isPinRequired() = true
// This is useful when launch a secured activity with
// singleTop, singleTask, singleInstance launch mode
override fun onNewIntent(intent: Intent?) {
super.onNewIntent(intent)
setIntent(intent)
}
}
Troisièmement, je crée une classe qui s'étend de l'application, toute la logique est à l'intérieur de cette classe
MyApplication.kt
class MyApplication : Application() {
private var wasEnterBackground = false
override fun onCreate() {
super.onCreate()
registerActivityLifecycleCallbacks(ActivityLifecycleCallbacksImpl())
ProcessLifecycleOwner.get().lifecycle.addObserver(LifecycleObserverImpl())
}
private fun showPinActivity() {
startActivity(Intent(this, PinActivity::class.java))
}
inner class LifecycleObserverImpl : LifecycleObserver {
@OnLifecycleEvent(Lifecycle.Event.ON_STOP)
fun onEnterBackground() {
wasEnterBackground = true
}
}
inner class ActivityLifecycleCallbacksImpl : ActivityLifecycleCallbacks {
override fun onActivityResumed(activity: Activity) {
val baseActivity = activity as BaseActivity
if (!wasEnterBackground) {
// Handle case [1] [2] and [4]
val removed = removeIsPinRequiredKeyFromActivity(activity)
if (removed) {
showPinActivity()
}
} else {
// Handle case [3]
wasEnterBackground = false
if (baseActivity.isPinRequired()) {
removeIsPinRequiredKeyFromActivity(activity)
showPinActivity()
}
}
}
private fun removeIsPinRequiredKeyFromActivity(activity: Activity): Boolean {
val key = EXTRA_IS_PIN_REQUIRED
if (activity.intent.hasExtra(key)) {
activity.intent.removeExtra(key)
return true
}
return false
}
override fun onActivityCreated(activity: Activity, savedInstanceState: Bundle?) {}
override fun onActivityStarted(activity: Activity) {}
override fun onActivityPaused(activity: Activity) {}
override fun onActivitySaveInstanceState(activity: Activity, outState: Bundle) {}
override fun onActivityStopped(activity: Activity) {}
override fun onActivityDestroyed(activity: Activity) {}
}
}
Conclusion:
Cette solution fonctionne pour les cas que j'ai mentionnés précédemment, mais je n'ai pas testé les scénarios suivants:
- Lorsque le démarrage d'une activité sécurisée a le mode de lancement singleTop | singleTask | singleInstance
- Lorsque l'application est tuée par le système avec une mémoire insuffisante
- D'autres scénarios que quelqu'un pourrait rencontrer (si oui, merci de me le faire savoir dans la section commentaires).