@@ -17,6 +17,7 @@ import androidx.fragment.app.Fragment
1717import com.google.android.material.button.MaterialButton
1818import com.google.android.material.slider.Slider
1919import com.mg4.control.R
20+ import com.mg4.control.debug.AppLogger
2021import com.mg4.control.hardware.MG4Hardware
2122import com.mg4.control.hardware.MG4Hardware.AebMode
2223import com.mg4.control.hardware.MG4Hardware.AebSensitivity
@@ -86,6 +87,12 @@ class DashboardFragment : Fragment() {
8687 /* * Dernier mode de conduite connu — nécessaire pour arbitrer les exclusions SNOW / Éco énergie. */
8788 private var currentDriveMode: DriveMode ? = null
8889
90+ // ── Mode Personnalisé — puissance / direction / pédale ───────────────────
91+ // Une entrée par ligne : le bouton, et l'index qu'il représente (0/1/2).
92+ private val customPowerButtons = mutableMapOf<Int , Button >()
93+ private val customSteerButtons = mutableMapOf<Int , Button >()
94+ private val customPedalButtons = mutableMapOf<Int , Button >()
95+
8996 // ── AEB : page 0 pour VSM-based, page 1 (SWI133) pour les autres ───────────
9097 private var switchAeb: Switch ? = null
9198 private var btnAebAlarm: Button ? = null
@@ -320,6 +327,17 @@ class DashboardFragment : Fragment() {
320327 switchSoundWarning = view.findViewById(R .id.switch_sound_warning)
321328 alertsGroupSwi133 = view.findViewById(R .id.alerts_group_swi133)
322329
330+ // Mode Personnalisé — trois lignes de trois boutons
331+ customPowerButtons[0 ] = view.findViewById(R .id.btn_cd_power_eco)
332+ customPowerButtons[1 ] = view.findViewById(R .id.btn_cd_power_normal)
333+ customPowerButtons[2 ] = view.findViewById(R .id.btn_cd_power_sport)
334+ customSteerButtons[0 ] = view.findViewById(R .id.btn_cd_steer_comfort)
335+ customSteerButtons[1 ] = view.findViewById(R .id.btn_cd_steer_normal)
336+ customSteerButtons[2 ] = view.findViewById(R .id.btn_cd_steer_sport)
337+ customPedalButtons[0 ] = view.findViewById(R .id.btn_cd_pedal_comfort)
338+ customPedalButtons[1 ] = view.findViewById(R .id.btn_cd_pedal_normal)
339+ customPedalButtons[2 ] = view.findViewById(R .id.btn_cd_pedal_sport)
340+
323341 // TSR + Économie d'énergie
324342 switchTsr = view.findViewById(R .id.switch_tsr)
325343 btnEnergySaving = view.findViewById(R .id.btn_energy_saving)
@@ -363,6 +381,22 @@ class DashboardFragment : Fragment() {
363381 }
364382 }
365383
384+ // Mode Personnalisé — chaque ligne écrit son réglage et rien d'autre.
385+ listOf (
386+ Triple (customPowerButtons, MG4Hardware ::setCustomPower, " puissance" ),
387+ Triple (customSteerButtons, MG4Hardware ::setCustomSteering, " direction" ),
388+ Triple (customPedalButtons, MG4Hardware ::setCustomPedal, " pédale" )
389+ ).forEach { (boutons, ecrire, nom) ->
390+ boutons.forEach { (index, btn) ->
391+ btn.setOnClickListener {
392+ highlightCustomRow(boutons, index)
393+ CoroutineScope (Dispatchers .IO ).launch {
394+ AppLogger .i(" MG4_DASH" , " mode personnalisé — $nom = $index → ${ecrire(index)} " )
395+ }
396+ }
397+ }
398+ }
399+
366400 // Regen
367401 regenButtons.forEach { (level, btn) ->
368402 btn.setOnClickListener {
@@ -906,8 +940,68 @@ class DashboardFragment : Fragment() {
906940 setRegenEnabled(! active && currentDriveMode != DriveMode .SNOW )
907941 }
908942
943+ /* * Surligne l'option retenue d'une ligne du mode Personnalisé. */
944+ private fun highlightCustomRow (boutons : Map <Int , Button >, actif : Int? ) {
945+ boutons.forEach { (index, btn) ->
946+ val on = index == actif
947+ btn.backgroundTintList = ColorStateList .valueOf(if (on) colorActive else colorInactive)
948+ btn.setTextColor(if (on) colorTextActive else colorTextInactive)
949+ }
950+ }
951+
952+ /* *
953+ * Révèle la carte du mode Personnalisé et y reporte l'état du véhicule.
954+ *
955+ * Deux niveaux de masquage, et ils ne disent pas la même chose :
956+ * • la CARTE n'apparaît que si la voiture est réellement en mode Personnalisé — ces trois
957+ * réglages n'ont aucun effet ailleurs ;
958+ * • une LIGNE disparaît si le véhicule ne rend pas son état. Les six firmwares exposent la
959+ * commande, mais rien ne garantit que la finition porte l'équipement — une direction à
960+ * assistance variable, par exemple. Un bouton qui n'écrirait nulle part vaut moins que
961+ * pas de bouton du tout.
962+ */
963+ private fun refreshCustomDrive (mode : DriveMode ? ) {
964+ val vue = view ? : return
965+ val carte = vue.findViewById<View >(R .id.section_custom_drive) ? : return
966+ if (mode != DriveMode .CUSTOM ) {
967+ carte.visibility = View .GONE
968+ return
969+ }
970+ CoroutineScope (Dispatchers .IO ).launch {
971+ val puissance = MG4Hardware .getCustomPower()
972+ val direction = MG4Hardware .getCustomSteering()
973+ val pedale = MG4Hardware .getCustomPedal()
974+ withContext(Dispatchers .Main ) {
975+ if (! isAdded) return @withContext
976+ // L'état a pu changer pendant la lecture (l'utilisateur quitte le mode) : on
977+ // revérifie avant d'afficher, sinon la carte réapparaîtrait toute seule.
978+ if (currentDriveMode != DriveMode .CUSTOM ) { carte.visibility = View .GONE ; return @withContext }
979+ val lignes = listOf (
980+ Triple (customPowerButtons, puissance, R .id.row_cd_power),
981+ Triple (customSteerButtons, direction, R .id.row_cd_steer),
982+ Triple (customPedalButtons, pedale, R .id.row_cd_pedal)
983+ )
984+ lignes.forEach { (boutons, valeur, rowId) ->
985+ vue.findViewById<View >(rowId)?.visibility =
986+ if (valeur == null ) View .GONE else View .VISIBLE
987+ highlightCustomRow(boutons, valeur)
988+ }
989+ // Aucune ligne lisible : la carte n'aurait qu'un titre à montrer.
990+ val lisible = lignes.any { it.second != null }
991+ carte.visibility = if (lisible) View .VISIBLE else View .GONE
992+ // Trois lectures nulles d'un coup, c'est plus vraisemblablement une couche
993+ // véhicule pas encore prête qu'une voiture dépourvue des trois équipements.
994+ // Même repli que refreshDriveRegen, qui réessaie pour la même raison.
995+ if (! lisible) view?.postDelayed({
996+ if (isAdded && currentDriveMode == DriveMode .CUSTOM ) refreshCustomDrive(currentDriveMode)
997+ }, 3_000 )
998+ }
999+ }
1000+ }
1001+
9091002 private fun applyDriveModeUI (mode : DriveMode ) {
9101003 currentDriveMode = mode
1004+ refreshCustomDrive(mode)
9111005 driveModeButtons.forEach { (m, btn) ->
9121006 val (bg, text) = when {
9131007 m != mode -> colorInactive to colorTextInactive
0 commit comments