21 changed files with 170 additions and 21 deletions
@ -0,0 +1,72 @@
|
||||
package com.utun.chat.data |
||||
|
||||
import android.media.AudioAttributes |
||||
import android.media.MediaPlayer |
||||
import com.utun.chat.ChatApplication |
||||
import com.utun.chat.R |
||||
|
||||
/** |
||||
* Гудки исходящего звонка (ringback) для вызывающего через зацикленный MediaPlayer. |
||||
* |
||||
* Две фазы с разными гудками: |
||||
* - фаза 1 «пытаемся связаться» — call_connecting (короткие бипы 425 Гц); |
||||
* - фаза 2 «звоним» (пир принял вызов, у него звонит) — call_ringing (длинные гудки). |
||||
* |
||||
* ToneGenerator для call-тонов на современном Android (API 31+) молчит, поэтому |
||||
* гудки — готовые MP3 из res/raw, как входящая мелодия (RingtonePlayer). |
||||
* На ACCEPTED/ENDED/hangup вызывающий останавливает гудок. Идемпотентный stop. |
||||
*/ |
||||
object CallRingback { |
||||
|
||||
@Volatile private var player: MediaPlayer? = null |
||||
|
||||
/** Фаза 1: короткие бипы «устанавливаем соединение». Идемпотентно. */ |
||||
fun startConnecting() { |
||||
start(R.raw.call_connecting, "connecting") |
||||
} |
||||
|
||||
/** Фаза 2: длинные гудки (у пира идёт вызов). Идемпотентно. */ |
||||
fun startRinging() { |
||||
start(R.raw.call_ringing, "ringing") |
||||
} |
||||
|
||||
/** Остановить текущий гудок. Идемпотентно. */ |
||||
fun stop() { |
||||
release() |
||||
LogManager.addLog("INFO", "Ringback", "stopped") |
||||
} |
||||
|
||||
private fun start(resId: Int, tag: String) { |
||||
release() |
||||
val ctx = ChatApplication.instance |
||||
val mp = try { |
||||
MediaPlayer.create(ctx, resId) |
||||
} catch (e: Exception) { |
||||
LogManager.addLog("ERROR", "Ringback", "MediaPlayer.create failed ($tag): ${e.message}") |
||||
null |
||||
} ?: return |
||||
try { |
||||
mp.setAudioAttributes( |
||||
AudioAttributes.Builder() |
||||
.setUsage(AudioAttributes.USAGE_MEDIA) |
||||
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) |
||||
.build() |
||||
) |
||||
mp.isLooping = true |
||||
mp.start() |
||||
} catch (e: Exception) { |
||||
LogManager.addLog("ERROR", "Ringback", "MediaPlayer start failed ($tag): ${e.message}") |
||||
try { mp.release() } catch (_: Exception) {} |
||||
return |
||||
} |
||||
player = mp |
||||
LogManager.addLog("INFO", "Ringback", "started $tag") |
||||
} |
||||
|
||||
private fun release() { |
||||
val p = player ?: return |
||||
player = null |
||||
try { p.stop() } catch (_: Exception) {} |
||||
try { p.release() } catch (_: Exception) {} |
||||
} |
||||
} |
||||
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Loading…
Reference in new issue