Skip to content

Commit 4d10444

Browse files
committed
Add mute button on Android
1 parent 93e1f29 commit 4d10444

File tree

8 files changed

+177
-53
lines changed

8 files changed

+177
-53
lines changed

Android/app/src/main/java/io/github/teamclouday/AndroidMic/domain/audio/MicAudioManager.kt

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,8 @@ class MicAudioManager(
4141
private val bufferFloatConvert: ByteBuffer
4242
private var streamJob: Job? = null
4343

44+
private var isMuted = false
45+
4446
init {
4547
// check microphone
4648
require(ctx.packageManager.hasSystemFeature(PackageManager.FEATURE_MICROPHONE)) {
@@ -92,6 +94,12 @@ class MicAudioManager(
9294
// launch in scope so infinite loop will be canceled when scope exits
9395
streamJob = scope.launch {
9496
while (true) {
97+
98+
if (isMuted) {
99+
delay(RECORD_DELAY_MS)
100+
continue
101+
}
102+
95103
if (recorder.state != AudioRecord.STATE_INITIALIZED || recorder.recordingState != AudioRecord.RECORDSTATE_RECORDING) {
96104
delay(RECORD_DELAY_MS)
97105
continue
@@ -143,6 +151,14 @@ class MicAudioManager(
143151
}
144152
}
145153

154+
fun mute() {
155+
isMuted = true
156+
}
157+
158+
fun unmute() {
159+
isMuted = false
160+
}
161+
146162
// start recording
147163
fun start() {
148164
recorder.startRecording()

Android/app/src/main/java/io/github/teamclouday/AndroidMic/domain/service/Command.kt

Lines changed: 44 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -12,10 +12,16 @@ import io.github.teamclouday.AndroidMic.utils.Either
1212
import io.github.teamclouday.AndroidMic.utils.checkIp
1313
import io.github.teamclouday.AndroidMic.utils.checkPort
1414

15-
15+
/**
16+
* Service -> UI
17+
*/
1618
private const val ID_MSG: String = "ID_MSG"
17-
private const val ID_STATE: String = "ID_STATE"
19+
private const val ID_CONNECTION_STATE: String = "ID_CONNECTION_STATE"
20+
private const val ID_MUTE_STATE: String = "ID_MUTE_STATE"
1821

22+
/**
23+
* UI -> Service
24+
*/
1925
private const val ID_MODE: String = "ID_MODE"
2026

2127
private const val ID_IP: String = "ID_IP"
@@ -27,7 +33,7 @@ private const val ID_AUDIO_FORMAT: String = "ID_AUDIO_FORMAT"
2733

2834

2935
/**
30-
* Commands UI -> Service
36+
* UI -> Service
3137
*/
3238
enum class Command {
3339
StartStream,
@@ -36,18 +42,12 @@ enum class Command {
3642

3743
// called when the ui is bind
3844
BindCheck,
39-
}
40-
41-
fun Bundle.getOrdinal(key: String): Int? {
42-
val v = this.getInt(key, Int.MIN_VALUE);
4345

44-
return if (v == Int.MIN_VALUE) {
45-
null
46-
} else {
47-
v
48-
}
46+
Mute,
47+
Unmute,
4948
}
5049

50+
5151
data class CommandData(
5252
val command: Command,
5353
val mode: Mode? = null,
@@ -134,29 +134,38 @@ data class CommandData(
134134

135135

136136
/**
137-
* Response Service -> UI
137+
* Service -> UI
138138
*/
139-
enum class Response {
139+
enum class ResponseKind {
140140
Standard,
141141
}
142142

143-
enum class ServiceState {
143+
private enum class ConnectionState {
144144
Connected,
145-
Disconnected,
145+
Disconnected;
146+
}
147+
148+
private enum class MuteState {
149+
Muted,
150+
Unmuted,
146151
}
147152

148153
data class ResponseData(
149-
val state: ServiceState? = null,
150154
val msg: String? = null,
151-
val kind: Response = Response.Standard,
155+
val isConnected: Boolean? = null,
156+
val isMuted: Boolean? = null,
157+
val kind: ResponseKind = ResponseKind.Standard,
152158
) {
153159

154160

155161
companion object {
156162
fun fromMessage(msg: Message): ResponseData {
157163
return ResponseData(
158-
kind = Response.entries[msg.what],
159-
state = msg.data.getOrdinal(ID_STATE)?.let { ServiceState.entries[it] },
164+
kind = ResponseKind.entries[msg.what],
165+
isConnected = msg.data.getOrdinal(ID_CONNECTION_STATE)
166+
?.let { ConnectionState.entries[it] == ConnectionState.Connected },
167+
isMuted = msg.data.getOrdinal(ID_MUTE_STATE)
168+
?.let { MuteState.entries[it] == MuteState.Muted },
160169
msg = msg.data.getString(ID_MSG)
161170
)
162171
}
@@ -168,7 +177,13 @@ data class ResponseData(
168177
val r = Bundle()
169178

170179
this.msg?.let { r.putString(ID_MSG, it) }
171-
this.state?.let { r.putInt(ID_STATE, it.ordinal) }
180+
this.isConnected?.let {
181+
r.putInt(
182+
ID_CONNECTION_STATE,
183+
(if (it) ConnectionState.Connected else ConnectionState.Disconnected).ordinal
184+
)
185+
}
186+
this.isMuted?.let { r.putInt(ID_MUTE_STATE, (if (it) MuteState.Muted else MuteState.Unmuted).ordinal) }
172187

173188
val reply = Message.obtain()
174189
reply.data = r
@@ -180,4 +195,12 @@ data class ResponseData(
180195
}
181196

182197

198+
fun Bundle.getOrdinal(key: String): Int? {
199+
val v = this.getInt(key, Int.MIN_VALUE);
183200

201+
return if (v == Int.MIN_VALUE) {
202+
null
203+
} else {
204+
v
205+
}
206+
}

Android/app/src/main/java/io/github/teamclouday/AndroidMic/domain/service/ForegroundService.kt

Lines changed: 28 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,7 @@ import kotlinx.coroutines.launch
2727
data class ServiceStates(
2828
var isStreamStarted: Boolean = false,
2929
var isAudioStarted: Boolean = false,
30+
var isMuted: Boolean = false,
3031
var mode: Mode = Mode.WIFI
3132
)
3233

@@ -52,6 +53,16 @@ class ForegroundService : Service() {
5253
Command.BindCheck -> {
5354
uiMessenger = msg.replyTo
5455
}
56+
57+
Command.Mute -> {
58+
states.isMuted = true
59+
managerAudio?.mute()
60+
}
61+
62+
Command.Unmute -> {
63+
states.isMuted = false
64+
managerAudio?.unmute()
65+
}
5566
}
5667

5768
}
@@ -181,13 +192,15 @@ class ForegroundService : Service() {
181192

182193
// start streaming
183194
private fun startStream(msg: CommandData, replyTo: Messenger) {
195+
states.isMuted = false
184196
// check connection state
185197
if (states.isStreamStarted) {
186198
reply(
187199
replyTo,
188200
ResponseData(
189-
ServiceState.Connected,
190-
this.getString(R.string.stream_already_started)
201+
msg = this.getString(R.string.stream_already_started),
202+
isConnected = true,
203+
isMuted = states.isMuted,
191204
)
192205
)
193206
return
@@ -206,8 +219,9 @@ class ForegroundService : Service() {
206219
reply(
207220
replyTo,
208221
ResponseData(
209-
ServiceState.Disconnected,
210-
applicationContext.getString(R.string.error) + e.message
222+
msg = applicationContext.getString(R.string.error) + e.message,
223+
isConnected = false,
224+
isMuted = states.isMuted,
211225
)
212226
)
213227
return
@@ -219,8 +233,9 @@ class ForegroundService : Service() {
219233
reply(
220234
replyTo,
221235
ResponseData(
222-
ServiceState.Disconnected,
223-
applicationContext.getString(R.string.failed_to_connect)
236+
msg = applicationContext.getString(R.string.failed_to_connect),
237+
isConnected = false,
238+
isMuted = states.isMuted,
224239
)
225240
)
226241
shutdownStream()
@@ -245,8 +260,9 @@ class ForegroundService : Service() {
245260
reply(
246261
replyTo,
247262
ResponseData(
248-
ServiceState.Connected,
249-
applicationContext.getString(R.string.connected_device) + managerStream?.getInfo()
263+
msg = applicationContext.getString(R.string.connected_device) + managerStream?.getInfo(),
264+
isConnected = true,
265+
isMuted = states.isMuted,
250266
)
251267
)
252268

@@ -263,8 +279,9 @@ class ForegroundService : Service() {
263279
reply(
264280
uiMessenger,
265281
ResponseData(
266-
ServiceState.Disconnected,
267-
applicationContext.getString(R.string.device_disconnected)
282+
msg = applicationContext.getString(R.string.device_disconnected),
283+
isConnected = false,
284+
isMuted = states.isMuted,
268285
)
269286
)
270287

@@ -279,13 +296,6 @@ class ForegroundService : Service() {
279296
states.isStreamStarted = false
280297
}
281298

282-
private fun isConnected(): ServiceState {
283-
return if (states.isStreamStarted) {
284-
ServiceState.Connected
285-
} else {
286-
ServiceState.Disconnected
287-
}
288-
}
289299

290300
// start mic
291301
private fun startAudio(msg: CommandData, replyTo: Messenger): Boolean {
@@ -349,6 +359,6 @@ class ForegroundService : Service() {
349359
private fun getStatus(replyTo: Messenger) {
350360
Log.d(TAG, "getStatus")
351361

352-
reply(replyTo, ResponseData(isConnected()))
362+
reply(replyTo, ResponseData(isConnected = states.isStreamStarted, isMuted = states.isMuted))
353363
}
354364
}

Android/app/src/main/java/io/github/teamclouday/AndroidMic/ui/MainViewModel.kt

Lines changed: 27 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ import io.github.teamclouday.AndroidMic.SampleRates
2121
import io.github.teamclouday.AndroidMic.Themes
2222
import io.github.teamclouday.AndroidMic.domain.service.Command
2323
import io.github.teamclouday.AndroidMic.domain.service.CommandData
24-
import io.github.teamclouday.AndroidMic.domain.service.Response
24+
import io.github.teamclouday.AndroidMic.domain.service.ResponseKind
2525
import io.github.teamclouday.AndroidMic.domain.service.ResponseData
26-
import io.github.teamclouday.AndroidMic.domain.service.ServiceState
2726
import io.github.teamclouday.AndroidMic.ui.utils.UiHelper
2827
import io.github.teamclouday.AndroidMic.utils.Either
2928
import kotlinx.coroutines.launch
@@ -51,6 +50,8 @@ class MainViewModel : ViewModel() {
5150
val isStreamStarted = mutableStateOf(false)
5251
val isButtonConnectClickable = mutableStateOf(false)
5352

53+
val isMuted = mutableStateOf(false)
54+
5455
init {
5556
Log.d(TAG, "init")
5657
}
@@ -61,10 +62,14 @@ class MainViewModel : ViewModel() {
6162
val data = ResponseData.fromMessage(msg)
6263

6364
when (data.kind) {
64-
Response.Standard -> {
65-
data.state?.let {
65+
ResponseKind.Standard -> {
66+
data.isConnected?.let {
6667
isButtonConnectClickable.value = true
67-
isStreamStarted.value = it == ServiceState.Connected
68+
isStreamStarted.value = it
69+
}
70+
71+
data.isMuted?.let {
72+
isMuted.value = it
6873
}
6974

7075
data.msg?.let {
@@ -89,14 +94,31 @@ class MainViewModel : ViewModel() {
8994

9095
isStreamStarted.value = false
9196
isButtonConnectClickable.value = false
97+
isMuted.value = false
9298

9399
val msg = CommandData(Command.BindCheck).toCommandMsg()
94100
msg.replyTo = messenger
95101
service?.send(msg)
96102
}
97103

104+
fun onMuteSwitch() {
105+
if (!isBound) return
106+
107+
val message = if (isMuted.value) {
108+
isMuted.value = false
109+
CommandData(Command.Unmute)
110+
} else {
111+
isMuted.value = true
112+
CommandData(Command.Mute)
113+
}.toCommandMsg()
114+
115+
message.replyTo = messenger
116+
service?.send(message)
117+
}
118+
98119
fun onConnectButton(): Dialogs? {
99120
if (!isBound) return null
121+
isMuted.value = false
100122
val message = if (isStreamStarted.value) {
101123
Log.d(TAG, "onConnectButton: stop stream")
102124
CommandData(Command.StopStream)

0 commit comments

Comments
 (0)