Android: Jak ustawić hasło / kod PIN tylko do mojej aplikacji?
Pracuję nad aplikacją, w której muszę ustawić funkcję zabezpieczeń, ustawiając hasło / hasło / kod PIN dla użytkownika, aby za każdym razem, gdy otworzy aplikację w tle, aplikacja prosi o hasło / hasło / kod PIN. Czytałem kilka artykułów / rozwiązania, takie jak to , ale nie może mi pomóc. Ta sama funkcjonalność, którą znaleźliśmy w zwykłych aplikacjach bankowych, w których za każdym razem, gdy otwierasz aplikację, proszą o hasło / odcisk palca.
Skonfigurowałem już logikę, aby zapisać hasło / kod PIN we wspólnych preferencjach, ale nie jestem pewien, kiedy o to zapytać. Wiem, że nie możemy zastąpić ekranu powitalnego aktywnością kodu dostępu / kodu PIN, ponieważ czasami z poziomu ekranu głównego aplikacji / MainActivity użytkownik naciska przycisk home, a gdy ponownie otworzy aplikację z ostatnich aplikacji, aplikacja powinna poprosić o hasło / pin, aby wznowić użycie aplikacji.
Każda pomoc będzie mile widziana.
Odpowiedzi
To interesujące pytanie, podzielę się przemyśleniami na ten temat i podam rozwiązanie.
Terminologia:
Typ blokady aplikacji: ogólna nazwa kodu PIN / kodu PIN / hasła / kodu dostępu itp. (W poniższej sekcji użyję nazwy pin do zademonstrowania)
PinActivity: ekran, na którym użytkownicy wprowadzają swój kod PIN, aby się zweryfikować
Fabuła:
W przypadku aplikacji, które wymagają od użytkowników wprowadzenia kodu PIN, zwykle chcą mieć pewność, że poufne informacje nie zostaną ujawnione / skradzione przez inne osoby. Więc podzielimy działania aplikacji na 2 grupy.
Normalne działania: nie zawiera żadnych poufnych informacji, zwykle przed zalogowaniem się użytkowników do aplikacji, takich jak SplashActivity, LoginActivity, RegistrationActivity, PinActivity itp.
Działania zabezpieczone: zawierają poufne informacje, zwykle po zalogowaniu się użytkowników, takie jak MainActivity, HomeActivity, UserInfoActivity itp.
Warunki:
W przypadku działań zabezpieczonych musimy upewnić się, że użytkownicy zawsze wprowadzają swój kod PIN przed wyświetleniem zawartości, pokazując PinActivity. Ta aktywność zostanie pokazana w następujących scenariuszach:
[1] Gdy użytkownicy otwierają zabezpieczone działanie, tworzą normalne działanie, takie jak od SplashActivity do MainActivity
[2] Gdy użytkownicy otwierają zabezpieczoną aktywność, dotykając Powiadomień, na przykład gdy dotykają powiadomienia, aby otworzyć MainActivity
[3] Gdy użytkownicy dotykają aplikacji na ekranie Ostatnie
[4] Gdy aplikacja rozpoczyna zabezpieczoną aktywność z innego miejsca, takiego jak usługi, odbiornik transmisji itp.
Realizacja:
W przypadku [1] [2] i [4], przed rozpoczęciem zabezpieczonej czynności dodamy dodatkowy do pierwotnego celu. Utworzę plik o nazwie 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)
}
}
Używaj tej klasy z normalnych czynności, powiadomień, usług itp.
startActivity(Intent(this, MainActivity::class.java).secured())
W przypadku [3] użyję 2 interfejsów API:
ProcessLifecycleOwner : aby wykryć, czy aplikacja działa w tle. Typowy scenariusz to kliknięcie przez użytkownika przycisku Home / Menu na swoich urządzeniach.
ActivityLifecycleCallbacks : Aby wykryć, czy działanie zostało wznowione, opierając się na metodzie onActivityResumed (activity) .
Najpierw tworzę aktywność podstawową, wszystkie normalne zapalenia muszą wykraczać poza tę klasę
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
}
Po drugie, tworzę zabezpieczone działanie, wszystkie zabezpieczone działania muszą pochodzić z tej klasy
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)
}
}
Po trzecie, tworzę klasę, która dziedziczy z aplikacji, cała logika znajduje się wewnątrz tej klasy
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) {}
}
}
Wniosek:
To rozwiązanie działa w przypadkach, o których wspomniałem wcześniej, ale nie testowałem następujących scenariuszy:
- Podczas uruchamiania zabezpieczone działanie ma tryb uruchamiania singleTop | singleTask | singleInstance
- Gdy aplikacja zostaje zabita przez system przy małej ilości pamięci
- Inne scenariusze, które ktoś może napotkać (jeśli tak, daj mi znać w sekcji komentarzy).