$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 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"