feat(android): settings live on the device, dark theme, and the app is called Coffre
URL and token are no longer constants. They sit in settings.bin, sealed with the no-auth Keystore AES key - deliberately weaker than the vault: they must be readable to rebuild a vault that no longer exists, and a bearer token only opens ENCRYPTED data, worthless without the sync password that stays in the guarded vault. Retyping 64 hex characters after every cache purge was the real risk: the risk of writing them down elsewhere. They are saved only after a fetch that worked - proven settings, not typed ones. The token and sync password moved to the gitignored local include, as first-run defaults only. Tracked source now carries no secret, and leaving them empty is a viable configuration: the app asks. Theme: Delphi's Dark.style, copied into the repo and linked as a resource rather than deployed as a file - this project's deployment puts nothing beside the app, which is the same hole that forces the manifest and dex staging. A failure to load is never fatal; the default look is ugly but works. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
@@ -81,3 +81,6 @@ pmserverApk.res
|
|||||||
|
|
||||||
# Mot de passe de sync du banc d'essai - jamais versionne
|
# Mot de passe de sync du banc d'essai - jamais versionne
|
||||||
android-client/PMA.LocalConfig.inc
|
android-client/PMA.LocalConfig.inc
|
||||||
|
# Genere depuis theme.rc + pm-dark.style, tous deux suivis (voir README).
|
||||||
|
android-client/theme/theme.RES
|
||||||
|
android-client/theme/theme.res
|
||||||
|
|||||||
@@ -1,7 +1,14 @@
|
|||||||
// Modele. Copier en PMA.LocalConfig.inc (ignore par git) et remplir.
|
// Modele. Copier en PMA.LocalConfig.inc (ignore par git) et remplir.
|
||||||
//
|
//
|
||||||
// Le mot de passe de sync ouvre TOUT le coffre une fois le conteneur
|
// VALEURS DE PREMIER LANCEMENT SEULEMENT. Une fois que l'appareil a enregistre
|
||||||
// telecharge : il n'a rien a faire dans un depot, meme sans remote.
|
// ses reglages, elles ne sont plus lues. Laisser vide est parfaitement viable :
|
||||||
|
// l'application demande alors de saisir jeton et mot de passe, ce qui est le
|
||||||
|
// comportement d'une vraie installation.
|
||||||
|
//
|
||||||
|
// Le jeton authentifie l'appareil aupres de l'endpoint ; le mot de passe de
|
||||||
|
// sync ouvre TOUT le coffre une fois le conteneur telecharge. Aucun des deux
|
||||||
|
// n'a sa place dans un depot.
|
||||||
|
|
||||||
const
|
const
|
||||||
|
FIRSTRUN_TOKEN = '';
|
||||||
TEST_PWD = '';
|
TEST_PWD = '';
|
||||||
|
|||||||
@@ -107,6 +107,20 @@ procedure ClearVault;
|
|||||||
/// Chemin du fichier, pour le journal et les tests.
|
/// Chemin du fichier, pour le journal et les tests.
|
||||||
function VaultPath: string;
|
function VaultPath: string;
|
||||||
|
|
||||||
|
/// Reglages du serveur : URL et jeton Bearer.
|
||||||
|
///
|
||||||
|
/// FICHIER SEPARE, ET PROTECTION PLUS FAIBLE, DELIBEREMENT. Ils sont chiffres
|
||||||
|
/// par la cle AES du Keystore - liee au materiel, mais lisible SANS
|
||||||
|
/// authentification. Deux raisons :
|
||||||
|
/// - il faut pouvoir les relire pour reconstruire un coffre efface, alors
|
||||||
|
/// que la cle qui protege le coffre ne sert a rien s'il n'existe plus ;
|
||||||
|
/// - le jeton ne donne acces qu'a du CHIFFRE. Sans le mot de passe de sync,
|
||||||
|
/// qui lui reste dans le coffre protege, il ne vaut rien.
|
||||||
|
/// Retaper un jeton de 64 caracteres apres chaque purge de cache serait le
|
||||||
|
/// vrai risque : celui de le noter ailleurs.
|
||||||
|
procedure SaveDeviceSettings(const AUrl, AToken: string);
|
||||||
|
function LoadDeviceSettings(out AUrl, AToken: string): Boolean;
|
||||||
|
|
||||||
implementation
|
implementation
|
||||||
|
|
||||||
{$IF DEFINED(ANDROID)}
|
{$IF DEFINED(ANDROID)}
|
||||||
@@ -115,7 +129,8 @@ uses
|
|||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
|
|
||||||
const
|
const
|
||||||
VAULT_FILE = 'vault.bin';
|
VAULT_FILE = 'vault.bin';
|
||||||
|
SETTINGS_FILE = 'settings.bin';
|
||||||
// Un alias par schema : la contrainte d'authentification est figee a la
|
// Un alias par schema : la contrainte d'authentification est figee a la
|
||||||
// creation de la cle et ne peut pas etre ajoutee apres coup.
|
// creation de la cle et ne peut pas etre ajoutee apres coup.
|
||||||
KEY_ALIAS = 'pm_vault_key'; // AES, sans authentification
|
KEY_ALIAS = 'pm_vault_key'; // AES, sans authentification
|
||||||
@@ -596,6 +611,100 @@ begin
|
|||||||
end;
|
end;
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
|
|
||||||
|
{$IF DEFINED(ANDROID)}
|
||||||
|
/// Chiffre sous la cle AES du Keystore, sans contrainte d'authentification.
|
||||||
|
/// Format : [IV][chiffre+tag]. Sert aux reglages, pas au coffre.
|
||||||
|
function AesSeal(const APlain: TBytes): TBytes;
|
||||||
|
var
|
||||||
|
LCipher: JCipherX;
|
||||||
|
LIv, LCt: TBytes;
|
||||||
|
begin
|
||||||
|
LCipher := TJCipherX.JavaClass.getInstance(StringToJString('AES/GCM/NoPadding'));
|
||||||
|
LCipher.init(TJCipherX.JavaClass.ENCRYPT_MODE, EnsureAesKey);
|
||||||
|
LIv := FromJava(LCipher.getIV); // genere par le Keystore, jamais choisi ici
|
||||||
|
LCt := FromJava(LCipher.doFinal(ToJava(APlain)));
|
||||||
|
SetLength(Result, Length(LIv) + Length(LCt));
|
||||||
|
Move(LIv[0], Result[0], Length(LIv));
|
||||||
|
Move(LCt[0], Result[Length(LIv)], Length(LCt));
|
||||||
|
end;
|
||||||
|
|
||||||
|
function AesOpen(const ARaw: TBytes): TBytes;
|
||||||
|
var
|
||||||
|
LCipher: JCipherX;
|
||||||
|
LIv, LCt: TBytes;
|
||||||
|
begin
|
||||||
|
if Length(ARaw) < IV_LEN + 16 then Exit(nil);
|
||||||
|
SetLength(LIv, IV_LEN);
|
||||||
|
Move(ARaw[0], LIv[0], IV_LEN);
|
||||||
|
SetLength(LCt, Length(ARaw) - IV_LEN);
|
||||||
|
Move(ARaw[IV_LEN], LCt[0], Length(LCt));
|
||||||
|
LCipher := TJCipherX.JavaClass.getInstance(StringToJString('AES/GCM/NoPadding'));
|
||||||
|
LCipher.init(TJCipherX.JavaClass.DECRYPT_MODE, EnsureAesKey,
|
||||||
|
TJGCMParameterSpecX.JavaClass.init(128, ToJava(LIv)));
|
||||||
|
Result := FromJava(LCipher.doFinal(ToJava(LCt)));
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
|
procedure SaveDeviceSettings(const AUrl, AToken: string);
|
||||||
|
{$IF DEFINED(ANDROID)}
|
||||||
|
var
|
||||||
|
LObj: TJSONObject;
|
||||||
|
begin
|
||||||
|
LObj := TJSONObject.Create;
|
||||||
|
try
|
||||||
|
LObj.AddPair('url', AUrl);
|
||||||
|
LObj.AddPair('token', AToken);
|
||||||
|
try
|
||||||
|
TFile.WriteAllBytes(TPath.Combine(TPath.GetHomePath, SETTINGS_FILE),
|
||||||
|
AesSeal(TEncoding.UTF8.GetBytes(LObj.ToJSON)));
|
||||||
|
except
|
||||||
|
// Des reglages non enregistres se retapent ; ce n'est pas une raison
|
||||||
|
// d'interrompre une synchronisation qui, elle, a reussi.
|
||||||
|
on E: Exception do FLastStoreError := E.ClassName + ': ' + E.Message;
|
||||||
|
end;
|
||||||
|
finally
|
||||||
|
LObj.Free;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
{$ELSE}
|
||||||
|
begin
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
|
function LoadDeviceSettings(out AUrl, AToken: string): Boolean;
|
||||||
|
{$IF DEFINED(ANDROID)}
|
||||||
|
var
|
||||||
|
LPath: string;
|
||||||
|
LObj: TJSONObject;
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
AUrl := '';
|
||||||
|
AToken := '';
|
||||||
|
LPath := TPath.Combine(TPath.GetHomePath, SETTINGS_FILE);
|
||||||
|
if not TFile.Exists(LPath) then Exit;
|
||||||
|
try
|
||||||
|
LObj := TJSONObject.ParseJSONValue(
|
||||||
|
TEncoding.UTF8.GetString(AesOpen(TFile.ReadAllBytes(LPath)))) as TJSONObject;
|
||||||
|
if LObj = nil then Exit;
|
||||||
|
try
|
||||||
|
AUrl := LObj.GetValue<string>('url', '');
|
||||||
|
AToken := LObj.GetValue<string>('token', '');
|
||||||
|
Result := AUrl <> '';
|
||||||
|
finally
|
||||||
|
LObj.Free;
|
||||||
|
end;
|
||||||
|
except
|
||||||
|
Result := False;
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
{$ELSE}
|
||||||
|
begin
|
||||||
|
Result := False;
|
||||||
|
AUrl := '';
|
||||||
|
AToken := '';
|
||||||
|
end;
|
||||||
|
{$ENDIF}
|
||||||
|
|
||||||
function VaultNeedsAuth: Boolean;
|
function VaultNeedsAuth: Boolean;
|
||||||
var
|
var
|
||||||
LFs: TFileStream;
|
LFs: TFileStream;
|
||||||
|
|||||||
@@ -0,0 +1,43 @@
|
|||||||
|
unit PMA.Theme;
|
||||||
|
{
|
||||||
|
Thème sombre, chargé depuis une ressource embarquée.
|
||||||
|
|
||||||
|
POURQUOI UNE RESSOURCE ET NON UN FICHIER : le mecanisme de deploiement de ce
|
||||||
|
projet ne pose aucun fichier a cote de l'application - c'est le meme trou qui
|
||||||
|
oblige a mettre en place le manifeste et les .dex a la main. Une ressource
|
||||||
|
liee dans le .so ne depend de rien : elle est la ou n'est pas, et elle est la.
|
||||||
|
|
||||||
|
Le style vient de Dark.style, livre avec Delphi, recopie dans le depot pour
|
||||||
|
que la compilation ne dependre pas d'un chemin d'installation.
|
||||||
|
|
||||||
|
Un echec ici n'est jamais fatal : sans style, l'application garde l'apparence
|
||||||
|
par defaut. Elle est laide, elle fonctionne.
|
||||||
|
}
|
||||||
|
|
||||||
|
interface
|
||||||
|
|
||||||
|
/// A appeler une fois, avant la creation de la forme.
|
||||||
|
procedure ApplyDarkTheme;
|
||||||
|
|
||||||
|
implementation
|
||||||
|
|
||||||
|
uses
|
||||||
|
System.SysUtils, System.Types, System.Classes, FMX.Styles, FMX.Types;
|
||||||
|
|
||||||
|
{$R 'theme\theme.res' 'theme\theme.rc'}
|
||||||
|
|
||||||
|
procedure ApplyDarkTheme;
|
||||||
|
var
|
||||||
|
LStyle: TFmxObject;
|
||||||
|
begin
|
||||||
|
try
|
||||||
|
// La lecture appartient a TStyleStreaming, l'application a TStyleManager.
|
||||||
|
LStyle := TStyleStreaming.LoadFromResource(HInstance, 'PMDARKSTYLE', RT_RCDATA);
|
||||||
|
if LStyle <> nil then
|
||||||
|
TStyleManager.SetStyle(LStyle);
|
||||||
|
except
|
||||||
|
// Rien : l'apparence par defaut reste utilisable.
|
||||||
|
end;
|
||||||
|
end;
|
||||||
|
|
||||||
|
end.
|
||||||
@@ -196,6 +196,13 @@ De quoi il a besoin, mis en place à la main sous `Android64\Debug\pmserverApk\`
|
|||||||
| `classes\*.dex` | `Android64\Debug\pmserverApk.classes\*.dex` |
|
| `classes\*.dex` | `Android64\Debug\pmserverApk.classes\*.dex` |
|
||||||
| `library\lib\arm64-v8a\libpmserverApk.so` | `Android64\Debug\libpmserverApk.so` |
|
| `library\lib\arm64-v8a\libpmserverApk.so` | `Android64\Debug\libpmserverApk.so` |
|
||||||
|
|
||||||
|
Le thème a besoin de sa ressource, **une fois** (MSBuild ne compile pas les
|
||||||
|
`.rc` référencés par `{$R}` — c'est une commodité de l'EDI, pas du compilateur) :
|
||||||
|
|
||||||
|
```bash
|
||||||
|
cd android-client\theme && brcc32 theme.rc -fotheme.res
|
||||||
|
```
|
||||||
|
|
||||||
Ensuite `msbuild pmserverApk.dproj /p:Platform=Android64 /p:Config=Debug
|
Ensuite `msbuild pmserverApk.dproj /p:Platform=Android64 /p:Config=Debug
|
||||||
/t:Deploy` produit et signe `bin\pmserverApk.apk`, et `adb install -r` l'installe.
|
/t:Deploy` produit et signe `bin\pmserverApk.apk`, et `adb install -r` l'installe.
|
||||||
Le correctif durable serait d'écrire ces entrées une fois dans le `.dproj`.
|
Le correctif durable serait d'écrire ces entrées une fois dans le `.dproj`.
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1 @@
|
|||||||
|
PMDARKSTYLE RCDATA "pm-dark.style"
|
||||||
+23
-11
@@ -32,20 +32,20 @@ uses
|
|||||||
PMA.Vault;
|
PMA.Vault;
|
||||||
|
|
||||||
const
|
const
|
||||||
// Parametres du banc d'essai, pre-remplis pour ne pas les retaper.
|
// VALEURS DE PREMIER LANCEMENT UNIQUEMENT. Des que l'appareil a enregistre
|
||||||
|
// ses reglages, elles ne servent plus a rien : LoadDeviceSettings les
|
||||||
|
// remplace. Rien de secret ne doit vivre ici - le jeton et le mot de passe
|
||||||
|
// viennent du fichier local hors depot, plus bas.
|
||||||
//
|
//
|
||||||
// Le port differe selon la plateforme : sur Windows on attaque Apache
|
// Le port differe selon la plateforme : sur Windows on attaque Apache
|
||||||
// directement (80), sur Android on passe par "adb reverse tcp:8080 tcp:80",
|
// directement (80), sur Android on passe par "adb reverse tcp:8080 tcp:80",
|
||||||
// qui fait voir le PC comme 127.0.0.1 - ce qui satisfait au passage
|
// qui fait voir le PC comme 127.0.0.1 - ce qui satisfait au passage
|
||||||
// l'exemption HTTPS de l'endpoint, reservee a la boucle locale.
|
// l'exemption HTTPS de l'endpoint, reservee a la boucle locale.
|
||||||
{$IF DEFINED(ANDROID)}
|
{$IF DEFINED(ANDROID)}
|
||||||
TEST_URL = 'http://127.0.0.1:8080/server-php/sync.php';
|
FIRSTRUN_URL = 'http://127.0.0.1:8080/server-php/sync.php';
|
||||||
{$ELSE}
|
{$ELSE}
|
||||||
TEST_URL = 'http://127.0.0.1/server-php/sync.php';
|
FIRSTRUN_URL = 'http://127.0.0.1/server-php/sync.php';
|
||||||
{$ENDIF}
|
{$ENDIF}
|
||||||
// Jeton d'un serveur de test local. Il n'authentifie qu'un appareil et ne
|
|
||||||
// donne acces qu'a du chiffre - mais a remplacer avant toute mise en ligne.
|
|
||||||
TEST_TOKEN = '8765e5bf426039e9888f44bfbab27d489d12b7dbaaa54c8c695ae6af8b2a820e';
|
|
||||||
|
|
||||||
// Assez pour aller coller dans une autre application, trop court pour qu'un
|
// Assez pour aller coller dans une autre application, trop court pour qu'un
|
||||||
// mot de passe traine dans l'historique du presse-papiers.
|
// mot de passe traine dans l'historique du presse-papiers.
|
||||||
@@ -56,9 +56,10 @@ const
|
|||||||
// allume, application ouverte.
|
// allume, application ouverte.
|
||||||
AUTOLOCK_MINUTES = 3;
|
AUTOLOCK_MINUTES = 3;
|
||||||
|
|
||||||
// TEST_PWD vient d'un fichier LOCAL, hors depot : il ouvre le coffre entier
|
// FIRSTRUN_TOKEN et TEST_PWD viennent d'un fichier LOCAL, hors depot : le
|
||||||
// une fois le conteneur telecharge. Absent apres un clone -> recopier
|
// jeton authentifie l'appareil, le mot de passe ouvre le coffre entier. Ni
|
||||||
// PMA.LocalConfig.template.inc.
|
// l'un ni l'autre n'a sa place dans un depot, meme sans remote. Absents apres
|
||||||
|
// un clone -> recopier PMA.LocalConfig.template.inc.
|
||||||
{$I android-client\PMA.LocalConfig.inc}
|
{$I android-client\PMA.LocalConfig.inc}
|
||||||
|
|
||||||
type
|
type
|
||||||
@@ -285,6 +286,8 @@ begin
|
|||||||
end;
|
end;
|
||||||
|
|
||||||
procedure TForm8.BuildUnlock;
|
procedure TForm8.BuildUnlock;
|
||||||
|
var
|
||||||
|
LUrl, LToken: string;
|
||||||
begin
|
begin
|
||||||
FLayUnlock := TLayout.Create(Self);
|
FLayUnlock := TLayout.Create(Self);
|
||||||
FLayUnlock.Parent := Self;
|
FLayUnlock.Parent := Self;
|
||||||
@@ -310,8 +313,14 @@ begin
|
|||||||
FLaySettings.Align := TAlignLayout.Top;
|
FLaySettings.Align := TAlignLayout.Top;
|
||||||
FLaySettings.Height := 116;
|
FLaySettings.Height := 116;
|
||||||
FLaySettings.Visible := False;
|
FLaySettings.Visible := False;
|
||||||
FToken := TopEdit(FLaySettings, 'jeton Bearer', TEST_TOKEN);
|
FToken := TopEdit(FLaySettings, 'jeton Bearer', FIRSTRUN_TOKEN);
|
||||||
FUrl := TopEdit(FLaySettings, 'URL de l''endpoint', TEST_URL);
|
FUrl := TopEdit(FLaySettings, 'URL de l''endpoint', FIRSTRUN_URL);
|
||||||
|
// Ce que l'appareil a retenu l'emporte sur les valeurs de premier lancement.
|
||||||
|
if LoadDeviceSettings(LUrl, LToken) then
|
||||||
|
begin
|
||||||
|
FUrl.Text := LUrl;
|
||||||
|
if LToken <> '' then FToken.Text := LToken;
|
||||||
|
end;
|
||||||
|
|
||||||
FBtnSettings := TopButton(FLayUnlock, 'Reglages du serveur',
|
FBtnSettings := TopButton(FLayUnlock, 'Reglages du serveur',
|
||||||
OnToggleSettings, 36);
|
OnToggleSettings, 36);
|
||||||
@@ -1017,6 +1026,9 @@ begin
|
|||||||
// Le mot de passe part avec le coffre : c'est lui qui permettra au
|
// Le mot de passe part avec le coffre : c'est lui qui permettra au
|
||||||
// prochain lancement de rafraichir sans rien demander.
|
// prochain lancement de rafraichir sans rien demander.
|
||||||
SaveVault(LPlain, FETag, APwd, LCanAuth);
|
SaveVault(LPlain, FETag, APwd, LCanAuth);
|
||||||
|
// Ce qui vient de fonctionner merite d'etre retenu : on n'enregistre
|
||||||
|
// pas des reglages saisis, on enregistre des reglages EPROUVES.
|
||||||
|
SaveDeviceSettings(AUrl, ATok);
|
||||||
if VaultNeedsAuth then
|
if VaultNeedsAuth then
|
||||||
Log(' cache ecrit - protege par biometrie/code')
|
Log(' cache ecrit - protege par biometrie/code')
|
||||||
else if LCanAuth then
|
else if LCanAuth then
|
||||||
|
|||||||
+5
-1
@@ -12,12 +12,16 @@ uses
|
|||||||
PMA.Vault in 'android-client\PMA.Vault.pas',
|
PMA.Vault in 'android-client\PMA.Vault.pas',
|
||||||
PMA.Totp in 'android-client\PMA.Totp.pas',
|
PMA.Totp in 'android-client\PMA.Totp.pas',
|
||||||
PMA.ScreenGuard in 'android-client\PMA.ScreenGuard.pas',
|
PMA.ScreenGuard in 'android-client\PMA.ScreenGuard.pas',
|
||||||
PMA.Clipboard in 'android-client\PMA.Clipboard.pas';
|
PMA.Clipboard in 'android-client\PMA.Clipboard.pas',
|
||||||
|
PMA.Theme in 'android-client\PMA.Theme.pas';
|
||||||
|
|
||||||
{$R *.res}
|
{$R *.res}
|
||||||
|
|
||||||
begin
|
begin
|
||||||
Application.Initialize;
|
Application.Initialize;
|
||||||
|
// Avant la creation de la forme : appliquer un style apres coup laisse les
|
||||||
|
// controles deja construits avec l'ancien.
|
||||||
|
ApplyDarkTheme;
|
||||||
Application.CreateForm(TForm8, Form8);
|
Application.CreateForm(TForm8, Form8);
|
||||||
Application.Run;
|
Application.Run;
|
||||||
end.
|
end.
|
||||||
|
|||||||
+3
-3
@@ -1,4 +1,4 @@
|
|||||||
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||||
<PropertyGroup>
|
<PropertyGroup>
|
||||||
<ProjectGuid>{1AB0ADDE-54BE-4156-B446-73E9D7B3BE4D}</ProjectGuid>
|
<ProjectGuid>{1AB0ADDE-54BE-4156-B446-73E9D7B3BE4D}</ProjectGuid>
|
||||||
<ProjectVersion>20.1</ProjectVersion>
|
<ProjectVersion>20.1</ProjectVersion>
|
||||||
@@ -110,7 +110,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Base_Android)'!=''">
|
<PropertyGroup Condition="'$(Base_Android)'!=''">
|
||||||
<DCC_UsePackage>FireDACIBDriver;fmx;DbxCommonDriver;bindengine;IndyIPCommon;IndyProtocols;dbxcds;FmxTeeUI;FireDACSqliteDriver;DbxClientDriver;soapmidas;dbexpress;inet;CustomIPTransport;IndySystem;ibxbindings;FireDACCommon;bindcompdbx;rtl;DBXSqliteDriver;FireDAC;xmlrtl;ibxpress;dsnap;FireDACCommonDriver;IndyIPClient;bindcompfmx;ibmonitor;fmxFireDAC;fmxase;dbrtl;DBXInterBaseDriver;bindcomp;IndyCore;RESTBackendComponents;RESTComponents;IndyIPServer;dsnapxml;tethering;CloudService;FMXTee;soaprtl;soapserver;$(DCC_UsePackage)</DCC_UsePackage>
|
<DCC_UsePackage>FireDACIBDriver;fmx;DbxCommonDriver;bindengine;IndyIPCommon;IndyProtocols;dbxcds;FmxTeeUI;FireDACSqliteDriver;DbxClientDriver;soapmidas;dbexpress;inet;CustomIPTransport;IndySystem;ibxbindings;FireDACCommon;bindcompdbx;rtl;DBXSqliteDriver;FireDAC;xmlrtl;ibxpress;dsnap;FireDACCommonDriver;IndyIPClient;bindcompfmx;ibmonitor;fmxFireDAC;fmxase;dbrtl;DBXInterBaseDriver;bindcomp;IndyCore;RESTBackendComponents;RESTComponents;IndyIPServer;dsnapxml;tethering;CloudService;FMXTee;soaprtl;soapserver;$(DCC_UsePackage)</DCC_UsePackage>
|
||||||
<VerInfo_Keys>package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=</VerInfo_Keys>
|
<VerInfo_Keys>package=com.embarcadero.$(MSBuildProjectName);label=Coffre;versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=</VerInfo_Keys>
|
||||||
<BT_BuildType>Debug</BT_BuildType>
|
<BT_BuildType>Debug</BT_BuildType>
|
||||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||||
<Android_LauncherIcon36>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png</Android_LauncherIcon36>
|
<Android_LauncherIcon36>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png</Android_LauncherIcon36>
|
||||||
@@ -144,7 +144,7 @@
|
|||||||
</PropertyGroup>
|
</PropertyGroup>
|
||||||
<PropertyGroup Condition="'$(Base_Android64)'!=''">
|
<PropertyGroup Condition="'$(Base_Android64)'!=''">
|
||||||
<DCC_UsePackage>FireDACIBDriver;fmx;DbxCommonDriver;bindengine;IndyIPCommon;IndyProtocols;dbxcds;FmxTeeUI;FireDACSqliteDriver;DbxClientDriver;soapmidas;dbexpress;inet;CustomIPTransport;IndySystem;ibxbindings;FireDACCommon;bindcompdbx;rtl;DBXSqliteDriver;FireDAC;xmlrtl;ibxpress;dsnap;FireDACCommonDriver;IndyIPClient;bindcompfmx;ibmonitor;fmxFireDAC;dbrtl;DBXInterBaseDriver;bindcomp;IndyCore;RESTBackendComponents;RESTComponents;IndyIPServer;dsnapxml;tethering;CloudService;FMXTee;soaprtl;soapserver;$(DCC_UsePackage)</DCC_UsePackage>
|
<DCC_UsePackage>FireDACIBDriver;fmx;DbxCommonDriver;bindengine;IndyIPCommon;IndyProtocols;dbxcds;FmxTeeUI;FireDACSqliteDriver;DbxClientDriver;soapmidas;dbexpress;inet;CustomIPTransport;IndySystem;ibxbindings;FireDACCommon;bindcompdbx;rtl;DBXSqliteDriver;FireDAC;xmlrtl;ibxpress;dsnap;FireDACCommonDriver;IndyIPClient;bindcompfmx;ibmonitor;fmxFireDAC;dbrtl;DBXInterBaseDriver;bindcomp;IndyCore;RESTBackendComponents;RESTComponents;IndyIPServer;dsnapxml;tethering;CloudService;FMXTee;soaprtl;soapserver;$(DCC_UsePackage)</DCC_UsePackage>
|
||||||
<VerInfo_Keys>package=com.embarcadero.$(MSBuildProjectName);label=$(MSBuildProjectName);versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=</VerInfo_Keys>
|
<VerInfo_Keys>package=com.embarcadero.$(MSBuildProjectName);label=Coffre;versionCode=1;versionName=1.0.0;persistent=False;restoreAnyVersion=False;installLocation=auto;largeHeap=False;theme=TitleBar;hardwareAccelerated=true;apiKey=</VerInfo_Keys>
|
||||||
<BT_BuildType>Debug</BT_BuildType>
|
<BT_BuildType>Debug</BT_BuildType>
|
||||||
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
<VerInfo_IncludeVerInfo>true</VerInfo_IncludeVerInfo>
|
||||||
<Android_LauncherIcon36>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png</Android_LauncherIcon36>
|
<Android_LauncherIcon36>$(BDS)\bin\Artwork\Android\FM_LauncherIcon_36x36.png</Android_LauncherIcon36>
|
||||||
|
|||||||
Reference in New Issue
Block a user