Cómo hacer doble pitido con ToneGenerator

El problema

He intentado get un doble pitido en una aplicación que estoy desarrollando una vez que se presiona un button.

El problema es que no logré hacerlo bien. A veces los sonidos se superponen, otras veces juegan más rápido de lo que deberían.

Quería tocar un tono arbitrario, tener una pausa / demora arbitraria y luego volver a tocar el tono.

OBS : He visto algunas soluciones para este problema usando MediaPlayer o la class de Ringtone , pero esta pregunta es sobre cómo lograr esto usando ToneGenerator solamente, sin usar los tonos TONE_PROP_BEEP2 o TONE_PROP_ACK .

Lo que he intentado hasta ahora

Antes que nada, intenté llamar al método startTone() dos veces, pero pronto descubrí que los sonidos se reproducían simultáneamente.

 button.setOnClickListener { val toneG = ToneGenerator(AudioManager.STREAM_ALARM, 100) toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200) toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200) } 

Luego traté de usar Handler de dos maneras.

Primero traté de usar dos de ellos:

 button.setOnClickListener { val toneG = ToneGenerator(AudioManager.STREAM_ALARM, 100) val handler = Handler() handler.postDelayed({ toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200) }, 50) handler.postDelayed({ toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200) }, 350) } 

En segundo lugar, también probé con Thread.sleep() :

 button.setOnClickListener { val toneG = ToneGenerator(AudioManager.STREAM_ALARM, 100) val handler = Handler() handler.postDelayed({ toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200) Thread.sleep(100) toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200) }, 50) } 

Ambas ideas "algo" funcionaron. Pero al presionar el primer button, los pitidos sonaron graciosos, ya que jugaban demasiado rápido, y algunas veces incluso se superponían.

La segunda vez que presioné el Button , los tonos se reprodujeron correctamente.

Puede probar CountDownTimer y ver si funciona mejor. De hecho, probé tu primera opción y no noté el efecto que mencionaste. No estoy seguro de que postDelayed haga otras promises que no sean "Causa que la Runnable r se agregue a la queue de posts, que se ejecute una vez transcurrido el time especificado" , también podría probar postAtTime() lugar de postDelayed()

  button.setOnClickListener { val toneG = ToneGenerator(AudioManager.STREAM_ALARM, 100) val timer = object: CountDownTimer(350,50) { var skipThisInterval = false override fun onFinish() { toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200) } override fun onTick(p0: Long) { if (skipThisInterval) return skipThisInterval = true toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200) } } timer.start() } 

usar postAtTime() lugar de postDelayed() podría funcionar mejor.

  button.setOnClickListener { val toneG = ToneGenerator(AudioManager.STREAM_ALARM, 100) val handler = Handler() val timeNow = SystemClock.uptimeMillis() handler.postAtTime({ toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200) }, 50 + timeNow) handler.postAtTime({ toneG.startTone(ToneGenerator.TONE_CDMA_ALERT_CALL_GUARD, 200) }, 350 + timeNow) }