946880ecf4
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
178 lines
5.2 KiB
ObjectPascal
178 lines
5.2 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;
|
|
|
|
/// Deplie le clavier pour le champ qui a le focus (la recherche). Un focus()
|
|
/// JavaScript seul pose le curseur sans ouvrir le clavier.
|
|
procedure ShowSoftKeyboard;
|
|
|
|
/// Renvoie l'application en arriere-plan : on retombe sur l'app d'ou l'on
|
|
/// venait, prete a recevoir le collage.
|
|
procedure MoveAppToBack;
|
|
|
|
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}
|
|
|
|
{$IF DEFINED(ANDROID)}
|
|
procedure ShowSoftKeyboard;
|
|
var
|
|
LService: JObject;
|
|
LView: JView;
|
|
begin
|
|
try
|
|
LView := TAndroidHelper.Activity.getWindow.getDecorView.findFocus;
|
|
if LView = nil then Exit;
|
|
LService := TAndroidHelper.Context.getSystemService(
|
|
TJContext.JavaClass.INPUT_METHOD_SERVICE);
|
|
if LService = nil then Exit;
|
|
TJInputMethodManager.Wrap(LService).showSoftInput(LView, 0);
|
|
except
|
|
// Pas de clavier : la recherche reste a un appui.
|
|
end;
|
|
end;
|
|
|
|
procedure MoveAppToBack;
|
|
begin
|
|
CallInUIThread(
|
|
procedure
|
|
begin
|
|
try
|
|
TAndroidHelper.Activity.moveTaskToBack(True);
|
|
except
|
|
// Reste au premier plan : on colle apres un retour manuel.
|
|
end;
|
|
end);
|
|
end;
|
|
{$ELSE}
|
|
procedure ShowSoftKeyboard;
|
|
begin
|
|
end;
|
|
|
|
procedure MoveAppToBack;
|
|
begin
|
|
end;
|
|
{$ENDIF}
|
|
|
|
end.
|