Files
Password-Manager/android-client/PMA.ScreenGuard.pas
T
r-zakarya 8f8a8ec3f4 fix(android): the back key folds the keyboard before anything else
It was our own handler. The web view is not a field the system knows, so
it does not intercept the key for us - it arrives here, and we consumed
it in every case: the vault locked, or the list came back, while the
keyboard stayed up with no way left to close it.

hideSoftInputFromWindow answers whether there was anything to fold, which
is the synchronous signal needed to decide. Something folded, key eaten;
nothing folded, the key keeps its usual meaning.

Measured on the device, before and after: mInputShown stayed true, it now
goes false and the application stays in front. A second press locks, as
before.
2026-08-21 12:48:29 +01:00

130 lines
4.1 KiB
ObjectPascal

unit PMA.ScreenGuard;
{
FLAG_SECURE : interdit la capture d'ecran et l'apparition du contenu dans
l'apercu des applications recentes.
COMPLEMENTAIRE, pas redondant, avec le masquage sur WillBecomeInactive :
celui-ci traite l'apercu des recentes, celui-la traite en plus les captures
manuelles, l'enregistrement d'ecran et la duplication vers un ecran externe -
qu'aucun code applicatif ne peut intercepter.
ACTIF EN RELEASE SEULEMENT. En debug le drapeau empeche aussi les captures
d'ecran de developpement, ce qui rend le diagnostic penible pour un gain nul
sur un appareil de test. Le verifier demande donc une compilation Release.
}
interface
uses
System.UITypes;
/// A appeler une fois, au demarrage, depuis le thread principal.
procedure SecureScreen;
/// Teinte les barres systeme - etat en haut, navigation en bas.
///
/// Sans cela l'application flotte dans un cadre qui ne lui appartient pas :
/// barre d'etat grise, barre de navigation BLANCHE sous un fond bleu. La
/// reference les fait baigner dans le meme degrade, et c'est ce qui donne
/// l'impression que l'application occupe l'ecran plutot que d'y etre posee.
procedure TintSystemBars(ATop, ABottom: TAlphaColor);
/// Replie le clavier, et dit s'il etait bien deplie.
///
/// LA REPONSE COMPTE AUTANT QUE L'ACTION. La touche retour arrive chez nous
/// avant le clavier - la vue web n'est pas un champ que le systeme connait -
/// et notre gestionnaire la consommait toujours, verrouillant le coffre ou
/// remontant a la liste pendant que le clavier restait deplie, sans plus aucun
/// moyen de le refermer. Savoir s'il y avait quelque chose a replier permet de
/// rendre a la touche son premier sens et de ne consommer que dans ce cas.
///
/// A appeler depuis le thread d'interface, et sans differer : celui qui appelle
/// decide de la suite d'apres la reponse.
function HideSoftKeyboard: Boolean;
implementation
{$IF DEFINED(ANDROID)}
uses
// System.SysUtils : sans elle, TAndroidHelper.GetJActivity reste un appel
// non expanse et le compilateur le signale a chaque usage.
System.SysUtils, Androidapi.JNI.JavaTypes, Androidapi.Helpers, Androidapi.JNI.App,
Androidapi.JNI.GraphicsContentViewText, FMX.Helpers.Android;
procedure SecureScreen;
begin
// La condition est ICI, dans le corps, et non sur la clause uses : celle-ci
// sert aussi a TintSystemBars, qui doit fonctionner en debug.
{$IFNDEF DEBUG}
// setFlags touche la fenetre : thread d'interface obligatoire.
CallInUIThread(
procedure
begin
TAndroidHelper.Activity.getWindow.setFlags(
TJWindowManager_LayoutParams.JavaClass.FLAG_SECURE,
TJWindowManager_LayoutParams.JavaClass.FLAG_SECURE);
end);
{$ENDIF}
end;
{$ELSE}
procedure SecureScreen;
begin
// Rien a faire : ce n'est pas Android.
end;
{$ENDIF}
{$IF DEFINED(ANDROID)}
procedure TintSystemBars(ATop, ABottom: TAlphaColor);
begin
CallInUIThread(
procedure
var
LWindow: JWindow;
begin
try
LWindow := TAndroidHelper.Activity.getWindow;
// Les deux couleurs different : le haut prend le depart du degrade,
// le bas son arrivee, pour que la continuite soit exacte.
LWindow.setStatusBarColor(Integer(ATop));
LWindow.setNavigationBarColor(Integer(ABottom));
except
// Une barre non teintee n'empeche rien de fonctionner.
end;
end);
end;
{$ELSE}
procedure TintSystemBars(ATop, ABottom: TAlphaColor);
begin
end;
{$ENDIF}
{$IF DEFINED(ANDROID)}
function HideSoftKeyboard: Boolean;
var
LService: JObject;
LView: JView;
begin
Result := False;
try
LView := TAndroidHelper.Activity.getWindow.getDecorView;
if LView = nil then Exit;
LService := TAndroidHelper.Context.getSystemService(
TJContext.JavaClass.INPUT_METHOD_SERVICE);
if LService = nil then Exit;
// Rend False si rien n'etait deplie : c'est notre reponse.
Result := TJInputMethodManager.Wrap(LService)
.hideSoftInputFromWindow(LView.getWindowToken, 0);
except
// Un clavier qui ne se replie pas ne doit pas emporter la touche retour.
Result := False;
end;
end;
{$ELSE}
function HideSoftKeyboard: Boolean;
begin
Result := False;
end;
{$ENDIF}
end.