Code partagé entre les apps Files Tech (PDF Tech, Read Files Tech, Pass Tech). Package local Flutter (path-dependency) — non publié sur pub.dev.
| Module | Rôle |
|---|---|
RecentFile + RecentFilesService |
Liste des fichiers récents persistée dans SharedPreferences, robuste aux entrées corrompues |
UpdateService + UpdateInfo |
Vérification de mises à jour via GitHub Releases, cache 12h, validation semver + host whitelist |
CloudShareRow + CloudTargets |
Boutons de partage cloud (kDrive, Google Drive, Proton Drive) via MethodChannel |
LegalSupportSections |
Sections "Aide & support" + "Mentions légales" avec whitelist de schemes (anti intent:/javascript:) |
FormatUtils |
Formatage humanisé d'octets (bytes style FR, bytesStorage style EN) |
PathSafe |
Validation et sanitisation de noms de fichiers (anti path-traversal) |
Ajouter dans le pubspec.yaml de l'app consommatrice :
dependencies:
files_tech_core:
path: ../files_tech_coreimport 'package:files_tech_core/files_tech_core.dart';
final svc = RecentFilesService();
List<RecentFile> recents = await svc.load();
recents = await svc.addOrUpdate(recents, '/storage/emulated/0/Download/foo.pdf');Configurer un singleton par app :
// app/lib/services/app_update.dart
const appUpdateService = UpdateService(
owner: 'gitubpatrice',
repo: 'PDF-TECH',
currentVersion: '1.8.0',
);
// Usage
final info = await appUpdateService.checkForUpdate();
final manualCheck = await appUpdateService.checkForUpdate(force: true);Wrapper per-app pour injecter le nom de channel :
// app/lib/widgets/cloud_share_row.dart
import 'package:files_tech_core/files_tech_core.dart' as core;
class CloudShareRow extends StatelessWidget {
final String path;
const CloudShareRow({super.key, required this.path});
@override
Widget build(BuildContext context) => core.CloudShareRow(
path: path,
channelName: 'com.pdftech.pdf_tech/share',
alignment: WrapAlignment.center,
);
}Côté Kotlin, le channel doit exposer sendToPackage qui prend path, mime, package.
const LegalSupportSections(
appName: 'PDF Tech',
version: '1.8.0',
)L'app consommatrice doit déclarer dans son pubspec.yaml :
flutter:
assets:
- assets/legal/PRIVACY.fr.md
- assets/legal/TERMS.fr.mdOverride possible des chemins via privacyAsset / termsAsset, et de
contactEmail / websiteUrl si nécessaire.
FormatUtils.bytes(1500); // → "1.5 Ko" (FR)
FormatUtils.bytesStorage(1500); // → "1.5 KB" (EN, MB sans décimale)PathSafe.basename('/foo/bar.pdf'); // → "bar.pdf"
PathSafe.basename('..'); // → throws ArgumentError
PathSafe.sanitizeFileName('my:file*.txt'); // → "my_file_.txt"
PathSafe.sanitizeForFs('hello world'); // → "hello_world" (variante FS-safe)
PathSafe.isValidUserFileName('foo.pdf'); // → true- Pas de secret hardcodé
- HTTPS uniquement (
api.github.com) - Schemes autorisés dans les liens :
https,http,mailto(refus deintent:,javascript:,file:, etc.) - Refus des URIs avec
userInfo(anti credential phishing) LaunchMode.externalApplication(pas de browser-in-app)RecentFilesService.loadfiltre silencieusement les entrées corrompues (anti DoS)UpdateServicevalidetag_nameen regex semver et whitelist le host de l'APK (github.com/objects.githubusercontent.com)mailtostrip CRLF (anti header injection)
flutter test- Versions des apps doivent rester synchronisées entre
pubspec.yaml,AboutScreen._versionetapp_update.dartcurrentVersion. - Les channels Kotlin sont par-app (
com.pdftech.pdf_tech/share,com.readfilestech/open_file, etc.) — déclarés dans le wrapper local.