diff --git a/mainApk.pas b/mainApk.pas index b0631f1..b802f89 100644 --- a/mainApk.pas +++ b/mainApk.pas @@ -56,6 +56,11 @@ const // allume, application ouverte. AUTOLOCK_MINUTES = 3; + // Sursis avant de jeter le clair quand l'application passe derriere. Assez + // pour aller coller un mot de passe ailleurs et revenir ; trop court pour + // qu'un telephone repose ouvert. + BACKGROUND_GRACE_SECONDS = 30; + // FIRSTRUN_TOKEN et TEST_PWD viennent d'un fichier LOCAL, hors depot : le // jeton authentifie l'appareil, le mot de passe ouvre le coffre entier. Ni // l'un ni l'autre n'a sa place dans un depot, meme sans remote. Absents apres @@ -136,6 +141,10 @@ type // --- verrouillage sur inactivite FIdleTimer: TTimer; FLastActivity: TDateTime; + /// Quand l'application est passee derriere, et quel ecran elle a quitte. + /// 0 = elle est au premier plan. + FBackgroundAt: TDateTime; + FHiddenOnDetail: Boolean; procedure Touch; procedure OnIdleTimer(Sender: TObject); @@ -930,10 +939,16 @@ begin end else begin - Log('No protection left on this device - cache cleared'); + // ON N'EFFACE PLUS, MEME ICI. C'etait le dernier endroit ou + // l'application detruisait le cache, sur la foi d'un verdict qui + // s'est deja revele fragile aujourd'hui. Le garder ne coute que + // quelques centaines de kilo-octets, et un telechargement le + // remplace de toute facon. Desormais RIEN ne detruit le cache : + // seule une ecriture reussie le remplace. + Log('No usable protection on this device - vault kept but unreadable'); Log(' ' + LastBiometricError); - ClearVault; ShowUnlock; + Status('Screen lock missing - download again to replace the cache'); end; else // boFailed : la couche Java a leve quelque chose. Cela ne dit rien de @@ -1462,16 +1477,42 @@ begin case AAppEvent of // WillBecomeInactive arrive AVANT la capture d'ecran des applications // recentes : masquer ici, c'est ne pas y laisser la liste des comptes. + // Le masquage est IMMEDIAT et sans condition - c'est lui qui protege. TApplicationEvent.WillBecomeInactive: if FLayList.Visible or FLayDetail.Visible then begin + FHiddenOnDetail := FLayDetail.Visible; FLayDetail.Visible := False; FLayList.Visible := False; FLayUnlock.Visible := True; end; - // Et le clair est reellement jete quand l'application passe derriere. + + // Le clair, lui, survit a un bref passage en arriere-plan. + // + // Verrouiller instantanement rendait le geste le plus courant penible : + // copier un mot de passe, basculer pour le coller, revenir - et devoir se + // reauthentifier, avec le code 2FA de nouveau derriere une invite. L'ecran + // etant deja masque, garder le clair quelques secondes de plus n'expose + // rien de visible ; c'est le temps d'un aller-retour, pas d'un oubli. TApplicationEvent.EnteredBackground: - if FVault.Count > 0 then LockVault; + FBackgroundAt := Now; + + TApplicationEvent.BecameActive: + if FVault.Count > 0 then + begin + if (FBackgroundAt > 0) and + (SecondsBetween(Now, FBackgroundAt) >= BACKGROUND_GRACE_SECONDS) then + LockVault + else + begin + // Retour a temps : on rend l'ecran quitte, pas un autre. + FLayUnlock.Visible := False; + FLayDetail.Visible := FHiddenOnDetail; + FLayList.Visible := not FHiddenOnDetail; + Touch; + end; + FBackgroundAt := 0; + end; end; end;