feat(sync): QR des reglages sur le PC, Paste settings sur le telephone

Le poste genere le QR (DelphiZXingQRCode, Apache 2.0) depuis ses prefs :
URL + jeton, jamais le mot de passe de synchro. Le telephone lit le texte
copie par l'appareil photo et remplit les champs ; Save valide.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
r-zakarya
2026-09-17 18:03:00 +01:00
parent 05ff08886d
commit f817a6df43
10 changed files with 3913 additions and 1 deletions
+47 -1
View File
@@ -35,7 +35,7 @@ uses
FMX.TMSFNCEdgeWebBrowser,
{$ENDIF}
PM.HTTPServer, PM.Bridge, PM.QuickUnlock, PM.PinUnlock, PM.UserPrefs, PM.Licence, PM.AutoStart,
PM.Favicon,
PM.Favicon, DelphiZXingQRCode,
FMX.Platform.Win, FMX.Menus; // WindowHandleToPlatform → HWND for visibility check
const
// Bump on each release. Surfaced to JS via cmd://app/version, displayed
@@ -1330,6 +1330,52 @@ begin
PM.UserPrefs.SetPref(LKey, GetParam('value'));
end
// ---- QR des reglages de synchro, pour le telephone ------------------
// L'EXE FABRIQUE LE QR LUI-MEME : l'URL et le jeton se lisent ici, dans les
// prefs DPAPI, et seule la matrice noir/blanc part vers la page - le jeton
// ne transite pas en clair par le moteur de rendu (meme raison que
// prefs/has). Le mot de passe de synchro n'y est JAMAIS : un QR se
// photographie par-dessus l'epaule.
// JSON nu, sans prefixe "pmsync:" : un prefixe a deux-points se lit comme un
// lien, et l'appareil photo proposait "Ouvrir avec" au lieu de copier.
else if ACmd = 'sync/qr' then
begin
var LQrUrl := PM.UserPrefs.GetPref('syncUrl');
var LQrTok := PM.UserPrefs.GetPref('syncPwd');
var LBits := '';
var LSize := 0;
if (LQrUrl <> '') and (LQrTok <> '') then
begin
var LJson := TJSONObject.Create;
var LQr := TDelphiZXingQRCode.Create;
var LSb := TStringBuilder.Create;
try
LJson.AddPair('pmsync', TJSONNumber.Create(1));
LJson.AddPair('u', LQrUrl);
LJson.AddPair('t', LQrTok);
LQr.Encoding := qrUTF8NoBOM;
LQr.QuietZone := 4;
LQr.Data := LJson.ToJSON;
LSize := LQr.Rows;
for var R := 0 to LQr.Rows - 1 do
for var C := 0 to LQr.Columns - 1 do
if LQr.IsBlack[R, C] then LSb.Append('1') else LSb.Append('0');
LBits := LSb.ToString;
finally
LSb.Free;
LQr.Free;
LJson.Free;
end;
end;
if LSize > 0 then
LogLine('sync/qr: ' + IntToStr(LSize) + ' modules')
else
LogLine('sync/qr: URL or token not set');
WebBrowser.ExecuteJavaScript(
'if(window.Bridge&&Bridge.onSyncQr)Bridge.onSyncQr(' + IntToStr(LSize) +
',"' + LBits + '")');
end
// ---- Licence hors ligne (PM.Licence) ---------------------------------
// cmd://licence/status -> Bridge.onLicenceResult(ok, porteur)
// cmd://licence/activate?key=<...> -> idem, apres verification + rangement