Initial upload: fin65-mobile

This commit is contained in:
Abderrahim-Mouhcine
2026-08-16 19:59:55 +01:00
commit 46558a5ec8
242 changed files with 16334 additions and 0 deletions
@@ -0,0 +1,43 @@
# FIN65 - Réparation MainActivity biométrie
Ce patch corrige le crash Android :
```text
ClassNotFoundException:
Didn't find class "com.example.fin65_mobile.MainActivity"
```
La compilation réussit, mais Android ne trouve pas la classe lancée par le Manifest. Donc l'APK s'installe, puis l'app crashe au démarrage. Très théâtral, très Android.
## Ce que le script fait
- détecte le package Android depuis `build.gradle.kts` ;
- recrée `MainActivity.kt` au bon endroit ;
- utilise `FlutterFragmentActivity`, nécessaire pour la biométrie ;
- force le Manifest à pointer vers la classe complète :
`com.example.fin65_mobile.MainActivity`
- garde :
- `INTERNET`
- `USE_BIOMETRIC`
- `usesCleartextTraffic=true`
- icône FIN65
- label FIN65
- supprime si besoin le bloc Gradle Kotlin invalide revenu par accident ;
- lance `flutter clean` et `flutter pub get`.
## Utilisation
Décompresse puis lance :
```powershell
.\repair_mainactivity_biometrics.bat
```
Ensuite :
```powershell
cd D:\07-Logiciels\20-FIN65\fin65_mobile
flutter run
```
Ne relance pas les anciens patchs biométrie après celui-ci.
@@ -0,0 +1,4 @@
@echo off
setlocal
powershell -NoProfile -ExecutionPolicy Bypass -File "%~dp0repair_mainactivity_biometrics.bat.ps1"
@@ -0,0 +1,245 @@
$MobileDir = "D:\07-Logiciels\20-FIN65\fin65_mobile"
$AndroidMain = Join-Path $MobileDir "android\app\src\main"
$Manifest = Join-Path $AndroidMain "AndroidManifest.xml"
$GradleKts = Join-Path $MobileDir "android\app\build.gradle.kts"
$GradleGroovy = Join-Path $MobileDir "android\app\build.gradle"
Write-Host ""
Write-Host "=========================================="
Write-Host " FIN65 - Repair MainActivity biometrie"
Write-Host "=========================================="
Write-Host ""
if (!(Test-Path $MobileDir)) {
Write-Host "[ERREUR] Dossier mobile introuvable : $MobileDir" -ForegroundColor Red
Read-Host "Appuie sur Entree pour fermer"
exit 1
}
if (!(Test-Path $Manifest)) {
Write-Host "[ERREUR] AndroidManifest.xml introuvable : $Manifest" -ForegroundColor Red
Read-Host "Appuie sur Entree pour fermer"
exit 1
}
$Stamp = Get-Date -Format "yyyyMMdd_HHmmss"
function Backup-File($Path) {
if (Test-Path $Path) {
Copy-Item $Path "$Path.before_mainactivity_repair_$Stamp.bak" -Force
Write-Host "Sauvegarde : $Path.before_mainactivity_repair_$Stamp.bak"
}
}
Backup-File $Manifest
if (Test-Path $GradleKts) { Backup-File $GradleKts }
if (Test-Path $GradleGroovy) { Backup-File $GradleGroovy }
# 1) Déterminer package/namespace Android.
$PackageName = "com.example.fin65_mobile"
if (Test-Path $GradleKts) {
$g = Get-Content $GradleKts -Raw
$m = [regex]::Match($g, 'namespace\s*=\s*"([^"]+)"')
if ($m.Success) { $PackageName = $m.Groups[1].Value }
$m2 = [regex]::Match($g, 'applicationId\s*=\s*"([^"]+)"')
if ($m2.Success) { $PackageName = $m2.Groups[1].Value }
} elseif (Test-Path $GradleGroovy) {
$g = Get-Content $GradleGroovy -Raw
$m = [regex]::Match($g, 'namespace\s+["'']([^"'']+)["'']')
if ($m.Success) { $PackageName = $m.Groups[1].Value }
$m2 = [regex]::Match($g, 'applicationId\s+["'']([^"'']+)["'']')
if ($m2.Success) { $PackageName = $m2.Groups[1].Value }
}
Write-Host "Package Android retenu : $PackageName"
# 2) Créer MainActivity.kt au bon endroit avec FlutterFragmentActivity.
$PackagePath = $PackageName.Replace(".", "\")
$KotlinDir = Join-Path $AndroidMain "kotlin\$PackagePath"
$MainActivity = Join-Path $KotlinDir "MainActivity.kt"
if (!(Test-Path $KotlinDir)) {
New-Item -ItemType Directory -Force -Path $KotlinDir | Out-Null
}
if (Test-Path $MainActivity) {
Backup-File $MainActivity
}
$MainActivityText = @"
package $PackageName
import io.flutter.embedding.android.FlutterFragmentActivity
class MainActivity : FlutterFragmentActivity()
"@
Set-Content -Path $MainActivity -Value $MainActivityText -Encoding UTF8
Write-Host "MainActivity.kt recrée : $MainActivity"
# 3) Patch Manifest sans casser XML.
[xml]$xml = Get-Content $Manifest
$androidNs = "http://schemas.android.com/apk/res/android"
$manifestNode = $xml.manifest
function Ensure-Permission($name) {
$existing = $manifestNode.'uses-permission' | Where-Object {
$_.GetAttribute("name", $androidNs) -eq $name
}
if ($null -eq $existing) {
$perm = $xml.CreateElement("uses-permission")
$attr = $xml.CreateAttribute("android", "name", $androidNs)
$attr.Value = $name
[void]$perm.Attributes.Append($attr)
[void]$manifestNode.InsertBefore($perm, $manifestNode.FirstChild)
Write-Host "Permission ajoutee : $name"
} else {
Write-Host "Permission deja presente : $name"
}
}
Ensure-Permission "android.permission.INTERNET"
Ensure-Permission "android.permission.USE_BIOMETRIC"
$app = $manifestNode.application
if ($null -eq $app) {
Write-Host "[ERREUR] Balise <application> introuvable dans AndroidManifest.xml" -ForegroundColor Red
Read-Host "Appuie sur Entree pour fermer"
exit 1
}
$app.SetAttribute("label", $androidNs, "FIN65")
$app.SetAttribute("icon", $androidNs, "@mipmap/ic_launcher")
$app.SetAttribute("roundIcon", $androidNs, "@mipmap/ic_launcher_round")
$app.SetAttribute("usesCleartextTraffic", $androidNs, "true")
$activities = @($app.activity)
$mainActivityNode = $null
foreach ($a in $activities) {
if ($null -ne $a) {
$name = $a.GetAttribute("name", $androidNs)
$exported = $a.GetAttribute("exported", $androidNs)
$hasLauncher = $false
foreach ($f in @($a.'intent-filter')) {
if ($null -ne $f) {
foreach ($cat in @($f.category)) {
if ($cat.GetAttribute("name", $androidNs) -eq "android.intent.category.LAUNCHER") {
$hasLauncher = $true
}
}
}
}
if ($hasLauncher -or $name -like "*MainActivity*") {
$mainActivityNode = $a
break
}
}
}
if ($null -eq $mainActivityNode) {
$mainActivityNode = $xml.CreateElement("activity")
[void]$app.PrependChild($mainActivityNode)
}
$mainActivityNode.SetAttribute("name", $androidNs, "$PackageName.MainActivity")
$mainActivityNode.SetAttribute("exported", $androidNs, "true")
$mainActivityNode.SetAttribute("launchMode", $androidNs, "singleTop")
$mainActivityNode.SetAttribute("taskAffinity", $androidNs, "")
$mainActivityNode.SetAttribute("theme", $androidNs, "@style/LaunchTheme")
$mainActivityNode.SetAttribute("configChanges", $androidNs, "orientation|keyboardHidden|keyboard|screenSize|smallestScreenSize|locale|layoutDirection|fontScale|screenLayout|density|uiMode")
$mainActivityNode.SetAttribute("hardwareAccelerated", $androidNs, "true")
$mainActivityNode.SetAttribute("windowSoftInputMode", $androidNs, "adjustResize")
# Ajouter NormalTheme s'il manque.
$hasNormalTheme = $false
foreach ($md in @($mainActivityNode.'meta-data')) {
if ($null -ne $md -and $md.GetAttribute("name", $androidNs) -eq "io.flutter.embedding.android.NormalTheme") {
$hasNormalTheme = $true
}
}
if (!$hasNormalTheme) {
$meta = $xml.CreateElement("meta-data")
$meta.SetAttribute("name", $androidNs, "io.flutter.embedding.android.NormalTheme")
$meta.SetAttribute("resource", $androidNs, "@style/NormalTheme")
[void]$mainActivityNode.PrependChild($meta)
}
# Ajouter launcher intent-filter s'il manque.
$hasLauncherFilter = $false
foreach ($f in @($mainActivityNode.'intent-filter')) {
if ($null -ne $f) {
foreach ($cat in @($f.category)) {
if ($cat.GetAttribute("name", $androidNs) -eq "android.intent.category.LAUNCHER") {
$hasLauncherFilter = $true
}
}
}
}
if (!$hasLauncherFilter) {
$filter = $xml.CreateElement("intent-filter")
$action = $xml.CreateElement("action")
$action.SetAttribute("name", $androidNs, "android.intent.action.MAIN")
[void]$filter.AppendChild($action)
$category = $xml.CreateElement("category")
$category.SetAttribute("name", $androidNs, "android.intent.category.LAUNCHER")
[void]$filter.AppendChild($category)
[void]$mainActivityNode.AppendChild($filter)
}
# Ajouter flutterEmbedding s'il manque.
$hasFlutterEmbedding = $false
foreach ($md in @($app.'meta-data')) {
if ($null -ne $md -and $md.GetAttribute("name", $androidNs) -eq "flutterEmbedding") {
$hasFlutterEmbedding = $true
}
}
if (!$hasFlutterEmbedding) {
$md = $xml.CreateElement("meta-data")
$md.SetAttribute("name", $androidNs, "flutterEmbedding")
$md.SetAttribute("value", $androidNs, "2")
[void]$app.AppendChild($md)
}
$xml.Save($Manifest)
Write-Host "AndroidManifest.xml mis a jour."
# 4) Nettoyer les MainActivity douteuses : ne pas supprimer, juste les afficher.
Write-Host ""
Write-Host "MainActivity detectees :"
Get-ChildItem (Join-Path $AndroidMain "kotlin") -Recurse -Filter MainActivity.kt -ErrorAction SilentlyContinue | ForEach-Object {
Write-Host " - $($_.FullName)"
}
Get-ChildItem (Join-Path $AndroidMain "java") -Recurse -Filter MainActivity.java -ErrorAction SilentlyContinue | ForEach-Object {
Write-Host " - $($_.FullName)"
}
# 5) Gradle : garder minSdk = 24, supprimer bloc Kotlin invalide si revenu.
if (Test-Path $GradleKts) {
$text = Get-Content $GradleKts -Raw
$text = [regex]::Replace($text, '(?ms)^\s*kotlin\s*\{\s*compilerOptions\s*\{.*?\}\s*\}\s*$', '')
$text = [regex]::Replace($text, 'minSdk\s*=\s*flutter\.minSdkVersion', 'minSdk = 24')
$text = [regex]::Replace($text, 'minSdk\s*=\s*\d+', 'minSdk = 24')
Set-Content -Path $GradleKts -Value $text -Encoding UTF8
Write-Host "build.gradle.kts verifie."
}
Set-Location $MobileDir
Write-Host ""
Write-Host "Nettoyage Flutter..."
flutter clean
flutter pub get
Write-Host ""
Write-Host "Reparation terminee."
Write-Host "Relance maintenant :"
Write-Host "flutter run"
Write-Host ""
Read-Host "Appuie sur Entree pour fermer"