Skip to content

Commit 3b78bf9

Browse files
committed
fix next/preivous radio for SWI133
1 parent f630b70 commit 3b78bf9

1 file changed

Lines changed: 40 additions & 53 deletions

File tree

‎app/src/main/java/com/mg4/control/hardware/MG4Hardware.kt‎

Lines changed: 40 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -4031,12 +4031,17 @@ object MG4Hardware {
40314031
* voie a réellement agi.
40324032
*/
40334033
private fun mediaTransact(action: String, descripteur: String, code: Int,
4034-
pkg: String = MEDIA_PKG, cls: String? = MEDIA_CLS): Boolean {
4034+
pkg: String = MEDIA_PKG, cls: String? = MEDIA_CLS,
4035+
arg: Int? = null): Boolean {
40354036
val binder = serviceBinder(action, pkg, cls) ?: return false
40364037
val data = Parcel.obtain()
40374038
val reply = Parcel.obtain()
40384039
return try {
40394040
data.writeInterfaceToken(descripteur)
4041+
// ⚠️ Toutes ces méthodes ne sont pas sans argument. Omettre celui qu'attend le
4042+
// service ne provoque aucune erreur : il lit un parcel vide et obtient 0 — une
4043+
// valeur parfaitement valide, mais qui n'est pas celle voulue.
4044+
if (arg != null) data.writeInt(arg)
40404045
val ok = binder.transact(code, data, reply, 0)
40414046
reply.readException()
40424047
AppLogger.i(MEDIA_TAG, "${action.substringAfterLast('.')} tx=0x${Integer.toHexString(code)} → $ok")
@@ -4076,20 +4081,25 @@ object MG4Hardware {
40764081
}
40774082
}
40784083

4084+
/** Bande et état de la radio, lus en une seule interrogation. */
4085+
private data class InfoRadio(val type: Int, val enLecture: Boolean)
4086+
40794087
/**
4080-
* La radio joue-t-elle ? `null` si on ne peut pas conclure.
4088+
* Lit le `RadioBean` courant : bande écoutée et lecture en cours. `null` si illisible.
4089+
*
4090+
* Les deux informations viennent du même appel parce qu'elles sont dans le même bean, et
4091+
* qu'une seconde interrogation donnerait au mieux le même résultat, au pire un résultat
4092+
* décalé.
40814093
*
4082-
* `isPlaying()` du SDK radio se résume à `getCurrentRadioInfo().getRadioState() == 1`. Il
4083-
* faut donc lire le `RadioBean` renvoyé, dans l'ordre exact de son `writeToParcel` :
4084-
* enable(byte), name(String), rds(byte), cover(String), fréquence(int), type(int),
4085-
* **état(int)**. C'est le seul endroit du projet couplé à la sérialisation d'un bean SAIC —
4086-
* tout est sous try/catch, et un échec vaut `null`, jamais une supposition.
4094+
* ⚠️ Lecture dans l'ordre EXACT de `writeToParcel` : enable(byte), nom(String), rds(byte),
4095+
* pochette(String), fréquence(int), **type(int)**, **état(int)**. C'est le seul endroit du
4096+
* projet couplé à la sérialisation d'un bean SAIC — tout est sous try/catch, et un échec
4097+
* vaut `null`, jamais une supposition.
40874098
*
4088-
* Sans cette lecture, `isMusicActive` servait de repli : or il est **faux même quand la
4089-
* radio joue**, son flux ne passant pas par le canal musique. On envoyait donc toujours
4090-
* « lecture », jamais « pause » — d'où un raccourci sans effet.
4099+
* `isPlaying()` du SDK radio se résume à `getRadioState() == 1` ; `next()`/`previous()`, eux,
4100+
* reçoivent `getRadioType()`.
40914101
*/
4092-
private fun radioEnLecture(): Boolean? {
4102+
private fun radioInfo(): InfoRadio? {
40934103
val binder = serviceBinder(RADIO_ACT, RADIO_PKG, null) ?: return null
40944104
val data = Parcel.obtain()
40954105
val reply = Parcel.obtain()
@@ -4100,10 +4110,11 @@ object MG4Hardware {
41004110
if (reply.readInt() == 0) return null // bean nul
41014111
reply.readByte(); reply.readString() // enable, nom
41024112
reply.readByte(); reply.readString() // rds, pochette
4103-
reply.readInt(); reply.readInt() // fréquence, type
4113+
reply.readInt() // fréquence
4114+
val type = reply.readInt()
41044115
val etat = reply.readInt()
4105-
AppLogger.i(MEDIA_TAG, "état radio = $etat (1 = en lecture)")
4106-
etat == 1
4116+
AppLogger.i(MEDIA_TAG, "radio : type=$type état=$etat (1 = en lecture)")
4117+
InfoRadio(type, etat == 1)
41074118
} catch (e: Exception) {
41084119
AppLogger.w(MEDIA_TAG, "état radio illisible : ${(e.cause ?: e).message}")
41094120
null
@@ -4113,43 +4124,6 @@ object MG4Hardware {
41134124
}
41144125
}
41154126

4116-
/**
4117-
* CarPlay / Android Auto joue-t-il ? `null` si on ne peut pas conclure.
4118-
*
4119-
* `getLastCpAaAudioInfoBean` (tx 0x7) rend un `AudioInfoBean` dont le champ `mPlayState`
4120-
* vaut 3 en lecture — la carte média du launcher le compare à cette valeur exacte. Il faut
4121-
* dérouler le bean dans l'ordre de son `writeToParcel` : id(long), durée(long), puis sept
4122-
* chaînes, deux longs, une chaîne, et enfin **l'état(int)**.
4123-
*
4124-
* Sans cette lecture, la projection retombait sur `isMusicActive`, qui reste vrai deux à
4125-
* trois secondes après une pause : le raccourci renvoyait une pause au lieu d'une reprise,
4126-
* et il fallait patienter avant que la bascule redevienne possible.
4127-
*/
4128-
private fun cpAaEnLecture(): Boolean? {
4129-
val binder = serviceBinder(ACT_CPAA, MEDIA_PKG, MEDIA_CLS) ?: return null
4130-
val data = Parcel.obtain()
4131-
val reply = Parcel.obtain()
4132-
return try {
4133-
data.writeInterfaceToken(DESC_CPAA)
4134-
binder.transact(0x7, data, reply, 0)
4135-
reply.readException()
4136-
if (reply.readInt() == 0) return null // bean nul : rien à conclure
4137-
reply.readLong(); reply.readLong() // id, durée
4138-
repeat(7) { reply.readString() } // nom, pochette, chemin, artiste, utilisateur, avatar, album
4139-
reply.readLong(); reply.readLong() // ajout, dernière lecture
4140-
reply.readString() // position lisible
4141-
val etat = reply.readInt()
4142-
AppLogger.i(MEDIA_TAG, "état projection = $etat ($PLAYER_STATUS_START = en lecture)")
4143-
etat == PLAYER_STATUS_START
4144-
} catch (e: Exception) {
4145-
AppLogger.w(MEDIA_TAG, "état projection illisible : ${(e.cause ?: e).message}")
4146-
null
4147-
} finally {
4148-
data.recycle()
4149-
reply.recycle()
4150-
}
4151-
}
4152-
41534127
/** Source audio réellement active, ou -1 si le service SAIC ne répond pas. */
41544128
private fun mediaSourceCourante(): Int {
41554129
val v = mediaLireInt(ACT_STATUS, DESC_STATUS, 0x9) ?: return -1 // getCurrentMediaSource
@@ -4345,16 +4319,29 @@ object MG4Hardware {
43454319
cible in SRC_RADIO_MIN..SRC_RADIO_MAX -> {
43464320
// La radio a son propre service : next/previous y changent de station, et
43474321
// srcPlayRadio/srcPauseRadio sont les commandes que le launcher utilise.
4322+
val info = radioInfo()
43484323
// Un état illisible est traité comme « en lecture » : c'est l'état normal
43494324
// d'une radio qui est la source active, et une pause de trop se corrige d'un
43504325
// second appui — l'inverse laisserait le raccourci sans effet.
4351-
val joue = radioEnLecture() != false
4326+
val joue = info?.enLecture != false
43524327
val code = when (cmd) {
43534328
CmdMedia.SUIVANT -> RADIO_NEXT
43544329
CmdMedia.PRECEDENT -> RADIO_PREV
43554330
CmdMedia.LECTURE_PAUSE -> if (joue) RADIO_PAUSE else RADIO_PLAY
43564331
}
4357-
val ok = mediaTransact(RADIO_ACT, DESC_RADIO, code, RADIO_PKG, null)
4332+
4333+
// ⚠️ `next` et `previous` attendent la BANDE écoutée, contrairement à
4334+
// play/pause. Sans elle le service lit 0 et bascule en FM : mesuré sur SWI133,
4335+
// une station DAB retombait systématiquement en FM. Bande inconnue = on
4336+
// s'abstient, plutôt que de changer de bande dans le dos de l'utilisateur.
4337+
val bande = if (cmd == CmdMedia.LECTURE_PAUSE) null else info?.type
4338+
if (cmd != CmdMedia.LECTURE_PAUSE && bande == null) {
4339+
AppLogger.w(MEDIA_TAG, "radio : bande illisible — aucune action " +
4340+
"(l'envoyer sans la bande ferait basculer en FM)")
4341+
return false
4342+
}
4343+
4344+
val ok = mediaTransact(RADIO_ACT, DESC_RADIO, code, RADIO_PKG, null, bande)
43584345
// Repli DANS la source uniquement : la façade générique s'adresse à la source
43594346
// courante, elle ne peut donc pas en réveiller une autre.
43604347
if (ok) true else mediaTransact(ACT_MEDIA, DESC_MEDIA, when (cmd) {

0 commit comments

Comments
 (0)