feat(android): paint the interface instead of wearing a style
None of the styles Delphi ships is meant for a phone - grey gradients, sharp corners, thick frames, all from the desktop era. And the day showed that anything rendered through a style template may not exist in the style that is actually loaded: the list row template, the search prompt and the detail row each vanished in turn. So the buttons are rectangles we draw: slate at rest, cyan for the one action that matters, rounded, no gradient, with a press feedback a TRectangle does not have on its own - without it you cannot tell whether the tap landed. The form's own fill carries the background, the list drops its frame, labels take the palette. A style stays loaded underneath, as a base only: edits, list and memo are native controls we are not going to rewrite, and with no style at all they would render light. Palette lives in PMA.Theme, the same slate and cyan as the desktop app. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
+52
-53
@@ -1,4 +1,4 @@
|
||||
unit mainApk;
|
||||
unit mainApk;
|
||||
{
|
||||
Client Android en LECTURE SEULE : deverrouiller, chercher, copier.
|
||||
|
||||
@@ -90,7 +90,7 @@ type
|
||||
// --- ecran 1 : deverrouillage
|
||||
FLayUnlock: TLayout;
|
||||
FUrl, FToken, FPwd: TEdit;
|
||||
FBtnOpen, FBtnSync, FBtnSettings: TButton;
|
||||
FBtnOpen, FBtnSync, FBtnSettings: TRectangle;
|
||||
FLaySettings: TLayout;
|
||||
FStatus: TLabel;
|
||||
FLog: TMemo;
|
||||
@@ -114,6 +114,7 @@ type
|
||||
FLayDetail: TLayout;
|
||||
FDetBody: TVertScrollBox;
|
||||
FDetTitle, FDetToast: TLabel;
|
||||
FBtnReveal: TRectangle;
|
||||
FRows: TArray<TDetRow>;
|
||||
FRowCtls: TArray<TLayout>;
|
||||
FRowValues: TArray<TLabel>;
|
||||
@@ -189,7 +190,7 @@ implementation
|
||||
|
||||
uses
|
||||
PMA.Crypto, PMA.SyncClient, PMA.Store, PMA.Biometric, PMA.Totp,
|
||||
PMA.ScreenGuard, PMA.Clipboard;
|
||||
PMA.ScreenGuard, PMA.Clipboard, PMA.Theme;
|
||||
|
||||
{$R *.fmx}
|
||||
|
||||
@@ -210,6 +211,10 @@ var
|
||||
begin
|
||||
inherited;
|
||||
Caption := 'Coffre';
|
||||
// Le fond de la forme, et non celui du style : tout le reste est peint
|
||||
// par-dessus, et un gris herite se verrait entre les blocs.
|
||||
Fill.Kind := TBrushKind.Solid;
|
||||
Fill.Color := COL_BG;
|
||||
SecureScreen; // avant tout affichage de contenu
|
||||
FVault := TVault.Create;
|
||||
FIcons := TObjectDictionary<Integer, TBitmap>.Create([doOwnsValues]);
|
||||
@@ -263,15 +268,12 @@ end;
|
||||
/// s'empilent A REBOURS de leur creation : le dernier cree se retrouve tout en
|
||||
/// haut. Chaque Build* cree donc du bas vers le haut.
|
||||
function TopButton(AParent: TFmxObject; const AText: string;
|
||||
AOnClick: TNotifyEvent; AHeight: Single = 48): TButton;
|
||||
AOnClick: TNotifyEvent; AHeight: Single = 52; APrimary: Boolean = False): TRectangle;
|
||||
begin
|
||||
Result := TButton.Create(AParent);
|
||||
Result.Parent := AParent;
|
||||
Result := FlatButton(AParent, AText, AOnClick, APrimary);
|
||||
Result.Align := TAlignLayout.Top;
|
||||
Result.Margins.Rect := RectF(8, 4, 8, 0);
|
||||
Result.Margins.Rect := RectF(16, 8, 16, 0);
|
||||
Result.Height := AHeight;
|
||||
Result.Text := AText;
|
||||
Result.OnClick := AOnClick;
|
||||
end;
|
||||
|
||||
function TopEdit(AParent: TFmxObject; const APrompt, AText: string;
|
||||
@@ -316,6 +318,8 @@ begin
|
||||
FLaySettings.Visible := False;
|
||||
FToken := TopEdit(FLaySettings, 'jeton Bearer', FIRSTRUN_TOKEN);
|
||||
FUrl := TopEdit(FLaySettings, 'URL de l''endpoint', FIRSTRUN_URL);
|
||||
FToken.Margins.Rect := RectF(16, 8, 16, 0);
|
||||
FUrl.Margins.Rect := RectF(16, 8, 16, 0);
|
||||
// Ce que l'appareil a retenu l'emporte sur les valeurs de premier lancement.
|
||||
if LoadDeviceSettings(LUrl, LToken) then
|
||||
begin
|
||||
@@ -324,29 +328,28 @@ begin
|
||||
end;
|
||||
|
||||
FBtnSettings := TopButton(FLayUnlock, 'Reglages du serveur',
|
||||
OnToggleSettings, 36);
|
||||
FBtnSettings.TextSettings.Font.Size := 12;
|
||||
OnToggleSettings, 40);
|
||||
|
||||
FBtnSync := TopButton(FLayUnlock, 'Synchroniser', DoSync);
|
||||
FBtnOpen := TopButton(FLayUnlock, 'Ouvrir le coffre', DoOpenLocal);
|
||||
// Accentue : c'est l'action attendue quand aucun coffre n'est encore la.
|
||||
FBtnOpen := TopButton(FLayUnlock, 'Ouvrir le coffre', DoOpenLocal, 52, True);
|
||||
|
||||
// Pre-rempli depuis le fichier local, jamais depuis ce fichier-ci.
|
||||
FPwd := TopEdit(FLayUnlock, 'mot de passe de sync', TEST_PWD, True);
|
||||
FPwd.Margins.Top := 12;
|
||||
FPwd.Margins.Rect := RectF(16, 16, 16, 0);
|
||||
|
||||
FStatus := TLabel.Create(Self);
|
||||
FStatus.Parent := FLayUnlock;
|
||||
FStatus.Align := TAlignLayout.Top;
|
||||
FStatus.Margins.Rect := RectF(8, 16, 8, 0);
|
||||
FStatus.Height := 24;
|
||||
FStatus.StyledSettings := FStatus.StyledSettings - [TStyledSetting.Size];
|
||||
FStatus.TextSettings.Font.Size := 15;
|
||||
FStatus.Margins.Rect := RectF(16, 24, 16, 4);
|
||||
FStatus.Height := 26;
|
||||
Tint(FStatus, 15, True);
|
||||
end;
|
||||
|
||||
procedure TForm8.BuildList;
|
||||
var
|
||||
LBar, LTop: TLayout;
|
||||
LBtnLock: TButton;
|
||||
LBtnLock: TRectangle;
|
||||
begin
|
||||
FLayList := TLayout.Create(Self);
|
||||
FLayList.Parent := Self;
|
||||
@@ -358,28 +361,30 @@ begin
|
||||
LBar.Align := TAlignLayout.Bottom;
|
||||
LBar.Height := 52;
|
||||
|
||||
LBtnLock := TButton.Create(Self);
|
||||
LBtnLock.Parent := LBar;
|
||||
LBtnLock := FlatButton(LBar, 'Verrouiller', OnBackTap);
|
||||
LBtnLock.Align := TAlignLayout.Left;
|
||||
LBtnLock.Margins.Rect := RectF(8, 4, 0, 8);
|
||||
LBtnLock.Width := 130;
|
||||
LBtnLock.Text := 'Verrouiller';
|
||||
LBtnLock.OnClick := OnBackTap;
|
||||
LBtnLock.Margins.Rect := RectF(16, 4, 0, 8);
|
||||
LBtnLock.Width := 140;
|
||||
LBtnLock.Tag := 1; // 1 = verrouiller, 0 = simple retour (voir OnBackTap)
|
||||
|
||||
FCount := TLabel.Create(Self);
|
||||
FCount.Parent := LBar;
|
||||
FCount.Align := TAlignLayout.Client;
|
||||
FCount.Margins.Rect := RectF(12, 4, 8, 8);
|
||||
FCount.Margins.Rect := RectF(12, 4, 16, 8);
|
||||
FCount.TextSettings.HorzAlign := TTextAlign.Trailing;
|
||||
FCount.TextSettings.VertAlign := TTextAlign.Center;
|
||||
Tint(FCount, 13, True);
|
||||
|
||||
FList := TListBox.Create(Self);
|
||||
FList.Parent := FLayList;
|
||||
FList.Align := TAlignLayout.Client;
|
||||
FList.Margins.Rect := RectF(4, 0, 4, 0);
|
||||
FList.ItemHeight := 56;
|
||||
FList.Margins.Rect := RectF(8, 0, 8, 0);
|
||||
FList.ItemHeight := 62;
|
||||
FList.OnItemClick := OnItemTap;
|
||||
// Sans cadre : la liste est posee sur le fond general, pas dans une boite.
|
||||
// Le gabarit transparent existe dans les styles livres ; s'il manquait, on
|
||||
// retomberait simplement sur l'apparence encadree.
|
||||
FList.StyleLookup := 'transparentlistboxstyle';
|
||||
|
||||
LTop := TLayout.Create(Self);
|
||||
LTop.Parent := FLayList;
|
||||
@@ -452,35 +457,29 @@ begin
|
||||
end;
|
||||
|
||||
function AddRowButton(ARow: TLayout; const AText: string; ATag: Integer;
|
||||
AOnClick: TNotifyEvent): TButton;
|
||||
AOnClick: TNotifyEvent): TRectangle;
|
||||
begin
|
||||
Result := TButton.Create(ARow);
|
||||
Result.Parent := ARow;
|
||||
Result := FlatButton(ARow, AText, AOnClick);
|
||||
Result.Align := TAlignLayout.Right;
|
||||
Result.Margins.Left := 6;
|
||||
Result.Width := 90;
|
||||
Result.Text := AText;
|
||||
Result.Margins.Rect := RectF(8, 6, 0, 6);
|
||||
Result.Width := 96;
|
||||
Result.Tag := ATag;
|
||||
Result.OnClick := AOnClick;
|
||||
end;
|
||||
|
||||
procedure TForm8.BuildDetail;
|
||||
var
|
||||
LBtnBack: TButton;
|
||||
LBtnBack: TRectangle;
|
||||
begin
|
||||
FLayDetail := TLayout.Create(Self);
|
||||
FLayDetail.Parent := Self;
|
||||
FLayDetail.Align := TAlignLayout.Client;
|
||||
FLayDetail.Visible := False;
|
||||
|
||||
LBtnBack := TButton.Create(Self);
|
||||
LBtnBack.Parent := FLayDetail;
|
||||
LBtnBack := FlatButton(FLayDetail, 'Retour', OnBackTap);
|
||||
LBtnBack.Align := TAlignLayout.Bottom;
|
||||
LBtnBack.Height := 48;
|
||||
LBtnBack.Margins.Rect := RectF(8, 8, 8, 8);
|
||||
LBtnBack.Text := 'Retour';
|
||||
LBtnBack.Height := 52;
|
||||
LBtnBack.Margins.Rect := RectF(16, 8, 16, 8);
|
||||
LBtnBack.Tag := 0;
|
||||
LBtnBack.OnClick := OnBackTap;
|
||||
|
||||
FDetToast := TLabel.Create(Self);
|
||||
FDetToast.Parent := FLayDetail;
|
||||
@@ -522,12 +521,12 @@ begin
|
||||
Status('Coffre present sur cet appareil - protege')
|
||||
else
|
||||
Status('Coffre present sur cet appareil');
|
||||
FBtnSync.Text := 'Synchroniser';
|
||||
SetButtonText(FBtnSync, 'Synchroniser');
|
||||
end
|
||||
else
|
||||
begin
|
||||
Status('Aucun coffre local - premier telechargement');
|
||||
FBtnSync.Text := 'Telecharger le coffre';
|
||||
SetButtonText(FBtnSync, 'Telecharger le coffre');
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -714,7 +713,7 @@ end;
|
||||
procedure TForm8.OnBackTap(Sender: TObject);
|
||||
begin
|
||||
// Tag 1 = le bouton "Verrouiller" de la liste, 0 = le "Retour" du detail.
|
||||
if (Sender is TButton) and (TButton(Sender).Tag = 1) then
|
||||
if (Sender is TRectangle) and (TRectangle(Sender).Tag = 1) then
|
||||
LockVault
|
||||
else
|
||||
ShowList;
|
||||
@@ -894,7 +893,7 @@ begin
|
||||
end;
|
||||
FLog.Lines.Clear;
|
||||
|
||||
// MEME PORTE QUE « Ouvrir le coffre ». Sans cela la protection ne tenait
|
||||
// MEME PORTE QUE « Ouvrir le coffre ». Sans cela la protection ne tenait
|
||||
// qu'a un bouton pres : synchroniser reconstruit le coffre depuis le serveur
|
||||
// et l'affiche, ce qui revient exactement au meme resultat sans passer par
|
||||
// l'authentification. La condition est la presence d'un cache protege sur
|
||||
@@ -1258,20 +1257,20 @@ var
|
||||
I: Integer;
|
||||
begin
|
||||
Touch;
|
||||
if not (Sender is TButton) then Exit;
|
||||
I := TButton(Sender).Tag;
|
||||
if not (Sender is TRectangle) then Exit;
|
||||
I := TRectangle(Sender).Tag;
|
||||
if (I < 0) or (I > High(FRows)) then Exit;
|
||||
|
||||
FRowShown[I] := not FRowShown[I];
|
||||
if FRowShown[I] then
|
||||
begin
|
||||
FRowValues[I].Text := FRows[I].Value;
|
||||
TButton(Sender).Text := 'Masquer';
|
||||
SetButtonText(TRectangle(Sender), 'Masquer');
|
||||
end
|
||||
else
|
||||
begin
|
||||
FRowValues[I].Text := MASK;
|
||||
TButton(Sender).Text := 'Afficher';
|
||||
SetButtonText(TRectangle(Sender), 'Afficher');
|
||||
end;
|
||||
end;
|
||||
|
||||
@@ -1280,8 +1279,8 @@ var
|
||||
I: Integer;
|
||||
begin
|
||||
Touch;
|
||||
if not (Sender is TButton) then Exit;
|
||||
I := TButton(Sender).Tag;
|
||||
if not (Sender is TRectangle) then Exit;
|
||||
I := TRectangle(Sender).Tag;
|
||||
// -1 : la ligne du TOTP, qui n'est pas dans FRows - sa valeur change chaque
|
||||
// periode et ne vit que dans FTotpCode. Copie sans l'espace d'affichage.
|
||||
if I = -1 then
|
||||
@@ -1387,8 +1386,8 @@ end;
|
||||
procedure TForm8.SetBusy(ABusy: Boolean);
|
||||
begin
|
||||
FBusy := ABusy;
|
||||
FBtnSync.Enabled := not ABusy;
|
||||
FBtnOpen.Enabled := not ABusy;
|
||||
SetButtonEnabled(FBtnSync, not ABusy);
|
||||
SetButtonEnabled(FBtnOpen, not ABusy);
|
||||
end;
|
||||
|
||||
end.
|
||||
|
||||
Reference in New Issue
Block a user