commit 256599626e4d1adbd758002c4ba121927288eebc Author: kazerlelutin Date: Tue Jul 21 16:50:58 2026 +0200 first commit diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md new file mode 100644 index 0000000..388c817 --- /dev/null +++ b/CONTRIBUTING.md @@ -0,0 +1,31 @@ +# Contribuer à Ben-to + +## Workflow + +1. Ouvrir ou **référencer une issue** GitHub (comportement attendu, contexte, critères d’acceptation). +2. Créer une branche depuis `main` : `feat/issue-123-courte-description`, `fix/issue-123-…`, etc. +3. Implémenter en respectant la structure **feature-first** (`packages/features/*`, `packages/shared/*`) et les règles du dépôt (`.cursor/rules/`). +4. **Scénarios Gherkin** (`specs/features/*.feature`) : tout tag `@int-*` doit apparaître dans un fichier `*.int.test.ts` correspondant (voir `npm run check:gherkin-scenarios`). +5. **Imports explicites** : pas de fichier barrel `packages/**/src/index.ts` ; utiliser les sous-chemins documentés dans les `package.json` `exports` et les alias Vite (`@ben-to/.../domain`, etc.). +6. Avant la PR : `npm run check:quality` et `npm run test:ci`. +7. **Issue GitHub** : la laisser **ouverte** jusqu’au merge de la PR. Inclure dans le **message de commit** (recommandé, dernière ligne) `Closes #N` / `Fixes #N` pour que GitHub ferme l’issue automatiquement au merge ; éviter de fermer l’issue manuellement avant la fusion. + +### Hook pre-commit (tests unitaires) + +Après `npm install`, Husky installe un hook qui exécute `vitest related` sur les fichiers `.ts` / `.tsx` stagés (projet **unit** uniquement). + +Contournement exceptionnel (hotfix documenté en PR) : `git commit --no-verify`. + +## Qualité et couverture + +- Les seuils de couverture Vitest s’appliquent aux sources sous `packages/features/**` et `packages/shared/**` (voir `vitest.config.ts`). Le front web est couvert par les **e2e**, **Axe** et **Lighthouse**. +- La CI publie un **commentaire de synthèse** sur les PR (couverture, audit npm, Axe, Lighthouse, timings navigation). + +## GitHub + +- Activez **Code scanning** avec CodeQL si vous voulez les résultats SARIF dans l’onglet Sécurité ; puis retirez `upload: false` dans `.github/workflows/security.yml` (voir [docs/CODE_SCANNING.md](docs/CODE_SCANNING.md)). +- Protégez la branche `main` : CI obligatoire, revues si vous travaillez en équipe. + +## Mobile Android + +Les commandes `mobile:*` se lancent depuis la **racine** du monorepo (voir [README.md](README.md)). La CI exécute `assembleDebug` pour détecter les régressions Gradle/Capacitor. diff --git a/Dockerfile b/Dockerfile new file mode 100644 index 0000000..aa957f8 --- /dev/null +++ b/Dockerfile @@ -0,0 +1,27 @@ +FROM node:22-alpine AS build +WORKDIR /app +ENV CI=1 +COPY package*.json ./ +COPY apps/web/package.json apps/web/package.json +COPY packages/features/builder/package.json packages/features/builder/package.json +COPY packages/features/recipes/package.json packages/features/recipes/package.json +COPY packages/shared/i18n/package.json packages/shared/i18n/package.json +COPY packages/shared/privacy/package.json packages/shared/privacy/package.json +RUN npm ci --no-audit --no-fund +COPY . . +RUN node scripts/build-recipes.mjs + +# Vite : les VITE_* sont injectées au build (CapRover : build args ou CI avec --build-arg). +ARG VITE_MATOMO_URL= +ARG VITE_MATOMO_SITE_ID= +ENV VITE_MATOMO_URL=${VITE_MATOMO_URL} +ENV VITE_MATOMO_SITE_ID=${VITE_MATOMO_SITE_ID} +RUN npm run build:web + +FROM node:22-alpine AS runtime +WORKDIR /app +ENV NODE_ENV=production +COPY --from=build /app/apps/web/dist ./dist +RUN npm install -g serve@14.2.4 --omit=dev --no-audit --no-fund +EXPOSE 3000 +CMD ["serve", "-s", "dist", "-l", "3000"] diff --git a/README.md b/README.md new file mode 100644 index 0000000..52b8e68 --- /dev/null +++ b/README.md @@ -0,0 +1,156 @@ +# Ben-to + +Constructeur de bento pixel-art, multi-plateforme (web + Android + iOS). + +## Stack + +- Web: SolidJS (Vite) +- Mobile: Capacitor +- Données: catalogue embarqué, progression locale (offline, sans compte cloud en V1) +- Qualité: Vitest + Playwright + Axe + Lighthouse + +Partage des URLs recette (`/r/…`) et métadonnées Open Graph : voir [docs/seo-recipe-sharing.md](docs/seo-recipe-sharing.md). + +Matomo (analytics + bandeau cookies) : voir [docs/matomo-analytics.md](docs/matomo-analytics.md) et `apps/web/.env.example`. + +## Démarrage + +```bash +npm install +npm run pipeline:recipes +npm run dev:web +``` + +Les **recettes** (fichiers `*.lang.bentext`), les **couvertures** (`ressources/public/`, placeholders dans `ressources/recipe-placeholder-covers/`), les **sprites ingrédients** (`ressources/public/ingredients.png`, `ingredient-sprites.bentext`, source Aseprite sous `ressources/source/`) sont **versionnés dans ce dépôt** — pas de synchronisation avec un serveur de contenus externe. + +Après modification des sources, régénérer le catalogue : + +```bash +npm run pipeline:recipes +``` + +Règles d’édition des fichiers bentext : `.cursor/rules/recipes-bentext.mdc`. + +## Tester l'app web + +À exécuter depuis la **racine** du dépôt. + +**Même chaîne que la CI** (ESLint + TypeScript, Vitest avec **couverture** sur les packages métier, Playwright + Axe, pas de duplication unit/int séparée dans ce script) : + +```bash +npm run pipeline:recipes # régénère le catalogue si besoin (dev / e2e) +npm run test:ci +``` + +Voir [CONTRIBUTING.md](CONTRIBUTING.md) (hook pre-commit, tickets, Gherkin `@int-*`). + +Les **e2e** (`npm run test:e2e` ou via `test:ci`) s’appuient sur `playwright.config.ts` : le serveur Vite est démarré avec `npm run dev:web` si aucun n’écoute déjà sur le port (voir `reuseExistingServer`). + +Pour **régénérer les GIF** du tour d’accueil (fichiers dans `apps/web/public/tour/`, hors `test:ci`) : `npm run tour:capture` — nécessite `ffmpeg` sur la machine (`playwright.tour-capture.config.ts`). + +**Contrôles ciblés sur le paquet `apps/web`** (lint + types du front uniquement) : + +```bash +npm run lint -w apps/web +npm run typecheck -w apps/web +``` + +**Build de production** du front : + +```bash +npm run pipeline:recipes +npm run build:web +``` + +## Tester sur un téléphone Android (Capacitor) + +Le shell natif est dans `**apps/mobile**` et embarque le build statique `**apps/web/dist**` (voir `apps/mobile/capacitor.config.ts`). + +Un correctif **patch-package** (`patches/@capacitor-community+sqlite+8.1.0.patch`) remplace `proguard-android.txt` par `proguard-android-optimize.txt` dans le plugin SQLite (exigence R8 / AGP récents). Il est réappliqué à chaque `**npm install`** via le script `**postinstall`**. + +**Important — répertoire courant** : `pipeline:recipes`, `build:web` et les scripts `mobile:`* sont définis dans le `**package.json` à la racine** du dépôt (`ben-to/`, là où se trouve ce README). Si vous lancez `npm run …` depuis `**apps/mobile`**, npm cherche ces scripts dans le workspace mobile et échoue (`Missing script`). Vérifiez avec `ls package.json` : vous devez voir le manifest racine, pas celui du sous-dossier. + +### Prérequis + +- [Android Studio](https://developer.android.com/studio) (SDK + outils plate-forme). +- Sur le téléphone : **Options pour les développeurs** activées, puis **Débogage USB** (câble USB) ou **Débogage sans fil** (Wi‑Fi) selon votre appareil. + +### Première fois : ajouter la plateforme Android + +Si le dossier `**apps/mobile/android/`** n’existe pas encore dans votre clone : + +```bash +cd apps/mobile +npx cap add android +cd ../.. +``` + +### Build du web, sync Capacitor, lancer sur l’appareil + +**Toujours depuis la racine du dépôt** (puis Android Studio s’ouvre sur `apps/mobile/android`) : + +```bash +npm install +npm run pipeline:recipes +npm run build:web +npm run mobile:sync +npm run mobile:open:android +``` + +(`mobile:sync` / `mobile:open:android` appellent le workspace `**@ben-to/mobile**` ; évite les erreurs du type `No workspaces found` si le flag `-w` ne cible pas le bon nom.) + +Dans Android Studio : choisir votre téléphone comme cible, puis **Run** (▶). + +En ligne de commande (appareil déjà autorisé / `adb devices` OK), **toujours depuis la racine** : + +```bash +npm run mobile:run:android +``` + +### Modifier le front sans tout régénérer (optionnel) + +Pour charger l’UI depuis votre PC sur le réseau local (évite un `build:web` à chaque changement), vous pouvez temporairement renseigner dans `apps/mobile/capacitor.config.ts` un bloc `server: { url: "http://VOTRE_IP_LAN:3000", cleartext: true }` (à retirer avant release), puis lancer le front avec l’écoute réseau : + +```bash +npm run dev:web -- --host 0.0.0.0 +``` + +Adaptez le pare-feu / le même Wi‑Fi entre le PC et le téléphone. + +## Qualité obligatoire + +```bash +npm run check:quality +npm run test:unit +npm run test:integration +npm run test:e2e +``` + +## CI / Sécurité + +- `.github/workflows/ci.yml` — qualité, couverture, e2e + artefacts qualité, Lighthouse, **assembleDebug Android**, rapport PR +- `.github/workflows/security.yml` — CodeQL, `npm audit`, Gitleaks ([activation upload SARIF](docs/CODE_SCANNING.md)) +- `.github/workflows/dependency-review.yml` — revue des dépendances sur les PR (selon options du dépôt) +- `.github/workflows/mobile-release.yml` + +## Publication V1 (web + Android) + +Checklist gel release : [docs/V1_LAUNCH_CHECKLIST.md](docs/V1_LAUNCH_CHECKLIST.md). + +## Déploiement CapRover + +- Le dépôt contient `Dockerfile` et `captain-definition` pour un déploiement via CapRover (hors GitHub Actions). + +## Ticketing GitHub + +Création rapide des issues MVP : + +```bash +./scripts/github/create-issues.sh kazerlelutin/ben-to +``` + +Tickets post-MVP (builder SOLID, suppression des barrels `index`, SEO OG recettes) : gabarits sous [.github/ISSUE_TEMPLATE/](.github/ISSUE_TEMPLATE/) (`04`–`06`), corps dans [docs/tickets/](docs/tickets/), création en masse après `gh auth login` : + +```bash +./scripts/github/create-post-mvp-issues.sh +``` diff --git a/apps/mobile/android/.gitignore b/apps/mobile/android/.gitignore new file mode 100644 index 0000000..f57d246 --- /dev/null +++ b/apps/mobile/android/.gitignore @@ -0,0 +1,91 @@ +# Using Android gitignore template: https://github.com/github/gitignore/blob/HEAD/Android.gitignore + +# Built application files +*.apk +*.aar +*.ap_ +*.aab + +# Files for the ART/Dalvik VM +*.dex + +# Java class files +*.class + +# Generated files +bin/ +gen/ +out/ +# Uncomment the following line in case you need and you don't have the release build type files in your app +# release/ + +# Gradle files +.gradle/ +build/ + +# Local configuration file (sdk path, etc) +local.properties + +# Proguard folder generated by Eclipse +proguard/ + +# Log Files +*.log + +# Android Studio Navigation editor temp files +.navigation/ + +# Android Studio captures folder +captures/ + +# IntelliJ / Android Studio +*.iml +.idea/ + +# Keystore files +# Uncomment the following lines if you do not want to check your keystore files in. +#*.jks +#*.keystore + +# External native build folder generated in Android Studio 2.2 and later +.externalNativeBuild +.cxx/ + +# Google Services (e.g. APIs or Firebase) +# google-services.json + +# Freeline +freeline.py +freeline/ +freeline_project_description.json + +# fastlane +fastlane/report.xml +fastlane/Preview.html +fastlane/screenshots +fastlane/test_output +fastlane/readme.md + +# Version control +vcs.xml + +# lint +lint/intermediates/ +lint/generated/ +lint/outputs/ +lint/tmp/ +# lint/reports/ + +# Android Profiling +*.hprof + +# Cordova plugins for Capacitor +capacitor-cordova-android-plugins + +# Copied web assets +app/src/main/assets/public + +# Generated Config files +app/src/main/assets/capacitor.config.json +app/src/main/assets/capacitor.plugins.json +app/src/main/res/xml/config.xml diff --git a/apps/mobile/android/app/.gitignore b/apps/mobile/android/app/.gitignore new file mode 100644 index 0000000..043df80 --- /dev/null +++ b/apps/mobile/android/app/.gitignore @@ -0,0 +1,2 @@ +/build/* +!/build/.npmkeep diff --git a/apps/mobile/android/app/build.gradle b/apps/mobile/android/app/build.gradle new file mode 100644 index 0000000..369b18b --- /dev/null +++ b/apps/mobile/android/app/build.gradle @@ -0,0 +1,74 @@ +apply plugin: 'com.android.application' + +/** Fichier optionnel `android/keystore.properties` + chemin `storeFile` — voir `keystore.properties.example`. */ +def keystorePropertiesFile = rootProject.file("keystore.properties") +def keystoreProperties = new Properties() +if (keystorePropertiesFile.exists()) { + keystoreProperties.load(new FileInputStream(keystorePropertiesFile)) +} + +android { + namespace "com.kazerlelutin.bento" + compileSdk rootProject.ext.compileSdkVersion + defaultConfig { + applicationId "com.kazerlelutin.bento" + minSdkVersion rootProject.ext.minSdkVersion + targetSdkVersion rootProject.ext.targetSdkVersion + versionCode 1 + versionName "1.0" + testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner" + aaptOptions { + // Files and dirs to omit from the packaged assets dir, modified to accommodate modern web apps. + // Default: https://android.googlesource.com/platform/frameworks/base/+/282e181b58cf72b6ca770dc7ca5f91f135444502/tools/aapt/AaptAssets.cpp#61 + ignoreAssetsPattern '!.svn:!.git:!.ds_store:!*.scc:.*:!CVS:!thumbs.db:!picasa.ini:!*~' + } + } + signingConfigs { + release { + if (keystorePropertiesFile.exists()) { + keyAlias keystoreProperties["keyAlias"] + keyPassword keystoreProperties["keyPassword"] + storeFile rootProject.file(keystoreProperties["storeFile"]) + storePassword keystoreProperties["storePassword"] + } + } + } + buildTypes { + release { + minifyEnabled false + proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro' + if (keystorePropertiesFile.exists()) { + signingConfig signingConfigs.release + } + } + } +} + +repositories { + flatDir{ + dirs '../capacitor-cordova-android-plugins/src/main/libs', 'libs' + } +} + +dependencies { + implementation fileTree(include: ['*.jar'], dir: 'libs') + implementation "androidx.appcompat:appcompat:$androidxAppCompatVersion" + implementation "androidx.coordinatorlayout:coordinatorlayout:$androidxCoordinatorLayoutVersion" + implementation "androidx.core:core-splashscreen:$coreSplashScreenVersion" + implementation project(':capacitor-android') + testImplementation "junit:junit:$junitVersion" + androidTestImplementation "androidx.test.ext:junit:$androidxJunitVersion" + androidTestImplementation "androidx.test.espresso:espresso-core:$androidxEspressoCoreVersion" + implementation project(':capacitor-cordova-android-plugins') +} + +apply from: 'capacitor.build.gradle' + +try { + def servicesJSON = file('google-services.json') + if (servicesJSON.text) { + apply plugin: 'com.google.gms.google-services' + } +} catch(Exception e) { + logger.info("google-services.json not found, google-services plugin not applied. Push Notifications won't work") +} diff --git a/apps/mobile/android/app/capacitor.build.gradle b/apps/mobile/android/app/capacitor.build.gradle new file mode 100644 index 0000000..76e9c48 --- /dev/null +++ b/apps/mobile/android/app/capacitor.build.gradle @@ -0,0 +1,20 @@ +// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN + +android { + compileOptions { + sourceCompatibility JavaVersion.VERSION_21 + targetCompatibility JavaVersion.VERSION_21 + } +} + +apply from: "../capacitor-cordova-android-plugins/cordova.variables.gradle" +dependencies { + implementation project(':capacitor-share') + implementation project(':capacitor-community-sqlite') + +} + + +if (hasProperty('postBuildExtras')) { + postBuildExtras() +} diff --git a/apps/mobile/android/app/gradle.lockfile b/apps/mobile/android/app/gradle.lockfile new file mode 100644 index 0000000..20e6397 --- /dev/null +++ b/apps/mobile/android/app/gradle.lockfile @@ -0,0 +1,251 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +androidx.activity:activity:1.7.0=debugAndroidTestCompileClasspath +androidx.activity:activity:1.9.2=debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.annotation:annotation-experimental:1.4.0=debugAndroidTestCompileClasspath +androidx.annotation:annotation-experimental:1.4.1=debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.annotation:annotation-jvm:1.8.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.annotation:annotation:1.8.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.appcompat:appcompat-resources:1.7.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.appcompat:appcompat:1.7.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.arch.core:core-common:2.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.arch.core:core-runtime:2.1.0=debugAndroidTestCompileClasspath +androidx.arch.core:core-runtime:2.2.0=debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.biometric:biometric:1.1.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.collection:collection-jvm:1.4.2=debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.collection:collection:1.1.0=debugAndroidTestCompileClasspath +androidx.collection:collection:1.4.2=debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.concurrent:concurrent-futures-ktx:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath +androidx.concurrent:concurrent-futures:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.coordinatorlayout:coordinatorlayout:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.core:core-ktx:1.15.0=debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.core:core-ktx:1.2.0=debugAndroidTestCompileClasspath +androidx.core:core-splashscreen:1.0.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.core:core:1.13.0=debugAndroidTestCompileClasspath +androidx.core:core:1.15.0=debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.cursoradapter:cursoradapter:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.customview:customview:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.drawerlayout:drawerlayout:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.emoji2:emoji2-views-helper:1.3.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.emoji2:emoji2:1.3.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.fragment:fragment:1.5.4=debugAndroidTestCompileClasspath +androidx.fragment:fragment:1.8.4=debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.interpolator:interpolator:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.lifecycle:lifecycle-common:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.lifecycle:lifecycle-livedata-core:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.lifecycle:lifecycle-livedata:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.lifecycle:lifecycle-process:2.6.2=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.lifecycle:lifecycle-runtime:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.lifecycle:lifecycle-viewmodel-savedstate:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.lifecycle:lifecycle-viewmodel:2.6.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.loader:loader:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.profileinstaller:profileinstaller:1.3.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.resourceinspection:resourceinspection-annotation:1.0.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.room:room-common:2.6.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.room:room-runtime:2.6.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.savedstate:savedstate:1.2.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.security:security-crypto:1.1.0-alpha06=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.sqlite:sqlite-framework:2.4.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.sqlite:sqlite:2.4.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.startup:startup-runtime:1.1.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.test.espresso:espresso-core:3.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath +androidx.test.espresso:espresso-idling-resource:3.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath +androidx.test.ext:junit:1.2.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath +androidx.test.services:storage:1.5.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath +androidx.test:core:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath +androidx.test:monitor:1.7.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath +androidx.test:runner:1.6.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath +androidx.tracing:tracing:1.2.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.vectordrawable:vectordrawable-animated:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.vectordrawable:vectordrawable:1.1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.versionedparcelable:versionedparcelable:1.1.1=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.viewpager:viewpager:1.0.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +androidx.webkit:webkit:1.12.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +com.android.tools.analytics-library:protos:32.2.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +com.android.tools.analytics-library:shared:32.2.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +com.android.tools.analytics-library:tracker:32.2.0=androidLintTool +com.android.tools.build:aapt2-proto:9.2.0-15009934=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +com.android.tools.build:builder-model:9.2.0=androidLintTool +com.android.tools.build:manifest-merger:32.2.0=androidLintTool +com.android.tools.ddms:ddmlib:32.2.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +com.android.tools.emulator:proto:32.2.0=unified-test-platform-android-test-plugin-host-emulator-control +com.android.tools.external.com-intellij:intellij-core:32.2.0=androidLintTool +com.android.tools.external.com-intellij:kotlin-compiler:32.2.0=androidLintTool +com.android.tools.external.org-jetbrains:uast:32.2.0=androidLintTool +com.android.tools.layoutlib:layoutlib-api:32.2.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +com.android.tools.lint:lint-api:32.2.0=androidLintTool +com.android.tools.lint:lint-checks:32.2.0=androidLintTool +com.android.tools.lint:lint-gradle:32.2.0=androidLintTool +com.android.tools.lint:lint-model:32.2.0=androidLintTool +com.android.tools.lint:lint-typedef-remover:32.2.0=androidLintTool +com.android.tools.lint:lint:32.2.0=androidLintTool +com.android.tools.utp:android-device-provider-ddmlib-proto:32.2.0=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-gradle-work-action +com.android.tools.utp:android-device-provider-ddmlib:32.2.0=unified-test-platform-android-device-provider-ddmlib +com.android.tools.utp:android-test-plugin-host-additional-test-output-proto:32.2.0=unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-gradle-work-action +com.android.tools.utp:android-test-plugin-host-additional-test-output:32.2.0=unified-test-platform-android-test-plugin-host-additional-test-output +com.android.tools.utp:android-test-plugin-host-apk-installer-proto:32.2.0=unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-gradle-work-action +com.android.tools.utp:android-test-plugin-host-apk-installer:32.2.0=unified-test-platform-android-test-plugin-host-apk-installer +com.android.tools.utp:android-test-plugin-host-coverage-proto:32.2.0=unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-gradle-work-action +com.android.tools.utp:android-test-plugin-host-coverage:32.2.0=unified-test-platform-android-test-plugin-host-coverage +com.android.tools.utp:android-test-plugin-host-device-info-proto:32.2.0=unified-test-platform-android-test-plugin-host-device-info +com.android.tools.utp:android-test-plugin-host-device-info:32.2.0=unified-test-platform-android-test-plugin-host-device-info +com.android.tools.utp:android-test-plugin-host-emulator-control-proto:32.2.0=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-gradle-work-action +com.android.tools.utp:android-test-plugin-host-emulator-control:32.2.0=unified-test-platform-android-test-plugin-host-emulator-control +com.android.tools.utp:android-test-plugin-host-logcat-proto:32.2.0=unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-gradle-work-action +com.android.tools.utp:android-test-plugin-host-logcat:32.2.0=unified-test-platform-android-test-plugin-host-logcat +com.android.tools.utp:android-test-plugin-result-listener-gradle-proto:32.2.0=unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +com.android.tools.utp:android-test-plugin-result-listener-gradle:32.2.0=unified-test-platform-android-test-plugin-result-listener-gradle +com.android.tools.utp:gradle-work-action:32.2.0=unified-test-platform-gradle-work-action +com.android.tools.utp:utp-common:32.2.0=unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-logcat +com.android.tools:annotations:32.2.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +com.android.tools:common:32.2.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +com.android.tools:dvlib:32.2.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +com.android.tools:play-sdk-proto:32.2.0=androidLintTool +com.android.tools:repository:32.2.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +com.android.tools:sdk-common:32.2.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +com.android.tools:sdklib:32.2.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +com.google.android:annotations:4.1.1.4=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +com.google.api.grpc:proto-google-common-protos:2.17.0=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core +com.google.api.grpc:proto-google-common-protos:2.48.0=unified-test-platform-android-test-plugin-host-emulator-control +com.google.auto.service:auto-service-annotations:1.1.1=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle +com.google.auto.service:auto-service:1.1.1=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle +com.google.auto:auto-common:1.2.1=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle +com.google.code.findbugs:jsr305:3.0.2=androidLintTool,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher +com.google.code.gson:gson:2.10.1=unified-test-platform-core,unified-test-platform-gradle-work-action +com.google.code.gson:gson:2.11.0=androidLintTool,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-result-listener-gradle +com.google.code.gson:gson:2.8.9=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath,unified-test-platform-android-driver-instrumentation,unified-test-platform-launcher +com.google.crypto.tink:tink-android:1.8.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +com.google.crypto.tink:tink:1.18.0=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-gradle-work-action +com.google.dagger:dagger:2.48=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action +com.google.errorprone:error_prone_annotations:2.18.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath +com.google.errorprone:error_prone_annotations:2.23.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher +com.google.errorprone:error_prone_annotations:2.28.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +com.google.errorprone:error_prone_annotations:2.30.0=unified-test-platform-android-test-plugin-host-emulator-control +com.google.guava:failureaccess:1.0.1=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher +com.google.guava:failureaccess:1.0.2=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +com.google.guava:guava:32.0.1-jre=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher +com.google.guava:guava:33.3.1-jre=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +com.google.guava:listenablefuture:1.0=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher +com.google.j2objc:j2objc-annotations:2.8=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher +com.google.j2objc:j2objc-annotations:3.0.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +com.google.jimfs:jimfs:1.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +com.google.protobuf:protobuf-java-util:3.22.3=unified-test-platform-core +com.google.protobuf:protobuf-java-util:4.28.3=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-gradle-work-action,unified-test-platform-launcher +com.google.protobuf:protobuf-java:3.25.5=androidLintTool +com.google.protobuf:protobuf-java:4.28.3=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher +com.google.protobuf:protobuf-kotlin:4.28.3=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher +com.google.testing.platform:android-device-provider-local:0.0.9-alpha04=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle +com.google.testing.platform:android-driver-instrumentation:0.0.9-alpha04=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-emulator-control +com.google.testing.platform:android-test-plugin:0.0.9-alpha04=unified-test-platform-android-test-plugin +com.google.testing.platform:core-proto:0.0.9-alpha04=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +com.google.testing.platform:core:0.0.9-alpha04=unified-test-platform-core +com.google.testing.platform:launcher:0.0.9-alpha04=unified-test-platform-gradle-work-action,unified-test-platform-launcher +com.sun.istack:istack-commons-runtime:3.0.8=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +com.sun.xml.fastinfoset:FastInfoset:1.2.16=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +commons-codec:commons-codec:1.17.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +commons-io:commons-io:2.16.1=androidLintTool,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +commons-logging:commons-logging:1.2=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +io.grpc:grpc-api:1.57.2=unified-test-platform-core +io.grpc:grpc-api:1.69.1=unified-test-platform-android-test-plugin-host-emulator-control +io.grpc:grpc-context:1.57.2=unified-test-platform-core +io.grpc:grpc-context:1.69.1=unified-test-platform-android-test-plugin-host-emulator-control +io.grpc:grpc-core:1.57.2=unified-test-platform-core +io.grpc:grpc-core:1.69.1=unified-test-platform-android-test-plugin-host-emulator-control +io.grpc:grpc-netty:1.57.2=unified-test-platform-core +io.grpc:grpc-netty:1.69.1=unified-test-platform-android-test-plugin-host-emulator-control +io.grpc:grpc-protobuf-lite:1.57.2=unified-test-platform-core +io.grpc:grpc-protobuf-lite:1.69.1=unified-test-platform-android-test-plugin-host-emulator-control +io.grpc:grpc-protobuf:1.57.2=unified-test-platform-core +io.grpc:grpc-protobuf:1.69.1=unified-test-platform-android-test-plugin-host-emulator-control +io.grpc:grpc-services:1.57.2=unified-test-platform-core +io.grpc:grpc-stub:1.57.2=unified-test-platform-core +io.grpc:grpc-stub:1.69.1=unified-test-platform-android-test-plugin-host-emulator-control +io.grpc:grpc-util:1.69.1=unified-test-platform-android-test-plugin-host-emulator-control +io.netty:netty-buffer:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.netty:netty-codec-http2:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.netty:netty-codec-http:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.netty:netty-codec-socks:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.netty:netty-codec:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.netty:netty-common:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.netty:netty-handler-proxy:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.netty:netty-handler:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.netty:netty-resolver:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.netty:netty-transport-native-unix-common:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.netty:netty-transport:4.1.133.Final=unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-core +io.opencensus:opencensus-api:0.31.0=unified-test-platform-core +io.opencensus:opencensus-proto:0.2.0=unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher +io.perfmark:perfmark-api:0.26.0=unified-test-platform-core +io.perfmark:perfmark-api:0.27.0=unified-test-platform-android-test-plugin-host-emulator-control +jakarta.activation:jakarta.activation-api:1.2.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +jakarta.xml.bind:jakarta.xml.bind-api:2.3.2=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +javax.annotation:javax.annotation-api:1.3.2=unified-test-platform-android-test-plugin-host-emulator-control +javax.inject:javax.inject:1=androidLintTool,debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action +junit:junit:4.13.2=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +net.java.dev.jna:jna-platform:5.6.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +net.java.dev.jna:jna:5.6.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +net.sf.kxml:kxml2:2.3.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +net.zetetic:sqlcipher-android:4.10.0=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.apache.commons:commons-compress:1.27.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.apache.commons:commons-lang3:3.16.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.apache.cordova:framework:10.1.1=debugAndroidTestLintChecksClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.apache.httpcomponents:httpclient:4.5.6=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.apache.httpcomponents:httpcore:4.4.16=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.apache.httpcomponents:httpmime:4.5.6=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.bouncycastle:bcpkix-jdk18on:1.79=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.bouncycastle:bcprov-jdk18on:1.79=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.bouncycastle:bcutil-jdk18on:1.79=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.checkerframework:checker-qual:3.33.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher +org.checkerframework:checker-qual:3.43.0=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +org.codehaus.groovy:groovy:3.0.22=androidLintTool +org.codehaus.mojo:animal-sniffer-annotations:1.23=unified-test-platform-core +org.codehaus.mojo:animal-sniffer-annotations:1.24=unified-test-platform-android-test-plugin-host-emulator-control +org.glassfish.jaxb:jaxb-runtime:2.3.2=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.glassfish.jaxb:txw2:2.3.2=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.hamcrest:hamcrest-core:1.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath +org.hamcrest:hamcrest-library:1.3=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath +org.jacoco:org.jacoco.agent:0.8.14=androidJacocoAnt +org.jacoco:org.jacoco.ant:0.8.14=androidJacocoAnt +org.jacoco:org.jacoco.core:0.8.14=androidJacocoAnt +org.jacoco:org.jacoco.report:0.8.14=androidJacocoAnt +org.jetbrains.kotlin:kotlin-reflect:1.8.21=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher +org.jetbrains.kotlin:kotlin-reflect:2.2.10=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.jetbrains.kotlin:kotlin-stdlib-common:1.8.21=unified-test-platform-android-test-plugin,unified-test-platform-core +org.jetbrains.kotlin:kotlin-stdlib-common:1.8.22=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-common:1.9.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-launcher +org.jetbrains.kotlin:kotlin-stdlib-common:2.2.10=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-gradle-work-action +org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.20=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher +org.jetbrains.kotlin:kotlin-stdlib-jdk7:1.8.22=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.10=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.20=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-core,unified-test-platform-launcher +org.jetbrains.kotlin:kotlin-stdlib-jdk8:1.8.22=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.10=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +org.jetbrains.kotlin:kotlin-stdlib:1.8.21=unified-test-platform-android-test-plugin,unified-test-platform-core +org.jetbrains.kotlin:kotlin-stdlib:1.8.22=debugAndroidTestCompileClasspath,debugAndroidTestLintChecksClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.jetbrains.kotlin:kotlin-stdlib:1.9.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-launcher +org.jetbrains.kotlin:kotlin-stdlib:2.2.10=androidLintTool,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-gradle-work-action +org.jetbrains.kotlinx:atomicfu-jvm:0.22.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-gradle-work-action,unified-test-platform-launcher +org.jetbrains.kotlinx:atomicfu:0.22.0=unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-gradle-work-action,unified-test-platform-launcher +org.jetbrains.kotlinx:kotlinx-coroutines-android:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-android:1.7.1=debugAndroidTestLintChecksClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.1=debugAndroidTestLintChecksClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.7.3=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher +org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.1=debugAndroidTestLintChecksClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.7.3=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.1=debugAndroidTestLintChecksClasspath +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3=unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.jetbrains:annotations:13.0=debugAndroidTestCompileClasspath,debugAndroidTestRuntimeClasspath,debugCompileClasspath,debugLintChecksClasspath,debugRuntimeClasspath,debugUnitTestCompileClasspath,debugUnitTestLintChecksClasspath,debugUnitTestRuntimeClasspath,releaseCompileClasspath,releaseLintChecksClasspath,releaseRuntimeClasspath +org.jetbrains:annotations:23.0.0=androidLintTool,debugAndroidTestLintChecksClasspath,unified-test-platform-android-device-provider-ddmlib,unified-test-platform-android-driver-instrumentation,unified-test-platform-android-test-plugin,unified-test-platform-android-test-plugin-host-additional-test-output,unified-test-platform-android-test-plugin-host-apk-installer,unified-test-platform-android-test-plugin-host-coverage,unified-test-platform-android-test-plugin-host-device-info,unified-test-platform-android-test-plugin-host-emulator-control,unified-test-platform-android-test-plugin-host-logcat,unified-test-platform-android-test-plugin-result-listener-gradle,unified-test-platform-core,unified-test-platform-gradle-work-action,unified-test-platform-launcher +org.jvnet.staxex:stax-ex:1.8.1=androidLintTool,unified-test-platform-android-test-plugin-result-listener-gradle +org.ow2.asm:asm-analysis:9.9=androidLintTool +org.ow2.asm:asm-commons:9.9=androidJacocoAnt,androidLintTool +org.ow2.asm:asm-tree:9.9=androidJacocoAnt,androidLintTool +org.ow2.asm:asm:9.9=androidJacocoAnt,androidLintTool +empty=androidApis,androidJdkImage,androidTestUtil,coreLibraryDesugaring,debugAndroidTestAnnotationProcessorClasspath,debugAnnotationProcessorClasspath,debugReverseMetadataValues,debugUnitTestAnnotationProcessorClasspath,lintChecks,lintPublish,releaseAnnotationProcessorClasspath,releaseReverseMetadataValues diff --git a/apps/mobile/android/app/proguard-rules.pro b/apps/mobile/android/app/proguard-rules.pro new file mode 100644 index 0000000..f1b4245 --- /dev/null +++ b/apps/mobile/android/app/proguard-rules.pro @@ -0,0 +1,21 @@ +# Add project specific ProGuard rules here. +# You can control the set of applied configuration files using the +# proguardFiles setting in build.gradle. +# +# For more details, see +# http://developer.android.com/guide/developing/tools/proguard.html + +# If your project uses WebView with JS, uncomment the following +# and specify the fully qualified class name to the JavaScript interface +# class: +#-keepclassmembers class fqcn.of.javascript.interface.for.webview { +# public *; +#} + +# Uncomment this to preserve the line number information for +# debugging stack traces. +#-keepattributes SourceFile,LineNumberTable + +# If you keep the line number information, uncomment this to +# hide the original source file name. +#-renamesourcefileattribute SourceFile diff --git a/apps/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java b/apps/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java new file mode 100644 index 0000000..f2c2217 --- /dev/null +++ b/apps/mobile/android/app/src/androidTest/java/com/getcapacitor/myapp/ExampleInstrumentedTest.java @@ -0,0 +1,26 @@ +package com.getcapacitor.myapp; + +import static org.junit.Assert.*; + +import android.content.Context; +import androidx.test.ext.junit.runners.AndroidJUnit4; +import androidx.test.platform.app.InstrumentationRegistry; +import org.junit.Test; +import org.junit.runner.RunWith; + +/** + * Instrumented test, which will execute on an Android device. + * + * @see Testing documentation + */ +@RunWith(AndroidJUnit4.class) +public class ExampleInstrumentedTest { + + @Test + public void useAppContext() throws Exception { + // Context of the app under test. + Context appContext = InstrumentationRegistry.getInstrumentation().getTargetContext(); + + assertEquals("com.getcapacitor.app", appContext.getPackageName()); + } +} diff --git a/apps/mobile/android/app/src/main/AndroidManifest.xml b/apps/mobile/android/app/src/main/AndroidManifest.xml new file mode 100644 index 0000000..340e7df --- /dev/null +++ b/apps/mobile/android/app/src/main/AndroidManifest.xml @@ -0,0 +1,41 @@ + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/mobile/android/app/src/main/java/com/kazerlelutin/bento/MainActivity.java b/apps/mobile/android/app/src/main/java/com/kazerlelutin/bento/MainActivity.java new file mode 100644 index 0000000..9b4e865 --- /dev/null +++ b/apps/mobile/android/app/src/main/java/com/kazerlelutin/bento/MainActivity.java @@ -0,0 +1,5 @@ +package com.kazerlelutin.bento; + +import com.getcapacitor.BridgeActivity; + +public class MainActivity extends BridgeActivity {} diff --git a/apps/mobile/android/app/src/main/res/drawable-land-hdpi/splash.png b/apps/mobile/android/app/src/main/res/drawable-land-hdpi/splash.png new file mode 100644 index 0000000..e31573b Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable-land-hdpi/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/drawable-land-mdpi/splash.png b/apps/mobile/android/app/src/main/res/drawable-land-mdpi/splash.png new file mode 100644 index 0000000..f7a6492 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable-land-mdpi/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/drawable-land-xhdpi/splash.png b/apps/mobile/android/app/src/main/res/drawable-land-xhdpi/splash.png new file mode 100644 index 0000000..8077255 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable-land-xhdpi/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/drawable-land-xxhdpi/splash.png b/apps/mobile/android/app/src/main/res/drawable-land-xxhdpi/splash.png new file mode 100644 index 0000000..14c6c8f Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable-land-xxhdpi/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/drawable-land-xxxhdpi/splash.png b/apps/mobile/android/app/src/main/res/drawable-land-xxxhdpi/splash.png new file mode 100644 index 0000000..244ca25 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable-land-xxxhdpi/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/drawable-port-hdpi/splash.png b/apps/mobile/android/app/src/main/res/drawable-port-hdpi/splash.png new file mode 100644 index 0000000..74faaa5 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable-port-hdpi/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/drawable-port-mdpi/splash.png b/apps/mobile/android/app/src/main/res/drawable-port-mdpi/splash.png new file mode 100644 index 0000000..e944f4a Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable-port-mdpi/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/drawable-port-xhdpi/splash.png b/apps/mobile/android/app/src/main/res/drawable-port-xhdpi/splash.png new file mode 100644 index 0000000..564a82f Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable-port-xhdpi/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/drawable-port-xxhdpi/splash.png b/apps/mobile/android/app/src/main/res/drawable-port-xxhdpi/splash.png new file mode 100644 index 0000000..bfabe68 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable-port-xxhdpi/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/drawable-port-xxxhdpi/splash.png b/apps/mobile/android/app/src/main/res/drawable-port-xxxhdpi/splash.png new file mode 100644 index 0000000..6929071 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable-port-xxxhdpi/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml b/apps/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml new file mode 100644 index 0000000..c7bd21d --- /dev/null +++ b/apps/mobile/android/app/src/main/res/drawable-v24/ic_launcher_foreground.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + diff --git a/apps/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml b/apps/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml new file mode 100644 index 0000000..d5fccc5 --- /dev/null +++ b/apps/mobile/android/app/src/main/res/drawable/ic_launcher_background.xml @@ -0,0 +1,170 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/apps/mobile/android/app/src/main/res/drawable/splash.png b/apps/mobile/android/app/src/main/res/drawable/splash.png new file mode 100644 index 0000000..f7a6492 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/drawable/splash.png differ diff --git a/apps/mobile/android/app/src/main/res/layout/activity_main.xml b/apps/mobile/android/app/src/main/res/layout/activity_main.xml new file mode 100644 index 0000000..b5ad138 --- /dev/null +++ b/apps/mobile/android/app/src/main/res/layout/activity_main.xml @@ -0,0 +1,12 @@ + + + + + diff --git a/apps/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml b/apps/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/apps/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml b/apps/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml new file mode 100644 index 0000000..036d09b --- /dev/null +++ b/apps/mobile/android/app/src/main/res/mipmap-anydpi-v26/ic_launcher_round.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png b/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png new file mode 100644 index 0000000..c023e50 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png b/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..2127973 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_foreground.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png b/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png new file mode 100644 index 0000000..b441f37 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-hdpi/ic_launcher_round.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png b/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png new file mode 100644 index 0000000..72905b8 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png b/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..8ed0605 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_foreground.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png b/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png new file mode 100644 index 0000000..9502e47 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-mdpi/ic_launcher_round.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png b/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png new file mode 100644 index 0000000..4d1e077 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png b/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..df0f158 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_foreground.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png b/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png new file mode 100644 index 0000000..853db04 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-xhdpi/ic_launcher_round.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png b/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png new file mode 100644 index 0000000..6cdf97c Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png b/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..2960cbb Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_foreground.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png b/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..8e3093a Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-xxhdpi/ic_launcher_round.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png b/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png new file mode 100644 index 0000000..46de6e2 Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png b/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png new file mode 100644 index 0000000..d2ea9ab Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_foreground.png differ diff --git a/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png b/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png new file mode 100644 index 0000000..a40d73e Binary files /dev/null and b/apps/mobile/android/app/src/main/res/mipmap-xxxhdpi/ic_launcher_round.png differ diff --git a/apps/mobile/android/app/src/main/res/values/ic_launcher_background.xml b/apps/mobile/android/app/src/main/res/values/ic_launcher_background.xml new file mode 100644 index 0000000..c5d5899 --- /dev/null +++ b/apps/mobile/android/app/src/main/res/values/ic_launcher_background.xml @@ -0,0 +1,4 @@ + + + #FFFFFF + \ No newline at end of file diff --git a/apps/mobile/android/app/src/main/res/values/strings.xml b/apps/mobile/android/app/src/main/res/values/strings.xml new file mode 100644 index 0000000..640223f --- /dev/null +++ b/apps/mobile/android/app/src/main/res/values/strings.xml @@ -0,0 +1,7 @@ + + + Ben-to + Ben-to + com.kazerlelutin.bento + com.kazerlelutin.bento + diff --git a/apps/mobile/android/app/src/main/res/values/styles.xml b/apps/mobile/android/app/src/main/res/values/styles.xml new file mode 100644 index 0000000..be874e5 --- /dev/null +++ b/apps/mobile/android/app/src/main/res/values/styles.xml @@ -0,0 +1,22 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/apps/mobile/android/app/src/main/res/xml/file_paths.xml b/apps/mobile/android/app/src/main/res/xml/file_paths.xml new file mode 100644 index 0000000..bd0c4d8 --- /dev/null +++ b/apps/mobile/android/app/src/main/res/xml/file_paths.xml @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/apps/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java b/apps/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java new file mode 100644 index 0000000..0297327 --- /dev/null +++ b/apps/mobile/android/app/src/test/java/com/getcapacitor/myapp/ExampleUnitTest.java @@ -0,0 +1,18 @@ +package com.getcapacitor.myapp; + +import static org.junit.Assert.*; + +import org.junit.Test; + +/** + * Example local unit test, which will execute on the development machine (host). + * + * @see Testing documentation + */ +public class ExampleUnitTest { + + @Test + public void addition_isCorrect() throws Exception { + assertEquals(4, 2 + 2); + } +} diff --git a/apps/mobile/android/build.gradle b/apps/mobile/android/build.gradle new file mode 100644 index 0000000..b8984d9 --- /dev/null +++ b/apps/mobile/android/build.gradle @@ -0,0 +1,41 @@ +// Top-level build file where you can add configuration options common to all sub-projects/modules. + +buildscript { + configurations.classpath { + resolutionStrategy.activateDependencyLocking() + } + repositories { + google() + mavenCentral() + } + dependencies { + classpath 'com.android.tools.build:gradle:9.2.0' + classpath 'com.google.gms:google-services:4.4.2' + + // NOTE: Do not place your application dependencies here; they belong + // in the individual module build.gradle files + } +} + +apply from: "variables.gradle" + +allprojects { + repositories { + google() + mavenCentral() + } +} + +// Verrouillage des dépendances sur :app uniquement (module versionné). +// Le dossier capacitor-cordova-android-plugins est ignoré par git (généré par Capacitor) : pas de lockfiles versionnés là-bas. +subprojects { sub -> + if (sub.name == 'app') { + sub.dependencyLocking { + lockAllConfigurations() + } + } +} + +task clean(type: Delete) { + delete rootProject.buildDir +} diff --git a/apps/mobile/android/buildscript-gradle.lockfile b/apps/mobile/android/buildscript-gradle.lockfile new file mode 100644 index 0000000..bd3a02b --- /dev/null +++ b/apps/mobile/android/buildscript-gradle.lockfile @@ -0,0 +1,118 @@ +# This is a Gradle generated file for dependency locking. +# Manual edits can break the build and are not advised. +# This file is expected to be part of source control. +androidx.databinding:databinding-common:9.2.0=classpath +androidx.databinding:databinding-compiler-common:9.2.0=classpath +com.android.databinding:baseLibrary:9.2.0=classpath +com.android.tools.analytics-library:crash:32.2.0=classpath +com.android.tools.analytics-library:protos:32.2.0=classpath +com.android.tools.analytics-library:shared:32.2.0=classpath +com.android.tools.analytics-library:tracker:32.2.0=classpath +com.android.tools.build.jetifier:jetifier-core:1.0.0-beta10=classpath +com.android.tools.build.jetifier:jetifier-processor:1.0.0-beta10=classpath +com.android.tools.build:aapt2-proto:9.2.0-15009934=classpath +com.android.tools.build:aaptcompiler:9.2.0=classpath +com.android.tools.build:apksig:9.2.0=classpath +com.android.tools.build:apkzlib:9.2.0=classpath +com.android.tools.build:builder-model:9.2.0=classpath +com.android.tools.build:builder-test-api:9.2.0=classpath +com.android.tools.build:builder:9.2.0=classpath +com.android.tools.build:bundletool:1.18.3=classpath +com.android.tools.build:gradle-api:9.2.0=classpath +com.android.tools.build:gradle-common-api:9.2.0=classpath +com.android.tools.build:gradle-settings-api:9.2.0=classpath +com.android.tools.build:gradle:9.2.0=classpath +com.android.tools.build:manifest-merger:32.2.0=classpath +com.android.tools.ddms:ddmlib:32.2.0=classpath +com.android.tools.layoutlib:layoutlib-api:32.2.0=classpath +com.android.tools.lint:lint-model:32.2.0=classpath +com.android.tools.lint:lint-typedef-remover:32.2.0=classpath +com.android.tools.utp:gradle-work-action-api:32.2.0=classpath +com.android.tools:annotations:32.2.0=classpath +com.android.tools:common:32.2.0=classpath +com.android.tools:dvlib:32.2.0=classpath +com.android.tools:repository:32.2.0=classpath +com.android.tools:sdk-common:32.2.0=classpath +com.android.tools:sdklib:32.2.0=classpath +com.android:signflinger:9.2.0=classpath +com.android:zipflinger:9.2.0=classpath +com.google.android.gms:strict-version-matcher-plugin:1.2.4=classpath +com.google.auto.value:auto-value-annotations:1.6.2=classpath +com.google.code.findbugs:jsr305:3.0.2=classpath +com.google.code.gson:gson:2.11.0=classpath +com.google.crypto.tink:tink:1.7.0=classpath +com.google.dagger:dagger:2.28.3=classpath +com.google.errorprone:error_prone_annotations:2.28.0=classpath +com.google.flatbuffers:flatbuffers-java:1.12.0=classpath +com.google.gms:google-services:4.4.2=classpath +com.google.guava:failureaccess:1.0.2=classpath +com.google.guava:guava:33.3.1-jre=classpath +com.google.guava:listenablefuture:9999.0-empty-to-avoid-conflict-with-guava=classpath +com.google.j2objc:j2objc-annotations:3.0.0=classpath +com.google.jimfs:jimfs:1.1=classpath +com.google.protobuf:protobuf-java-util:3.25.5=classpath +com.google.protobuf:protobuf-java:3.25.5=classpath +com.googlecode.juniversalchardet:juniversalchardet:1.0.3=classpath +com.squareup:javapoet:1.13.0=classpath +com.squareup:javawriter:2.5.0=classpath +com.sun.istack:istack-commons-runtime:3.0.8=classpath +com.sun.xml.fastinfoset:FastInfoset:1.2.16=classpath +commons-codec:commons-codec:1.17.1=classpath +commons-io:commons-io:2.16.1=classpath +commons-logging:commons-logging:1.2=classpath +jakarta.activation:jakarta.activation-api:1.2.1=classpath +jakarta.xml.bind:jakarta.xml.bind-api:2.3.2=classpath +javax.inject:javax.inject:1=classpath +net.java.dev.jna:jna-platform:5.6.0=classpath +net.java.dev.jna:jna:5.6.0=classpath +net.sf.jopt-simple:jopt-simple:4.9=classpath +net.sf.kxml:kxml2:2.3.0=classpath +org.apache.commons:commons-compress:1.27.1=classpath +org.apache.commons:commons-lang3:3.16.0=classpath +org.apache.httpcomponents:httpclient:4.5.14=classpath +org.apache.httpcomponents:httpcore:4.4.16=classpath +org.apache.httpcomponents:httpmime:4.5.6=classpath +org.bitbucket.b_c:jose4j:0.9.5=classpath +org.bouncycastle:bcpkix-jdk18on:1.79=classpath +org.bouncycastle:bcprov-jdk18on:1.79=classpath +org.bouncycastle:bcutil-jdk18on:1.79=classpath +org.checkerframework:checker-qual:3.43.0=classpath +org.glassfish.jaxb:jaxb-runtime:2.3.2=classpath +org.glassfish.jaxb:txw2:2.3.2=classpath +org.jdom:jdom2:2.0.6=classpath +org.jetbrains.kotlin:abi-tools-api:2.2.10=classpath +org.jetbrains.kotlin:fus-statistics-gradle-plugin:2.2.10=classpath +org.jetbrains.kotlin:kotlin-build-statistics:2.2.10=classpath +org.jetbrains.kotlin:kotlin-build-tools-api:2.2.10=classpath +org.jetbrains.kotlin:kotlin-compiler-runner:2.2.10=classpath +org.jetbrains.kotlin:kotlin-daemon-client:2.2.10=classpath +org.jetbrains.kotlin:kotlin-gradle-plugin-annotations:2.2.10=classpath +org.jetbrains.kotlin:kotlin-gradle-plugin-api:2.2.10=classpath +org.jetbrains.kotlin:kotlin-gradle-plugin-idea-proto:2.2.10=classpath +org.jetbrains.kotlin:kotlin-gradle-plugin-idea:2.2.10=classpath +org.jetbrains.kotlin:kotlin-gradle-plugin-model:2.2.10=classpath +org.jetbrains.kotlin:kotlin-gradle-plugin:2.2.10=classpath +org.jetbrains.kotlin:kotlin-gradle-plugins-bom:2.2.10=classpath +org.jetbrains.kotlin:kotlin-klib-commonizer-api:2.2.10=classpath +org.jetbrains.kotlin:kotlin-native-utils:2.2.10=classpath +org.jetbrains.kotlin:kotlin-reflect:2.2.10=classpath +org.jetbrains.kotlin:kotlin-stdlib-jdk7:2.2.10=classpath +org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.10=classpath +org.jetbrains.kotlin:kotlin-stdlib:2.2.10=classpath +org.jetbrains.kotlin:kotlin-tooling-core:2.2.10=classpath +org.jetbrains.kotlin:kotlin-util-io:2.2.10=classpath +org.jetbrains.kotlin:kotlin-util-klib-metadata:2.2.10=classpath +org.jetbrains.kotlin:kotlin-util-klib:2.2.10=classpath +org.jetbrains.kotlinx:kotlinx-coroutines-bom:1.9.0=classpath +org.jetbrains.kotlinx:kotlinx-coroutines-core-jvm:1.9.0=classpath +org.jetbrains.kotlinx:kotlinx-coroutines-core:1.9.0=classpath +org.jetbrains:annotations:23.0.0=classpath +org.jvnet.staxex:stax-ex:1.8.1=classpath +org.ow2.asm:asm-analysis:9.9=classpath +org.ow2.asm:asm-commons:9.9=classpath +org.ow2.asm:asm-tree:9.9=classpath +org.ow2.asm:asm-util:9.9=classpath +org.ow2.asm:asm:9.9=classpath +org.slf4j:slf4j-api:1.7.30=classpath +org.tensorflow:tensorflow-lite-metadata:0.2.0=classpath +empty= diff --git a/apps/mobile/android/capacitor.settings.gradle b/apps/mobile/android/capacitor.settings.gradle new file mode 100644 index 0000000..fb8c2fd --- /dev/null +++ b/apps/mobile/android/capacitor.settings.gradle @@ -0,0 +1,9 @@ +// DO NOT EDIT THIS FILE! IT IS GENERATED EACH TIME "capacitor update" IS RUN +include ':capacitor-android' +project(':capacitor-android').projectDir = new File('../../../node_modules/@capacitor/android/capacitor') + +include ':capacitor-share' +project(':capacitor-share').projectDir = new File('../../../node_modules/@capacitor/share/android') + +include ':capacitor-community-sqlite' +project(':capacitor-community-sqlite').projectDir = new File('../../../node_modules/@capacitor-community/sqlite/android') diff --git a/apps/mobile/android/gradle.properties b/apps/mobile/android/gradle.properties new file mode 100644 index 0000000..65681bf --- /dev/null +++ b/apps/mobile/android/gradle.properties @@ -0,0 +1,32 @@ +# Project-wide Gradle settings. + +# IDE (e.g. Android Studio) users: +# Gradle settings configured through the IDE *will override* +# any settings specified in this file. + +# For more details on how to configure your build environment visit +# http://www.gradle.org/docs/current/userguide/build_environment.html + +# Specifies the JVM arguments used for the daemon process. +# The setting is particularly useful for tweaking memory settings. +org.gradle.jvmargs=-Xmx1536m + +# When configured, Gradle will run in incubating parallel mode. +# This option should only be used with decoupled projects. More details, visit +# http://www.gradle.org/docs/current/userguide/multi_project_builds.html#sec:decoupled_projects +# org.gradle.parallel=true + +# AndroidX package structure to make it clearer which packages are bundled with the +# Android operating system, and which are packaged with your app's APK +# https://developer.android.com/topic/libraries/support-library/androidx-rn +android.useAndroidX=true +android.defaults.buildfeatures.resvalues=true +android.sdk.defaultTargetSdkToCompileSdkIfUnset=false +android.enableAppCompileTimeRClass=false +android.usesSdkInManifest.disallowed=false +android.uniquePackageNames=false +android.dependency.useConstraints=true +android.r8.strictFullModeForKeepRules=false +android.r8.optimizedResourceShrinking=false +android.builtInKotlin=false +android.newDsl=false diff --git a/apps/mobile/android/gradle/wrapper/gradle-wrapper.jar b/apps/mobile/android/gradle/wrapper/gradle-wrapper.jar new file mode 100644 index 0000000..a4b76b9 Binary files /dev/null and b/apps/mobile/android/gradle/wrapper/gradle-wrapper.jar differ diff --git a/apps/mobile/android/gradle/wrapper/gradle-wrapper.properties b/apps/mobile/android/gradle/wrapper/gradle-wrapper.properties new file mode 100644 index 0000000..c82ad3f --- /dev/null +++ b/apps/mobile/android/gradle/wrapper/gradle-wrapper.properties @@ -0,0 +1,7 @@ +distributionBase=GRADLE_USER_HOME +distributionPath=wrapper/dists +distributionUrl=https\://services.gradle.org/distributions/gradle-9.4.1-all.zip +networkTimeout=10000 +validateDistributionUrl=true +zipStoreBase=GRADLE_USER_HOME +zipStorePath=wrapper/dists diff --git a/apps/mobile/android/gradlew b/apps/mobile/android/gradlew new file mode 100755 index 0000000..f5feea6 --- /dev/null +++ b/apps/mobile/android/gradlew @@ -0,0 +1,252 @@ +#!/bin/sh + +# +# Copyright © 2015-2021 the original authors. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# https://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# SPDX-License-Identifier: Apache-2.0 +# + +############################################################################## +# +# Gradle start up script for POSIX generated by Gradle. +# +# Important for running: +# +# (1) You need a POSIX-compliant shell to run this script. If your /bin/sh is +# noncompliant, but you have some other compliant shell such as ksh or +# bash, then to run this script, type that shell name before the whole +# command line, like: +# +# ksh Gradle +# +# Busybox and similar reduced shells will NOT work, because this script +# requires all of these POSIX shell features: +# * functions; +# * expansions «$var», «${var}», «${var:-default}», «${var+SET}», +# «${var#prefix}», «${var%suffix}», and «$( cmd )»; +# * compound commands having a testable exit status, especially «case»; +# * various built-in commands including «command», «set», and «ulimit». +# +# Important for patching: +# +# (2) This script targets any POSIX shell, so it avoids extensions provided +# by Bash, Ksh, etc; in particular arrays are avoided. +# +# The "traditional" practice of packing multiple parameters into a +# space-separated string is a well documented source of bugs and security +# problems, so this is (mostly) avoided, by progressively accumulating +# options in "$@", and eventually passing that to Java. +# +# Where the inherited environment variables (DEFAULT_JVM_OPTS, JAVA_OPTS, +# and GRADLE_OPTS) rely on word-splitting, this is performed explicitly; +# see the in-line comments for details. +# +# There are tweaks for specific operating systems such as AIX, CygWin, +# Darwin, MinGW, and NonStop. +# +# (3) This script is generated from the Groovy template +# https://github.com/gradle/gradle/blob/HEAD/platforms/jvm/plugins-application/src/main/resources/org/gradle/api/internal/plugins/unixStartScript.txt +# within the Gradle project. +# +# You can find Gradle at https://github.com/gradle/gradle/. +# +############################################################################## + +# Attempt to set APP_HOME + +# Resolve links: $0 may be a link +app_path=$0 + +# Need this for daisy-chained symlinks. +while + APP_HOME=${app_path%"${app_path##*/}"} # leaves a trailing /; empty if no leading path + [ -h "$app_path" ] +do + ls=$( ls -ld "$app_path" ) + link=${ls#*' -> '} + case $link in #( + /*) app_path=$link ;; #( + *) app_path=$APP_HOME$link ;; + esac +done + +# This is normally unused +# shellcheck disable=SC2034 +APP_BASE_NAME=${0##*/} +# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) +APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s +' "$PWD" ) || exit + +# Use the maximum available, or set MAX_FD != -1 to use that value. +MAX_FD=maximum + +warn () { + echo "$*" +} >&2 + +die () { + echo + echo "$*" + echo + exit 1 +} >&2 + +# OS specific support (must be 'true' or 'false'). +cygwin=false +msys=false +darwin=false +nonstop=false +case "$( uname )" in #( + CYGWIN* ) cygwin=true ;; #( + Darwin* ) darwin=true ;; #( + MSYS* | MINGW* ) msys=true ;; #( + NONSTOP* ) nonstop=true ;; +esac + +CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar + + +# Determine the Java command to use to start the JVM. +if [ -n "$JAVA_HOME" ] ; then + if [ -x "$JAVA_HOME/jre/sh/java" ] ; then + # IBM's JDK on AIX uses strange locations for the executables + JAVACMD=$JAVA_HOME/jre/sh/java + else + JAVACMD=$JAVA_HOME/bin/java + fi + if [ ! -x "$JAVACMD" ] ; then + die "ERROR: JAVA_HOME is set to an invalid directory: $JAVA_HOME + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +else + JAVACMD=java + if ! command -v java >/dev/null 2>&1 + then + die "ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. + +Please set the JAVA_HOME variable in your environment to match the +location of your Java installation." + fi +fi + +# Increase the maximum file descriptors if we can. +if ! "$cygwin" && ! "$darwin" && ! "$nonstop" ; then + case $MAX_FD in #( + max*) + # In POSIX sh, ulimit -H is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + MAX_FD=$( ulimit -H -n ) || + warn "Could not query maximum file descriptor limit" + esac + case $MAX_FD in #( + '' | soft) :;; #( + *) + # In POSIX sh, ulimit -n is undefined. That's why the result is checked to see if it worked. + # shellcheck disable=SC2039,SC3045 + ulimit -n "$MAX_FD" || + warn "Could not set maximum file descriptor limit to $MAX_FD" + esac +fi + +# Collect all arguments for the java command, stacking in reverse order: +# * args from the command line +# * the main class name +# * -classpath +# * -D...appname settings +# * --module-path (only if needed) +# * DEFAULT_JVM_OPTS, JAVA_OPTS, and GRADLE_OPTS environment variables. + +# For Cygwin or MSYS, switch paths to Windows format before running java +if "$cygwin" || "$msys" ; then + APP_HOME=$( cygpath --path --mixed "$APP_HOME" ) + CLASSPATH=$( cygpath --path --mixed "$CLASSPATH" ) + + JAVACMD=$( cygpath --unix "$JAVACMD" ) + + # Now convert the arguments - kludge to limit ourselves to /bin/sh + for arg do + if + case $arg in #( + -*) false ;; # don't mess with options #( + /?*) t=${arg#/} t=/${t%%/*} # looks like a POSIX filepath + [ -e "$t" ] ;; #( + *) false ;; + esac + then + arg=$( cygpath --path --ignore --mixed "$arg" ) + fi + # Roll the args list around exactly as many times as the number of + # args, so each arg winds up back in the position where it started, but + # possibly modified. + # + # NB: a `for` loop captures its iteration list before it begins, so + # changing the positional parameters here affects neither the number of + # iterations, nor the values presented in `arg`. + shift # remove old arg + set -- "$@" "$arg" # push replacement arg + done +fi + + +# Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' + +# Collect all arguments for the java command: +# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, +# and any embedded shellness will be escaped. +# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be +# treated as '${Hostname}' itself on the command line. + +set -- \ + "-Dorg.gradle.appname=$APP_BASE_NAME" \ + -classpath "$CLASSPATH" \ + org.gradle.wrapper.GradleWrapperMain \ + "$@" + +# Stop when "xargs" is not available. +if ! command -v xargs >/dev/null 2>&1 +then + die "xargs is not available" +fi + +# Use "xargs" to parse quoted args. +# +# With -n1 it outputs one arg per line, with the quotes and backslashes removed. +# +# In Bash we could simply go: +# +# readarray ARGS < <( xargs -n1 <<<"$var" ) && +# set -- "${ARGS[@]}" "$@" +# +# but POSIX shell has neither arrays nor command substitution, so instead we +# post-process each arg (as a line of input to sed) to backslash-escape any +# character that might be a shell metacharacter, then use eval to reverse +# that process (while maintaining the separation between arguments), and wrap +# the whole thing up as a single "set" statement. +# +# This will of course break if any of these variables contains a newline or +# an unmatched quote. +# + +eval "set -- $( + printf '%s\n' "$DEFAULT_JVM_OPTS $JAVA_OPTS $GRADLE_OPTS" | + xargs -n1 | + sed ' s~[^-[:alnum:]+,./:=@_]~\\&~g; ' | + tr '\n' ' ' + )" '"$@"' + +exec "$JAVACMD" "$@" diff --git a/apps/mobile/android/gradlew.bat b/apps/mobile/android/gradlew.bat new file mode 100644 index 0000000..9b42019 --- /dev/null +++ b/apps/mobile/android/gradlew.bat @@ -0,0 +1,94 @@ +@rem +@rem Copyright 2015 the original author or authors. +@rem +@rem Licensed under the Apache License, Version 2.0 (the "License"); +@rem you may not use this file except in compliance with the License. +@rem You may obtain a copy of the License at +@rem +@rem https://www.apache.org/licenses/LICENSE-2.0 +@rem +@rem Unless required by applicable law or agreed to in writing, software +@rem distributed under the License is distributed on an "AS IS" BASIS, +@rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +@rem See the License for the specific language governing permissions and +@rem limitations under the License. +@rem +@rem SPDX-License-Identifier: Apache-2.0 +@rem + +@if "%DEBUG%"=="" @echo off +@rem ########################################################################## +@rem +@rem Gradle startup script for Windows +@rem +@rem ########################################################################## + +@rem Set local scope for the variables with windows NT shell +if "%OS%"=="Windows_NT" setlocal + +set DIRNAME=%~dp0 +if "%DIRNAME%"=="" set DIRNAME=. +@rem This is normally unused +set APP_BASE_NAME=%~n0 +set APP_HOME=%DIRNAME% + +@rem Resolve any "." and ".." in APP_HOME to make it shorter. +for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi + +@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. +set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" + +@rem Find java.exe +if defined JAVA_HOME goto findJavaFromJavaHome + +set JAVA_EXE=java.exe +%JAVA_EXE% -version >NUL 2>&1 +if %ERRORLEVEL% equ 0 goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:findJavaFromJavaHome +set JAVA_HOME=%JAVA_HOME:"=% +set JAVA_EXE=%JAVA_HOME%/bin/java.exe + +if exist "%JAVA_EXE%" goto execute + +echo. 1>&2 +echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 1>&2 +echo. 1>&2 +echo Please set the JAVA_HOME variable in your environment to match the 1>&2 +echo location of your Java installation. 1>&2 + +goto fail + +:execute +@rem Setup the command line + +set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar + + +@rem Execute Gradle +"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* + +:end +@rem End local scope for the variables with windows NT shell +if %ERRORLEVEL% equ 0 goto mainEnd + +:fail +rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of +rem the _cmd.exe /c_ return code! +set EXIT_CODE=%ERRORLEVEL% +if %EXIT_CODE% equ 0 set EXIT_CODE=1 +if not ""=="%GRADLE_EXIT_CONSOLE%" exit %EXIT_CODE% +exit /b %EXIT_CODE% + +:mainEnd +if "%OS%"=="Windows_NT" endlocal + +:omega diff --git a/apps/mobile/android/keystore.properties.example b/apps/mobile/android/keystore.properties.example new file mode 100644 index 0000000..a72cd5c --- /dev/null +++ b/apps/mobile/android/keystore.properties.example @@ -0,0 +1,7 @@ +# Copier vers keystore.properties (non versionné) à côté de ce fichier. +# Génération du magasin : keytool -genkey -v -keystore release.keystore -alias BentoRelease -keyalg RSA -keysize 2048 -validity 10000 +# +storeFile=release.keystore +storePassword= +keyAlias=BentoRelease +keyPassword= diff --git a/apps/mobile/android/settings.gradle b/apps/mobile/android/settings.gradle new file mode 100644 index 0000000..3b4431d --- /dev/null +++ b/apps/mobile/android/settings.gradle @@ -0,0 +1,5 @@ +include ':app' +include ':capacitor-cordova-android-plugins' +project(':capacitor-cordova-android-plugins').projectDir = new File('./capacitor-cordova-android-plugins/') + +apply from: 'capacitor.settings.gradle' \ No newline at end of file diff --git a/apps/mobile/android/variables.gradle b/apps/mobile/android/variables.gradle new file mode 100644 index 0000000..2c8e408 --- /dev/null +++ b/apps/mobile/android/variables.gradle @@ -0,0 +1,16 @@ +ext { + minSdkVersion = 23 + compileSdkVersion = 35 + targetSdkVersion = 35 + androidxActivityVersion = '1.9.2' + androidxAppCompatVersion = '1.7.0' + androidxCoordinatorLayoutVersion = '1.2.0' + androidxCoreVersion = '1.15.0' + androidxFragmentVersion = '1.8.4' + coreSplashScreenVersion = '1.0.1' + androidxWebkitVersion = '1.12.1' + junitVersion = '4.13.2' + androidxJunitVersion = '1.2.1' + androidxEspressoCoreVersion = '3.6.1' + cordovaAndroidVersion = '10.1.1' +} \ No newline at end of file diff --git a/apps/mobile/capacitor.config.ts b/apps/mobile/capacitor.config.ts new file mode 100644 index 0000000..fe4d538 --- /dev/null +++ b/apps/mobile/capacitor.config.ts @@ -0,0 +1,12 @@ +import type { CapacitorConfig } from "@capacitor/cli"; + +const config: CapacitorConfig = { + appId: "com.kazerlelutin.bento", + appName: "Bento", + webDir: "../web/dist", + server: { + androidScheme: "https" + } +}; + +export default config; diff --git a/apps/mobile/package.json b/apps/mobile/package.json new file mode 100644 index 0000000..bedf805 --- /dev/null +++ b/apps/mobile/package.json @@ -0,0 +1,21 @@ +{ + "name": "@ben-to/mobile", + "version": "0.1.0", + "private": true, + "scripts": { + "sync": "npx cap sync", + "open:android": "npx cap open android", + "open:ios": "npx cap open ios", + "run:android": "npx cap run android" + }, + "dependencies": { + "@capacitor/android": "^8.4.1", + "@capacitor/core": "^8.4.1", + "@capacitor/ios": "^8.4.1", + "@capacitor/share": "^8.0.1", + "@capacitor-community/sqlite": "^8.1.0" + }, + "devDependencies": { + "@capacitor/cli": "^8.4.1" + } +} diff --git a/apps/web/.env.example b/apps/web/.env.example new file mode 100644 index 0000000..c40542a --- /dev/null +++ b/apps/web/.env.example @@ -0,0 +1,4 @@ +# Optionnel — Matomo (mesure d’audience). Les deux variables sont requises pour activer +# le tracker et le bandeau de consentement cookies. +# VITE_MATOMO_URL=https://analytics.example.com/ +# VITE_MATOMO_SITE_ID=1 diff --git a/apps/web/index.html b/apps/web/index.html new file mode 100644 index 0000000..db3f383 --- /dev/null +++ b/apps/web/index.html @@ -0,0 +1,39 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + Bento + + + +
+ + + + diff --git a/apps/web/package.json b/apps/web/package.json new file mode 100644 index 0000000..1aeb6f8 --- /dev/null +++ b/apps/web/package.json @@ -0,0 +1,30 @@ +{ + "name": "@ben-to/web", + "version": "0.1.0", + "private": true, + "type": "module", + "scripts": { + "predev": "node ../../scripts/build-recipes.mjs", + "dev": "vite", + "prebuild": "node ../../scripts/build-recipes.mjs", + "build": "vite build", + "preview": "vite preview", + "lint": "eslint src --ext .ts,.tsx", + "typecheck": "tsc -p tsconfig.json --noEmit" + }, + "dependencies": { + "@ben-to/builder": "0.1.0", + "@ben-to/i18n": "0.1.0", + "@ben-to/privacy": "0.1.0", + "@ben-to/recipes": "0.1.0", + "@capacitor/core": "^8.4.1", + "@capacitor/share": "^8.0.1", + "@solidjs/router": "^0.16.1", + "solid-js": "^1.9.14" + }, + "devDependencies": { + "typescript": "^6.0.3", + "vite": "^8.1.4", + "vite-plugin-solid": "^2.11.8" + } +} diff --git a/apps/web/public/fonts/cause/Cause-VariableFont_wght.ttf b/apps/web/public/fonts/cause/Cause-VariableFont_wght.ttf new file mode 100644 index 0000000..0bda93e Binary files /dev/null and b/apps/web/public/fonts/cause/Cause-VariableFont_wght.ttf differ diff --git a/apps/web/public/fonts/cause/OFL.txt b/apps/web/public/fonts/cause/OFL.txt new file mode 100644 index 0000000..7560aef --- /dev/null +++ b/apps/web/public/fonts/cause/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2025 The Cause Project Authors (https://github.com/xconsau/Cause) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/apps/web/public/fonts/gamja-flower/GamjaFlower-Regular.ttf b/apps/web/public/fonts/gamja-flower/GamjaFlower-Regular.ttf new file mode 100644 index 0000000..a5da794 Binary files /dev/null and b/apps/web/public/fonts/gamja-flower/GamjaFlower-Regular.ttf differ diff --git a/apps/web/public/fonts/gamja-flower/OFL.txt b/apps/web/public/fonts/gamja-flower/OFL.txt new file mode 100644 index 0000000..031e5f7 --- /dev/null +++ b/apps/web/public/fonts/gamja-flower/OFL.txt @@ -0,0 +1,93 @@ +Copyright (c) YoonDesign Inc. All Rights Reserved. + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/apps/web/public/fonts/klee-one/KleeOne-Regular.ttf b/apps/web/public/fonts/klee-one/KleeOne-Regular.ttf new file mode 100644 index 0000000..62f77f7 Binary files /dev/null and b/apps/web/public/fonts/klee-one/KleeOne-Regular.ttf differ diff --git a/apps/web/public/fonts/klee-one/KleeOne-SemiBold.ttf b/apps/web/public/fonts/klee-one/KleeOne-SemiBold.ttf new file mode 100644 index 0000000..53b8b3c Binary files /dev/null and b/apps/web/public/fonts/klee-one/KleeOne-SemiBold.ttf differ diff --git a/apps/web/public/fonts/klee-one/OFL.txt b/apps/web/public/fonts/klee-one/OFL.txt new file mode 100644 index 0000000..7052312 --- /dev/null +++ b/apps/web/public/fonts/klee-one/OFL.txt @@ -0,0 +1,93 @@ +Copyright 2020 The Klee Project Authors (https://github.com/fontworks-fonts/Klee) + +This Font Software is licensed under the SIL Open Font License, Version 1.1. +This license is copied below, and is also available with a FAQ at: +https://openfontlicense.org + + +----------------------------------------------------------- +SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007 +----------------------------------------------------------- + +PREAMBLE +The goals of the Open Font License (OFL) are to stimulate worldwide +development of collaborative font projects, to support the font creation +efforts of academic and linguistic communities, and to provide a free and +open framework in which fonts may be shared and improved in partnership +with others. + +The OFL allows the licensed fonts to be used, studied, modified and +redistributed freely as long as they are not sold by themselves. The +fonts, including any derivative works, can be bundled, embedded, +redistributed and/or sold with any software provided that any reserved +names are not used by derivative works. The fonts and derivatives, +however, cannot be released under any other type of license. The +requirement for fonts to remain under this license does not apply +to any document created using the fonts or their derivatives. + +DEFINITIONS +"Font Software" refers to the set of files released by the Copyright +Holder(s) under this license and clearly marked as such. This may +include source files, build scripts and documentation. + +"Reserved Font Name" refers to any names specified as such after the +copyright statement(s). + +"Original Version" refers to the collection of Font Software components as +distributed by the Copyright Holder(s). + +"Modified Version" refers to any derivative made by adding to, deleting, +or substituting -- in part or in whole -- any of the components of the +Original Version, by changing formats or by porting the Font Software to a +new environment. + +"Author" refers to any designer, engineer, programmer, technical +writer or other person who contributed to the Font Software. + +PERMISSION & CONDITIONS +Permission is hereby granted, free of charge, to any person obtaining +a copy of the Font Software, to use, study, copy, merge, embed, modify, +redistribute, and sell modified and unmodified copies of the Font +Software, subject to the following conditions: + +1) Neither the Font Software nor any of its individual components, +in Original or Modified Versions, may be sold by itself. + +2) Original or Modified Versions of the Font Software may be bundled, +redistributed and/or sold with any software, provided that each copy +contains the above copyright notice and this license. These can be +included either as stand-alone text files, human-readable headers or +in the appropriate machine-readable metadata fields within text or +binary files as long as those fields can be easily viewed by the user. + +3) No Modified Version of the Font Software may use the Reserved Font +Name(s) unless explicit written permission is granted by the corresponding +Copyright Holder. This restriction only applies to the primary font name as +presented to the users. + +4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font +Software shall not be used to promote, endorse or advertise any +Modified Version, except to acknowledge the contribution(s) of the +Copyright Holder(s) and the Author(s) or with their explicit written +permission. + +5) The Font Software, modified or unmodified, in part or in whole, +must be distributed entirely under this license, and must not be +distributed under any other license. The requirement for fonts to +remain under this license does not apply to any document created +using the Font Software. + +TERMINATION +This license becomes null and void if any of the above conditions are +not met. + +DISCLAIMER +THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT +OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE +COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, +INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL +DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM +OTHER DEALINGS IN THE FONT SOFTWARE. diff --git a/apps/web/public/ingredient-sprites.csv b/apps/web/public/ingredient-sprites.csv new file mode 100755 index 0000000..cfc47b4 --- /dev/null +++ b/apps/web/public/ingredient-sprites.csv @@ -0,0 +1,22 @@ +,assiette,fouet,baguettes,carnet,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,, +eau|vinaigre de riz|Vinaigre blanc|mirin,huile|jus de citron,jus d'orange,jus de citron vert,lait|crême fraîche liquide entière|Crème fraîche épaisse,chocolat fondu|sauce soja|aromate pistache,purée de sésame noir,moutarde forte,Chapelure panko|Chapelure,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,, +onigiri,empenadas,gimbap,radis mariné,kimchi,ramen,tofu,udon,dan bing|Omelette roulée,mandu,muffin,madelaine|madeleine,naan,bao,mandu shiitake,mandu aubergine,onigiri kimchi,blinis,cheese naan fromage,empanada-intensesalty,empanadas-butternut,empanadas-citron,madeleine-chocolat-blanc,guacamole +gateau de savoie,tranche cake,banana cake,carrot cake,cake au citron,cake à l'orange,flan,cheesecake-speculoos,speculoos,financier,financier lin,muffin au sésame noir,muffin aux pépites de chocolat,muffin chocolat blanc,muffin pistache et chocolat blanc,muffin pomme cannelle,muffin menthe et pépites de chocolat,gua bao,,,,,, +citron,orange,pomme rouge|Pomme,pomme verte,poire,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,, +betterave,carotte| carottes râpées,feuille de nori,cornichon|concombre,poivron rouge|poivron,poivron orange,poivron vert,poivron jaune,gingembre,navet,danmuji|radis jaune,aubergine|aubergines,champignon|champignons|shiitake,ail|aïl|ail frais|,radis|radis râpé,tomate|tomates,melon|melons,,,,,,, +haricots noirs,flageolets,haricots rouges,mais,epinard,courge,oignon,piment rouge,ciboulette,ciboule,piment vert,banane|bananes,Avocat|avocats|Avocados,Butternut|butternuts,Feuilles de coriandre|coriandre,Échalote|echalote,menthe|mint,,,,,,, +oeuf|oeufs|œufs|œuf,jaune d'oeuf|jaune d'œufs|jaune d'œuf,blanc d'oeuf|blanc d'œufs|blanc d'œuf,,,,,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,, +sel|bicarbonate|fleur de sel,poivre,poudre verte,poudre beige|poudre d'amande|poudre d'amandes|canelle en poudre|cannelle|cassonade|sucre brun|Levure boulangère sèche,gingembre en poudre,poudre blanche|sucre en poudre|sucre|fécule|fécule de maïs|levure chimique,poudre rouge|piment de cayenne,poudre orange,,,,,,,,,,,,,,,, +vanille,noisette|noisettes,noix,Muscade|muscade,Clous de girofle |clous de girofle|girofle,,,,,,,,,,,,,,,,,,, +pepite de chocolat au lait,pepites de chocolat noir,pepite de chocolat blanc|pepite chocolat blanc,,,,,,,,,,,,,,,,,,,,, +tablette chocolat|chocolat,tablette chocolat blanc|chocolat blanc,,,,,,,,,,,,,,,,,,,,,, +emmental,beurre|beurre doux,mozzarella,comté|gruyère,cheddar,fromage frais|yaourt grec,fromage râpé,,,,,,,,,,,,,,,,, +,,,,,,,,,,,,,,,,,,,,,,, +farine,sucre de canne,,,,,,,,,,,,,,,,,,,,,, +riz,,,,,,,,,,,,,,,,,,,,,,, +graines de sésame|graines de lin,,,,,,,,,,,,,,,,,,,,,,, +arachide|---,,,,,,,,,,,,,,,,,,,,,,, diff --git a/apps/web/public/ingredient-unknown.png b/apps/web/public/ingredient-unknown.png new file mode 100644 index 0000000..26b9092 Binary files /dev/null and b/apps/web/public/ingredient-unknown.png differ diff --git a/apps/web/public/ingredients.png b/apps/web/public/ingredients.png new file mode 100644 index 0000000..4749506 Binary files /dev/null and b/apps/web/public/ingredients.png differ diff --git a/apps/web/public/robots.txt b/apps/web/public/robots.txt new file mode 100644 index 0000000..5a3ed61 --- /dev/null +++ b/apps/web/public/robots.txt @@ -0,0 +1,4 @@ +User-agent: * +Allow: / + +Sitemap: https://ben-to.fr/sitemap.xml diff --git a/apps/web/public/sitemap.xml b/apps/web/public/sitemap.xml new file mode 100644 index 0000000..778f86e --- /dev/null +++ b/apps/web/public/sitemap.xml @@ -0,0 +1,28 @@ + + + + https://ben-to.fr/ + weekly + 1.0 + + + https://ben-to.fr/a-propos + monthly + 0.6 + + + https://ben-to.fr/cgu + yearly + 0.5 + + + https://ben-to.fr/mentions-legales + yearly + 0.5 + + + https://ben-to.fr/confidentialite + yearly + 0.5 + + diff --git a/apps/web/public/tour/flow-builder.gif b/apps/web/public/tour/flow-builder.gif new file mode 100644 index 0000000..1544b71 Binary files /dev/null and b/apps/web/public/tour/flow-builder.gif differ diff --git a/apps/web/public/tour/recipe-sheet.gif b/apps/web/public/tour/recipe-sheet.gif new file mode 100644 index 0000000..a292564 Binary files /dev/null and b/apps/web/public/tour/recipe-sheet.gif differ diff --git a/apps/web/src/infra/analytics.ts b/apps/web/src/infra/analytics.ts new file mode 100644 index 0000000..9d45752 --- /dev/null +++ b/apps/web/src/infra/analytics.ts @@ -0,0 +1,13 @@ +import { readAnalyticsConsent } from "./cookie-consent-storage"; + +type MatomoWindow = Window & + typeof globalThis & { + _paq?: unknown[][]; + }; + +export const trackEvent = (category: string, action: string, name?: string): void => { + if (readAnalyticsConsent() !== "analytics-accepted") return; + const matomoWindow = window as MatomoWindow; + matomoWindow._paq = matomoWindow._paq ?? []; + matomoWindow._paq.push(["trackEvent", category, action, name ?? ""]); +}; diff --git a/apps/web/src/infra/analytics.unit.test.ts b/apps/web/src/infra/analytics.unit.test.ts new file mode 100644 index 0000000..c161eb9 --- /dev/null +++ b/apps/web/src/infra/analytics.unit.test.ts @@ -0,0 +1,34 @@ +import { afterEach, beforeEach, describe, expect, it, vi } from "vitest"; + +vi.mock("./cookie-consent-storage", () => ({ + readAnalyticsConsent: vi.fn() +})); + +import { readAnalyticsConsent } from "./cookie-consent-storage"; +import { trackEvent } from "./analytics"; + +const readConsent = vi.mocked(readAnalyticsConsent); + +describe("trackEvent (Matomo)", () => { + beforeEach(() => { + readConsent.mockReset(); + readConsent.mockReturnValue("analytics-rejected"); + vi.stubGlobal("window", { _paq: [] as unknown[][] }); + }); + + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it("n’envoie rien sans consentement analytics", () => { + trackEvent("recipe", "share", "x"); + expect(window._paq?.length ?? 0).toBe(0); + }); + + it("pousse un trackEvent Matomo lorsque le consentement est accepté", () => { + readConsent.mockReturnValue("analytics-accepted"); + vi.stubGlobal("window", { _paq: [] as unknown[][] }); + trackEvent("recipe", "share", "y"); + expect(window._paq).toContainEqual(["trackEvent", "recipe", "share", "y"]); + }); +}); diff --git a/apps/web/src/infra/cookie-consent-storage.ts b/apps/web/src/infra/cookie-consent-storage.ts new file mode 100644 index 0000000..d0b80cb --- /dev/null +++ b/apps/web/src/infra/cookie-consent-storage.ts @@ -0,0 +1,22 @@ +import { + COOKIE_CONSENT_STORAGE_KEY, + normalizeConsentRaw, + type ConsentParseResult, + type StoredAnalyticsConsent +} from "@ben-to/privacy/cookie-consent"; + +export const readAnalyticsConsent = (): ConsentParseResult => { + try { + return normalizeConsentRaw(localStorage.getItem(COOKIE_CONSENT_STORAGE_KEY)); + } catch { + return "unknown"; + } +}; + +export const writeAnalyticsConsent = (value: StoredAnalyticsConsent): void => { + try { + localStorage.setItem(COOKIE_CONSENT_STORAGE_KEY, value); + } catch { + /* stockage indisponible (mode privé strict, etc.) */ + } +}; diff --git a/apps/web/src/infra/matomo.ts b/apps/web/src/infra/matomo.ts new file mode 100644 index 0000000..89a5cab --- /dev/null +++ b/apps/web/src/infra/matomo.ts @@ -0,0 +1,60 @@ +import { readAnalyticsConsent } from "./cookie-consent-storage"; + +export type MatomoRuntimeConfig = Readonly<{ + baseUrl: string; + siteId: string; +}>; + +type MatomoWindow = Window & + typeof globalThis & { + _paq?: unknown[][]; + }; + +let injectPromise: Promise | null = null; + +export const getMatomoRuntimeConfig = (): MatomoRuntimeConfig | null => { + const rawUrl = import.meta.env.VITE_MATOMO_URL; + const rawSite = import.meta.env.VITE_MATOMO_SITE_ID; + const url = typeof rawUrl === "string" ? rawUrl.trim() : ""; + const siteId = typeof rawSite === "string" ? rawSite.trim() : ""; + if (!url || !siteId) return null; + const baseUrl = url.endsWith("/") ? url : `${url}/`; + return { baseUrl, siteId }; +}; + +export const isMatomoConfigured = (): boolean => getMatomoRuntimeConfig() !== null; + +export const injectMatomoTracker = (cfg: MatomoRuntimeConfig): Promise => { + if (injectPromise) return injectPromise; + injectPromise = new Promise((resolve, reject) => { + const w = window as MatomoWindow; + w._paq = w._paq ?? []; + w._paq.push(["setTrackerUrl", `${cfg.baseUrl}matomo.php`]); + w._paq.push(["setSiteId", cfg.siteId]); + const g = document.createElement("script"); + g.async = true; + g.src = `${cfg.baseUrl}matomo.js`; + g.onload = () => { + const mw = window as MatomoWindow; + mw._paq?.push(["enableLinkTracking"]); + resolve(); + }; + g.onerror = () => reject(new Error("Failed to load matomo.js")); + const s = document.getElementsByTagName("script")[0]; + if (s?.parentNode) { + s.parentNode.insertBefore(g, s); + } else { + document.head.appendChild(g); + } + }); + return injectPromise; +}; + +export const trackMatomoPageView = (): void => { + if (readAnalyticsConsent() !== "analytics-accepted") return; + const w = window as MatomoWindow; + if (!w._paq) return; + w._paq.push(["setCustomUrl", window.location.href]); + w._paq.push(["setDocumentTitle", document.title]); + w._paq.push(["trackPageView"]); +}; diff --git a/apps/web/src/infra/persistence.ts b/apps/web/src/infra/persistence.ts new file mode 100644 index 0000000..9687858 --- /dev/null +++ b/apps/web/src/infra/persistence.ts @@ -0,0 +1,12 @@ +import type { BentoSelection } from "@ben-to/builder/domain"; + +const key = "ben-to.selection"; + +export const saveSelection = (selection: BentoSelection): void => { + localStorage.setItem(key, JSON.stringify(selection)); +}; + +export const loadSelection = (): BentoSelection | null => { + const raw = localStorage.getItem(key); + return raw ? (JSON.parse(raw) as BentoSelection) : null; +}; diff --git a/apps/web/src/infra/recipe-checklist-persistence.ts b/apps/web/src/infra/recipe-checklist-persistence.ts new file mode 100644 index 0000000..9b002c8 --- /dev/null +++ b/apps/web/src/infra/recipe-checklist-persistence.ts @@ -0,0 +1,54 @@ +const STORAGE_KEY = "ben-to.recipe-checklist"; + +export type RecipeChecklistStored = Readonly<{ + ingredientIds: readonly string[]; + stepIds: readonly string[]; +}>; + +type RecipeChecklistMap = Record; + +const emptyEntry = (): RecipeChecklistStored => + Object.freeze({ ingredientIds: Object.freeze([]), stepIds: Object.freeze([]) }); + +const loadMap = (): RecipeChecklistMap => { + if (typeof localStorage === "undefined") return {}; + try { + const raw = localStorage.getItem(STORAGE_KEY); + if (!raw) return {}; + const parsed = JSON.parse(raw) as unknown; + if (parsed === null || typeof parsed !== "object" || Array.isArray(parsed)) return {}; + return parsed as RecipeChecklistMap; + } catch { + return {}; + } +}; + +const saveMap = (map: RecipeChecklistMap): void => { + if (typeof localStorage === "undefined") return; + try { + localStorage.setItem(STORAGE_KEY, JSON.stringify(map)); + } catch { + /* quota / private mode */ + } +}; + +export const loadRecipeChecklist = (recipeKey: string): RecipeChecklistStored => { + const map = loadMap(); + const entry = map[recipeKey]; + if (!entry || !Array.isArray(entry.ingredientIds) || !Array.isArray(entry.stepIds)) { + return emptyEntry(); + } + return Object.freeze({ + ingredientIds: Object.freeze([...entry.ingredientIds]), + stepIds: Object.freeze([...entry.stepIds]) + }); +}; + +export const saveRecipeChecklist = (recipeKey: string, data: RecipeChecklistStored): void => { + const map = loadMap(); + map[recipeKey] = Object.freeze({ + ingredientIds: Object.freeze([...data.ingredientIds]), + stepIds: Object.freeze([...data.stepIds]) + }); + saveMap(map); +}; diff --git a/apps/web/src/infra/welcome-tour-storage.int.test.ts b/apps/web/src/infra/welcome-tour-storage.int.test.ts new file mode 100644 index 0000000..d7c5ae3 --- /dev/null +++ b/apps/web/src/infra/welcome-tour-storage.int.test.ts @@ -0,0 +1,36 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { + readWelcomeTourDismissed, + WELCOME_TOUR_SCHEMA_VERSION, + WELCOME_TOUR_STORAGE_KEY, + writeWelcomeTourDismissed +} from "./welcome-tour-storage"; + +const mockStorage = () => { + const map = new Map(); + vi.stubGlobal("localStorage", { + getItem: (k: string) => (map.has(k) ? map.get(k)! : null), + setItem: (k: string, v: string) => void map.set(k, v), + removeItem: (k: string) => void map.delete(k), + clear: () => void map.clear(), + key: (i: number) => [...map.keys()][i] ?? null, + get length() { + return map.size; + } + } as Storage); +}; + +describe("welcome tour storage (Gherkin @int-welcome-tour-dismiss)", () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + /** @int-welcome-tour-dismiss */ + it("enregistre la version courante lorsque le tour est fermé", () => { + mockStorage(); + expect(readWelcomeTourDismissed()).toBe(false); + writeWelcomeTourDismissed(); + expect(localStorage.getItem(WELCOME_TOUR_STORAGE_KEY)).toBe(WELCOME_TOUR_SCHEMA_VERSION); + expect(readWelcomeTourDismissed()).toBe(true); + }); +}); diff --git a/apps/web/src/infra/welcome-tour-storage.ts b/apps/web/src/infra/welcome-tour-storage.ts new file mode 100644 index 0000000..caad53f --- /dev/null +++ b/apps/web/src/infra/welcome-tour-storage.ts @@ -0,0 +1,21 @@ +/** Clé localStorage : fermeture du tour d'accueil pour une version de contenu donnée. */ +export const WELCOME_TOUR_STORAGE_KEY = "ben-to.welcome-tour-dismissed"; + +/** Incrémenter pour réafficher le tour après refonte majeure du contenu. */ +export const WELCOME_TOUR_SCHEMA_VERSION = "v1"; + +export const readWelcomeTourDismissed = (): boolean => { + try { + return localStorage.getItem(WELCOME_TOUR_STORAGE_KEY) === WELCOME_TOUR_SCHEMA_VERSION; + } catch { + return false; + } +}; + +export const writeWelcomeTourDismissed = (): void => { + try { + localStorage.setItem(WELCOME_TOUR_STORAGE_KEY, WELCOME_TOUR_SCHEMA_VERSION); + } catch { + /* stockage indisponible */ + } +}; diff --git a/apps/web/src/infra/welcome-tour-storage.unit.test.ts b/apps/web/src/infra/welcome-tour-storage.unit.test.ts new file mode 100644 index 0000000..bb4ed2b --- /dev/null +++ b/apps/web/src/infra/welcome-tour-storage.unit.test.ts @@ -0,0 +1,45 @@ +import { afterEach, describe, expect, it, vi } from "vitest"; +import { + readWelcomeTourDismissed, + WELCOME_TOUR_SCHEMA_VERSION, + WELCOME_TOUR_STORAGE_KEY, + writeWelcomeTourDismissed +} from "./welcome-tour-storage"; + +const mockStorage = () => { + const map = new Map(); + vi.stubGlobal("localStorage", { + getItem: (k: string) => (map.has(k) ? map.get(k)! : null), + setItem: (k: string, v: string) => void map.set(k, v), + removeItem: (k: string) => void map.delete(k), + clear: () => void map.clear(), + key: (i: number) => [...map.keys()][i] ?? null, + get length() { + return map.size; + } + } as Storage); +}; + +describe("welcome-tour-storage", () => { + afterEach(() => { + vi.unstubAllGlobals(); + }); + + it("readWelcomeTourDismissed est faux sans valeur", () => { + mockStorage(); + expect(readWelcomeTourDismissed()).toBe(false); + }); + + it("write puis read indique dismiss pour la version courante", () => { + mockStorage(); + writeWelcomeTourDismissed(); + expect(localStorage.getItem(WELCOME_TOUR_STORAGE_KEY)).toBe(WELCOME_TOUR_SCHEMA_VERSION); + expect(readWelcomeTourDismissed()).toBe(true); + }); + + it("une ancienne valeur ne compte pas comme dismiss", () => { + mockStorage(); + localStorage.setItem(WELCOME_TOUR_STORAGE_KEY, "v0"); + expect(readWelcomeTourDismissed()).toBe(false); + }); +}); diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx new file mode 100644 index 0000000..08ab5bc --- /dev/null +++ b/apps/web/src/main.tsx @@ -0,0 +1,5 @@ +import { render } from "solid-js/web"; +import { App } from "./ui/App"; +import "./ui/app.css"; + +render(() => , document.getElementById("root")!); diff --git a/apps/web/src/ui/App.tsx b/apps/web/src/ui/App.tsx new file mode 100644 index 0000000..d9ed5ab --- /dev/null +++ b/apps/web/src/ui/App.tsx @@ -0,0 +1,18 @@ +import { Route, Router } from "@solidjs/router"; +import { BuilderFlow } from "./BuilderFlow"; +import { RootLayout } from "./RootLayout"; +import { StaticInfoPage } from "./pages/StaticInfoPage"; + +export function App() { + return ( + + + + } /> + } /> + } /> + } /> + } /> + + ); +} diff --git a/apps/web/src/ui/BuilderFlow.tsx b/apps/web/src/ui/BuilderFlow.tsx new file mode 100644 index 0000000..28f8891 --- /dev/null +++ b/apps/web/src/ui/BuilderFlow.tsx @@ -0,0 +1,126 @@ +import type { RouteSectionProps } from "@solidjs/router"; +import type { BentoSelection } from "@ben-to/builder/domain"; +import { createInitialSelection } from "@ben-to/builder/domain"; +import { fetchRecipesIndex } from "@ben-to/recipes/catalog"; +import { getDictionary } from "@ben-to/i18n/dictionary"; +import { createEffect, createMemo, createResource, createSignal, Show } from "solid-js"; +import { clearBuilderResetBus, readBuilderResetNonce } from "./builder-reset-bus"; +import { useAppLocale } from "./app-locale-context"; +import { BuilderBaseStep } from "./builder-flow/BuilderBaseStep"; +import { BuilderCtaDock } from "./builder-flow/BuilderCtaDock"; +import { BuilderStepsNav } from "./builder-flow/BuilderStepsNav"; +import { BuilderVariantStep } from "./builder-flow/BuilderVariantStep"; +import { RecipeSheetSection } from "./builder-flow/RecipeSheetSection"; +import { useBuilderCatalog } from "./builder-flow/use-builder-catalog"; +import { useBuilderSteps } from "./builder-flow/use-builder-steps"; +import { useRecipeSheet } from "./builder-flow/use-recipe-sheet"; + +export function BuilderFlow(_props: RouteSectionProps) { + const { locale } = useAppLocale(); + const [selection, setSelection] = createSignal(createInitialSelection()); + const [recipesIndex] = createResource(async () => fetchRecipesIndex()); + const t = createMemo(() => getDictionary(locale())); + + const steps = useBuilderSteps({ + selection, + setSelection, + recipesIndex + }); + + const catalog = useBuilderCatalog({ + recipesIndex, + selection, + flavorFilter: steps.flavorFilter, + locale + }); + + const recipe = useRecipeSheet({ + selection, + currentStep: steps.currentStep, + mergedRecipe: catalog.mergedRecipe, + recipeDisplayTitle: catalog.recipeDisplayTitle, + locale, + t + }); + + createEffect(() => { + const n = readBuilderResetNonce(); + if (n === 0) return; + steps.goHomeWizard(); + recipe.resetServingsForGoHome(); + clearBuilderResetBus(); + }); + + return ( +
+ + + +
+ +
+
+ + +
+ +
+
+ + + + +
+ ); +} diff --git a/apps/web/src/ui/CookieConsentBanner.tsx b/apps/web/src/ui/CookieConsentBanner.tsx new file mode 100644 index 0000000..3165a99 --- /dev/null +++ b/apps/web/src/ui/CookieConsentBanner.tsx @@ -0,0 +1,41 @@ +import { A } from "@solidjs/router"; +import type { Accessor } from "solid-js"; +import type { Dictionary } from "@ben-to/i18n/dictionary"; + +export function CookieConsentBanner(props: { + readonly t: Accessor; + readonly onAccept: () => void; + readonly onReject: () => void; +}) { + return ( + + ); +} diff --git a/apps/web/src/ui/RootLayout.tsx b/apps/web/src/ui/RootLayout.tsx new file mode 100644 index 0000000..d8d516f --- /dev/null +++ b/apps/web/src/ui/RootLayout.tsx @@ -0,0 +1,222 @@ +import { A, useLocation } from "@solidjs/router"; +import type { RouteSectionProps } from "@solidjs/router"; +import { createEffect, createSignal, For, onCleanup, onMount, Show } from "solid-js"; +import { getDictionary, type LocaleCode } from "@ben-to/i18n/dictionary"; +import { readAnalyticsConsent, writeAnalyticsConsent } from "../infra/cookie-consent-storage"; +import { readWelcomeTourDismissed, writeWelcomeTourDismissed } from "../infra/welcome-tour-storage"; +import { getMatomoRuntimeConfig, injectMatomoTracker, trackMatomoPageView } from "../infra/matomo"; +import { bumpBuilderResetBus } from "./builder-reset-bus"; +import { AppLocaleProvider, useAppLocale } from "./app-locale-context"; +import { CookieConsentBanner } from "./CookieConsentBanner"; +import { WelcomeTourModal } from "./WelcomeTourModal"; +import { publicAssetHref } from "./builder-flow/public-asset"; + +const localeOptions: LocaleCode[] = ["fr", "en", "ko", "zh"]; + +const localeNativeNames: Record = { + fr: "Français", + en: "English", + ko: "한국어", + zh: "中文" +}; + +const localeFlags: Record = { + fr: "🇫🇷", + en: "🇬🇧", + ko: "🇰🇷", + zh: "🇨🇳" +}; + +function RootLayoutInner(props: RouteSectionProps) { + const { locale, setLocale } = useAppLocale(); + const location = useLocation(); + const [menuOpen, setMenuOpen] = createSignal(false); + const t = () => getDictionary(locale()); + const matomoCfg = getMatomoRuntimeConfig(); + const [matomoReady, setMatomoReady] = createSignal(false); + const [consentBannerOpen, setConsentBannerOpen] = createSignal(readAnalyticsConsent() === "unknown"); + const [welcomeTourOpen, setWelcomeTourOpen] = createSignal(false); + + createEffect(() => { + location.pathname; + setMenuOpen(false); + }); + + createEffect(() => { + location.pathname; + location.search; + if (!matomoReady()) return; + trackMatomoPageView(); + }); + + const acceptAnalyticsCookies = () => { + writeAnalyticsConsent("analytics-accepted"); + setConsentBannerOpen(false); + if (matomoCfg) { + void injectMatomoTracker(matomoCfg).then(() => setMatomoReady(true)); + } + }; + + const rejectAnalyticsCookies = () => { + writeAnalyticsConsent("analytics-rejected"); + setConsentBannerOpen(false); + }; + + const dismissWelcomeTour = () => { + writeWelcomeTourDismissed(); + setWelcomeTourOpen(false); + }; + + createEffect(() => { + location.pathname; + consentBannerOpen(); + if (location.pathname !== "/" || consentBannerOpen()) { + setWelcomeTourOpen(false); + return; + } + if (readWelcomeTourDismissed()) { + setWelcomeTourOpen(false); + return; + } + setWelcomeTourOpen(true); + }); + + onMount(() => { + const consent = readAnalyticsConsent(); + if (consent === "unknown") setConsentBannerOpen(true); + if (consent === "analytics-accepted" && matomoCfg) { + void injectMatomoTracker(matomoCfg).then(() => setMatomoReady(true)); + } + const onKeyDown = (event: KeyboardEvent) => { + if (event.key === "Escape") { + if (welcomeTourOpen()) { + dismissWelcomeTour(); + return; + } + setMenuOpen(false); + } + }; + window.addEventListener("keydown", onKeyDown); + onCleanup(() => window.removeEventListener("keydown", onKeyDown)); + }); + + return ( + <> +
+ +

+ { + if (location.pathname === "/") { + event.preventDefault(); + bumpBuilderResetBus(); + } + }} + > + + + +

+
+ + {(code) => ( + + )} + +
+
+ + {props.children} + + +