d06410dd7e
The white "copied" chip appearing long after the gesture was Android's, and it was ours to stop causing: the 45 s auto-clear wrote an empty string through setPrimaryClip, which the system reads as a copy and announces like one. The comment claimed the visible result was the same. It is not, and the API has distinguished emptying from copying since 2018 - clearPrimaryClip, kept behind an API 28 check because minSdk is 23. It only showed every other time because the timer clears only if the clipboard still holds our text; copying anything else in between cancels it. Nothing to fix on the sensitive flag: it is set, the chip shows no content, and that generic wording is exactly what the flag produces.
159 lines
5.0 KiB
ObjectPascal
159 lines
5.0 KiB
ObjectPascal
unit PMA.Clipboard;
|
|
{
|
|
Copie dans le presse-papiers, en marquant le contenu SENSIBLE.
|
|
|
|
POURQUOI PAS IFMXClipboardService : depuis Android 13, le systeme affiche un
|
|
apercu de ce qui vient d'etre copie, en bas de l'ecran. Pour un mot de passe,
|
|
c'est exactement ce qu'on cherche a eviter - il s'affiche en clair, hors de
|
|
l'application, et reste visible quelques secondes.
|
|
|
|
Le drapeau qui supprime cet apercu est un extra du ClipDescription, que le
|
|
service FMX n'expose pas. D'ou ce passage direct par ClipboardManager.
|
|
|
|
Cela ne remplace pas l'effacement differe : le drapeau cache l'apercu, il ne
|
|
vide pas le presse-papiers.
|
|
}
|
|
|
|
interface
|
|
|
|
/// Copie ATexte. Retourne False si la plateforme n'a rien fourni - l'appelant
|
|
/// peut alors retomber sur le service FMX.
|
|
function CopySensitive(const AText: string): Boolean;
|
|
|
|
/// Contenu courant, ou chaine vide. Sert a ne vider le presse-papiers que
|
|
/// s'il contient toujours ce qu'on y a mis.
|
|
function CurrentClipboard: string;
|
|
|
|
/// Vide le presse-papiers.
|
|
procedure ClearClipboard;
|
|
|
|
implementation
|
|
|
|
{$IF DEFINED(ANDROID)}
|
|
uses
|
|
System.SysUtils, Androidapi.Helpers, Androidapi.JNI.JavaTypes,
|
|
Androidapi.JNI.GraphicsContentViewText, Androidapi.JNI.Os, Androidapi.JNIBridge;
|
|
|
|
const
|
|
// android.content.ClipDescription.EXTRA_IS_SENSITIVE, ecrit en dur : la
|
|
// constante n'existe qu'a partir de l'API 33 dans les en-tetes, alors que la
|
|
// valeur est reconnue par le systeme des qu'il sait la lire, et ignoree
|
|
// ailleurs. Un extra inconnu ne derange personne.
|
|
EXTRA_IS_SENSITIVE = 'android.content.extra.IS_SENSITIVE';
|
|
|
|
/// Jcontent_ClipboardManager et NON JClipboardManager : ce dernier est
|
|
/// l'android.text.ClipboardManager deprecie, qui ne connait que du texte nu et
|
|
/// aucun ClipData - donc aucun extra ou poser le drapeau.
|
|
function Manager: Jcontent_ClipboardManager;
|
|
var
|
|
LObj: JObject;
|
|
begin
|
|
LObj := TAndroidHelper.Context.getSystemService(
|
|
TJContext.JavaClass.CLIPBOARD_SERVICE);
|
|
if LObj = nil then Exit(nil);
|
|
Result := TJcontent_ClipboardManager.Wrap((LObj as ILocalObject).GetObjectID);
|
|
end;
|
|
|
|
function CopySensitive(const AText: string): Boolean;
|
|
var
|
|
LMgr: Jcontent_ClipboardManager;
|
|
LClip: JClipData;
|
|
LExtras: JPersistableBundle;
|
|
begin
|
|
Result := False;
|
|
try
|
|
LMgr := Manager;
|
|
if LMgr = nil then Exit;
|
|
// L'etiquette est visible du systeme : elle ne doit rien decrire du
|
|
// contenu, seulement son origine.
|
|
// Les DEUX arguments sont des CharSequence : passer un JString au premier
|
|
// compile ailleurs mais pas ici.
|
|
LClip := TJClipData.JavaClass.newPlainText(StrToJCharSequence('vault'),
|
|
StrToJCharSequence(AText));
|
|
LExtras := TJPersistableBundle.JavaClass.init;
|
|
LExtras.putBoolean(StringToJString(EXTRA_IS_SENSITIVE), True);
|
|
LClip.getDescription.setExtras(LExtras);
|
|
LMgr.setPrimaryClip(LClip);
|
|
Result := True;
|
|
except
|
|
Result := False; // l'appelant retombera sur le service FMX
|
|
end;
|
|
end;
|
|
|
|
function CurrentClipboard: string;
|
|
var
|
|
LMgr: Jcontent_ClipboardManager;
|
|
LClip: JClipData;
|
|
begin
|
|
Result := '';
|
|
try
|
|
LMgr := Manager;
|
|
if (LMgr = nil) or not LMgr.hasPrimaryClip then Exit;
|
|
LClip := LMgr.getPrimaryClip;
|
|
if (LClip = nil) or (LClip.getItemCount < 1) then Exit;
|
|
Result := JCharSequenceToStr(LClip.getItemAt(0).getText);
|
|
except
|
|
Result := '';
|
|
end;
|
|
end;
|
|
|
|
procedure ClearClipboard;
|
|
var
|
|
LMgr: Jcontent_ClipboardManager;
|
|
begin
|
|
// clearPrimaryClip QUAND IL EXISTE, et un contenu vide seulement en dessous
|
|
// de l'API 28.
|
|
//
|
|
// Le resultat n'est PAS le meme, contrairement a ce qui etait ecrit ici :
|
|
// ecrire un contenu vide reste une COPIE aux yeux du systeme, et Android
|
|
// affiche donc sa vignette "copie" - quarante-cinq secondes apres le geste
|
|
// de l'utilisateur, sans que rien ne l'explique. Vider n'est pas copier, et
|
|
// l'API le distingue depuis 2018.
|
|
//
|
|
// Elle ne se montre pas a chaque fois : le minuteur ne vide que si le
|
|
// presse-papiers contient TOUJOURS notre texte, donc copier autre chose
|
|
// entre-temps la supprime. D'ou une apparition sur deux.
|
|
try
|
|
LMgr := Manager;
|
|
if LMgr = nil then Exit;
|
|
if TJBuild_VERSION.JavaClass.SDK_INT >= 28 then
|
|
LMgr.clearPrimaryClip
|
|
else
|
|
CopySensitive('');
|
|
except
|
|
// Un presse-papiers qu'on n'a pas pu vider vaut mieux qu'une exception :
|
|
// le contenu partira au prochain copier de l'utilisateur.
|
|
end;
|
|
end;
|
|
{$ELSE}
|
|
uses
|
|
System.SysUtils, System.Rtti, FMX.Platform;
|
|
|
|
function CopySensitive(const AText: string): Boolean;
|
|
var
|
|
LClip: IFMXClipboardService;
|
|
begin
|
|
// Hors Android il n'y a pas d'apercu systeme a supprimer : le service
|
|
// standard suffit.
|
|
Result := TPlatformServices.Current.SupportsPlatformService(
|
|
IFMXClipboardService, LClip);
|
|
if Result then LClip.SetClipboard(AText);
|
|
end;
|
|
|
|
function CurrentClipboard: string;
|
|
var
|
|
LClip: IFMXClipboardService;
|
|
begin
|
|
Result := '';
|
|
if TPlatformServices.Current.SupportsPlatformService(IFMXClipboardService, LClip) then
|
|
Result := LClip.GetClipboard.ToString;
|
|
end;
|
|
|
|
procedure ClearClipboard;
|
|
begin
|
|
CopySensitive('');
|
|
end;
|
|
{$ENDIF}
|
|
|
|
end.
|