davidliu

permissions toggle

@@ -72,6 +72,9 @@ class CallViewModel( @@ -72,6 +72,9 @@ class CallViewModel(
72 private val mutableDataReceived = MutableSharedFlow<String>() 72 private val mutableDataReceived = MutableSharedFlow<String>()
73 val dataReceived = mutableDataReceived 73 val dataReceived = mutableDataReceived
74 74
  75 + private val mutablePermissionAllowed = MutableStateFlow(true)
  76 + val permissionAllowed = mutablePermissionAllowed.hide()
  77 +
75 init { 78 init {
76 viewModelScope.launch { 79 viewModelScope.launch {
77 80
@@ -238,6 +241,11 @@ class CallViewModel( @@ -238,6 +241,11 @@ class CallViewModel(
238 room.value?.localParticipant?.publishData(message.toByteArray(Charsets.UTF_8)) 241 room.value?.localParticipant?.publishData(message.toByteArray(Charsets.UTF_8))
239 } 242 }
240 } 243 }
  244 +
  245 + fun toggleSubscriptionPermissions() {
  246 + mutablePermissionAllowed.value = !mutablePermissionAllowed.value
  247 + room.value?.localParticipant?.setTrackSubscriptionPermissions(mutablePermissionAllowed.value)
  248 + }
241 } 249 }
242 250
243 private fun <T> LiveData<T>.hide(): LiveData<T> = this 251 private fun <T> LiveData<T>.hide(): LiveData<T> = this
  1 +<!-- drawable/account_cancel.xml -->
  2 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:height="24dp"
  4 + android:width="24dp"
  5 + android:viewportWidth="24"
  6 + android:viewportHeight="24">
  7 + <path android:fillColor="#000" android:pathData="M10 4A4 4 0 0 0 6 8A4 4 0 0 0 10 12A4 4 0 0 0 14 8A4 4 0 0 0 10 4M17.5 13C15 13 13 15 13 17.5C13 20 15 22 17.5 22C20 22 22 20 22 17.5C22 15 20 13 17.5 13M10 14C5.58 14 2 15.79 2 18V20H11.5A6.5 6.5 0 0 1 11 17.5A6.5 6.5 0 0 1 11.95 14.14C11.32 14.06 10.68 14 10 14M17.5 14.5C19.16 14.5 20.5 15.84 20.5 17.5C20.5 18.06 20.35 18.58 20.08 19L16 14.92C16.42 14.65 16.94 14.5 17.5 14.5M14.92 16L19 20.08C18.58 20.35 18.06 20.5 17.5 20.5C15.84 20.5 14.5 19.16 14.5 17.5C14.5 16.94 14.65 16.42 14.92 16Z" />
  8 +</vector>
  1 +<!-- drawable/account_cancel_outline.xml -->
  2 +<vector xmlns:android="http://schemas.android.com/apk/res/android"
  3 + android:height="24dp"
  4 + android:width="24dp"
  5 + android:viewportWidth="24"
  6 + android:viewportHeight="24">
  7 + <path android:fillColor="#000" android:pathData="M10 4A4 4 0 0 0 6 8A4 4 0 0 0 10 12A4 4 0 0 0 14 8A4 4 0 0 0 10 4M10 6A2 2 0 0 1 12 8A2 2 0 0 1 10 10A2 2 0 0 1 8 8A2 2 0 0 1 10 6M10 13C7.33 13 2 14.33 2 17V20H11.5A6.5 6.5 0 0 1 11.03 18.1H3.9V17C3.9 16.36 7.03 14.9 10 14.9C10.5 14.9 11 14.95 11.5 15.03A6.5 6.5 0 0 1 12.55 13.29C11.61 13.1 10.71 13 10 13M17.5 13C15 13 13 15 13 17.5C13 20 15 22 17.5 22C20 22 22 20 22 17.5C22 15 20 13 17.5 13M17.5 14.5C19.16 14.5 20.5 15.84 20.5 17.5C20.5 18.06 20.35 18.58 20.08 19L16 14.92C16.42 14.65 16.94 14.5 17.5 14.5M14.92 16L19 20.08C18.58 20.35 18.06 20.5 17.5 20.5C15.84 20.5 14.5 19.16 14.5 17.5C14.5 16.94 14.65 16.42 14.92 16Z" />
  8 +</vector>
@@ -96,6 +96,7 @@ class CallActivity : AppCompatActivity() { @@ -96,6 +96,7 @@ class CallActivity : AppCompatActivity() {
96 val videoEnabled by viewModel.cameraEnabled.observeAsState(true) 96 val videoEnabled by viewModel.cameraEnabled.observeAsState(true)
97 val flipButtonEnabled by viewModel.flipButtonVideoEnabled.observeAsState(true) 97 val flipButtonEnabled by viewModel.flipButtonVideoEnabled.observeAsState(true)
98 val screencastEnabled by viewModel.screenshareEnabled.observeAsState(false) 98 val screencastEnabled by viewModel.screenshareEnabled.observeAsState(false)
  99 + val permissionAllowed by viewModel.permissionAllowed.collectAsState()
99 Content( 100 Content(
100 room, 101 room,
101 participants, 102 participants,
@@ -105,6 +106,7 @@ class CallActivity : AppCompatActivity() { @@ -105,6 +106,7 @@ class CallActivity : AppCompatActivity() {
105 videoEnabled, 106 videoEnabled,
106 flipButtonEnabled, 107 flipButtonEnabled,
107 screencastEnabled, 108 screencastEnabled,
  109 + permissionAllowed = permissionAllowed,
108 onExitClick = { finish() }, 110 onExitClick = { finish() },
109 onSendMessage = { viewModel.sendData(it) } 111 onSendMessage = { viewModel.sendData(it) }
110 ) 112 )
@@ -149,6 +151,7 @@ class CallActivity : AppCompatActivity() { @@ -149,6 +151,7 @@ class CallActivity : AppCompatActivity() {
149 videoEnabled: Boolean = true, 151 videoEnabled: Boolean = true,
150 flipButtonEnabled: Boolean = true, 152 flipButtonEnabled: Boolean = true,
151 screencastEnabled: Boolean = false, 153 screencastEnabled: Boolean = false,
  154 + permissionAllowed: Boolean = true,
152 onExitClick: () -> Unit = {}, 155 onExitClick: () -> Unit = {},
153 error: Throwable? = null, 156 error: Throwable? = null,
154 onSnackbarDismiss: () -> Unit = {}, 157 onSnackbarDismiss: () -> Unit = {},
@@ -210,7 +213,7 @@ class CallActivity : AppCompatActivity() { @@ -210,7 +213,7 @@ class CallActivity : AppCompatActivity() {
210 } 213 }
211 214
212 // Control bar for any switches such as mic/camera enable/disable. 215 // Control bar for any switches such as mic/camera enable/disable.
213 - Row( 216 + Column(
214 modifier = Modifier 217 modifier = Modifier
215 .padding(top = 10.dp, bottom = 20.dp) 218 .padding(top = 10.dp, bottom = 20.dp)
216 .fillMaxWidth() 219 .fillMaxWidth()
@@ -219,141 +222,170 @@ class CallActivity : AppCompatActivity() { @@ -219,141 +222,170 @@ class CallActivity : AppCompatActivity() {
219 width = Dimension.fillToConstraints 222 width = Dimension.fillToConstraints
220 height = Dimension.wrapContent 223 height = Dimension.wrapContent
221 }, 224 },
222 - horizontalArrangement = Arrangement.SpaceEvenly,  
223 - verticalAlignment = Alignment.Bottom, 225 + verticalArrangement = Arrangement.SpaceEvenly,
  226 + horizontalAlignment = Alignment.CenterHorizontally
224 ) { 227 ) {
  228 +
225 val controlSize = 40.dp 229 val controlSize = 40.dp
226 val controlPadding = 4.dp 230 val controlPadding = 4.dp
227 - Surface(  
228 - onClick = { viewModel.setMicEnabled(!micEnabled) },  
229 - indication = rememberRipple(false),  
230 - modifier = Modifier  
231 - .size(controlSize)  
232 - .padding(controlPadding)  
233 - ) {  
234 - val resource =  
235 - if (micEnabled) R.drawable.outline_mic_24 else R.drawable.outline_mic_off_24  
236 - Icon(  
237 - painterResource(id = resource),  
238 - contentDescription = "Mic",  
239 - tint = Color.White,  
240 - )  
241 - }  
242 - Surface(  
243 - onClick = { viewModel.setCameraEnabled(!videoEnabled) },  
244 - indication = rememberRipple(false),  
245 - modifier = Modifier  
246 - .size(controlSize)  
247 - .padding(controlPadding)  
248 - ) {  
249 - val resource =  
250 - if (videoEnabled) R.drawable.outline_videocam_24 else R.drawable.outline_videocam_off_24  
251 - Icon(  
252 - painterResource(id = resource),  
253 - contentDescription = "Video",  
254 - tint = Color.White,  
255 - )  
256 - }  
257 - Surface(  
258 - onClick = { viewModel.flipCamera() },  
259 - indication = rememberRipple(false),  
260 - modifier = Modifier  
261 - .size(controlSize)  
262 - .padding(controlPadding)  
263 - ) {  
264 - Icon(  
265 - painterResource(id = R.drawable.outline_flip_camera_android_24),  
266 - contentDescription = "Flip Camera",  
267 - tint = Color.White,  
268 - )  
269 - }  
270 - Surface(  
271 - onClick = {  
272 - if (!screencastEnabled) {  
273 - requestMediaProjection()  
274 - } else {  
275 - viewModel.stopScreenCapture()  
276 - }  
277 - },  
278 - indication = rememberRipple(false),  
279 - modifier = Modifier  
280 - .size(controlSize)  
281 - .padding(controlPadding) 231 + Row(
  232 + horizontalArrangement = Arrangement.SpaceEvenly,
  233 + verticalAlignment = Alignment.Bottom,
282 ) { 234 ) {
283 - val resource =  
284 - if (screencastEnabled) R.drawable.baseline_cast_connected_24 else R.drawable.baseline_cast_24  
285 - Icon(  
286 - painterResource(id = resource),  
287 - contentDescription = "Flip Camera",  
288 - tint = Color.White,  
289 - )  
290 - } 235 + Surface(
  236 + onClick = { viewModel.setMicEnabled(!micEnabled) },
  237 + indication = rememberRipple(false),
  238 + modifier = Modifier
  239 + .size(controlSize)
  240 + .padding(controlPadding)
  241 + ) {
  242 + val resource =
  243 + if (micEnabled) R.drawable.outline_mic_24 else R.drawable.outline_mic_off_24
  244 + Icon(
  245 + painterResource(id = resource),
  246 + contentDescription = "Mic",
  247 + tint = Color.White,
  248 + )
  249 + }
  250 + Surface(
  251 + onClick = { viewModel.setCameraEnabled(!videoEnabled) },
  252 + indication = rememberRipple(false),
  253 + modifier = Modifier
  254 + .size(controlSize)
  255 + .padding(controlPadding)
  256 + ) {
  257 + val resource =
  258 + if (videoEnabled) R.drawable.outline_videocam_24 else R.drawable.outline_videocam_off_24
  259 + Icon(
  260 + painterResource(id = resource),
  261 + contentDescription = "Video",
  262 + tint = Color.White,
  263 + )
  264 + }
  265 + Surface(
  266 + onClick = { viewModel.flipCamera() },
  267 + indication = rememberRipple(false),
  268 + modifier = Modifier
  269 + .size(controlSize)
  270 + .padding(controlPadding)
  271 + ) {
  272 + Icon(
  273 + painterResource(id = R.drawable.outline_flip_camera_android_24),
  274 + contentDescription = "Flip Camera",
  275 + tint = Color.White,
  276 + )
  277 + }
  278 + Surface(
  279 + onClick = {
  280 + if (!screencastEnabled) {
  281 + requestMediaProjection()
  282 + } else {
  283 + viewModel.stopScreenCapture()
  284 + }
  285 + },
  286 + indication = rememberRipple(false),
  287 + modifier = Modifier
  288 + .size(controlSize)
  289 + .padding(controlPadding)
  290 + ) {
  291 + val resource =
  292 + if (screencastEnabled) R.drawable.baseline_cast_connected_24 else R.drawable.baseline_cast_24
  293 + Icon(
  294 + painterResource(id = resource),
  295 + contentDescription = "Flip Camera",
  296 + tint = Color.White,
  297 + )
  298 + }
291 299
292 - var showMessageDialog by remember { mutableStateOf(false) }  
293 - var messageToSend by remember { mutableStateOf("") }  
294 - Surface(  
295 - onClick = { showMessageDialog = true },  
296 - indication = rememberRipple(false),  
297 - modifier = Modifier  
298 - .size(controlSize)  
299 - .padding(controlPadding)  
300 - ) {  
301 - Icon(  
302 - painterResource(id = R.drawable.baseline_chat_24),  
303 - contentDescription = "Send Message",  
304 - tint = Color.White,  
305 - )  
306 - } 300 + var showMessageDialog by remember { mutableStateOf(false) }
  301 + var messageToSend by remember { mutableStateOf("") }
  302 + Surface(
  303 + onClick = { showMessageDialog = true },
  304 + indication = rememberRipple(false),
  305 + modifier = Modifier
  306 + .size(controlSize)
  307 + .padding(controlPadding)
  308 + ) {
  309 + Icon(
  310 + painterResource(id = R.drawable.baseline_chat_24),
  311 + contentDescription = "Send Message",
  312 + tint = Color.White,
  313 + )
  314 + }
307 315
308 - if (showMessageDialog) {  
309 - AlertDialog(  
310 - onDismissRequest = {  
311 - showMessageDialog = false  
312 - messageToSend = ""  
313 - },  
314 - title = {  
315 - Text(text = "Send Message")  
316 - },  
317 - text = {  
318 - OutlinedTextField(  
319 - value = messageToSend,  
320 - onValueChange = { messageToSend = it },  
321 - label = { Text("Message") },  
322 - modifier = Modifier.fillMaxWidth(),  
323 - )  
324 - },  
325 - confirmButton = {  
326 - Button(  
327 - onClick = {  
328 - onSendMessage(messageToSend)  
329 - showMessageDialog = false  
330 - messageToSend = ""  
331 - }  
332 - ) { Text("Send") }  
333 - },  
334 - dismissButton = {  
335 - Button(  
336 - onClick = {  
337 - showMessageDialog = false  
338 - messageToSend = ""  
339 - }  
340 - ) { Text("Cancel") }  
341 - },  
342 - backgroundColor = Color.Black,  
343 - ) 316 + if (showMessageDialog) {
  317 + AlertDialog(
  318 + onDismissRequest = {
  319 + showMessageDialog = false
  320 + messageToSend = ""
  321 + },
  322 + title = {
  323 + Text(text = "Send Message")
  324 + },
  325 + text = {
  326 + OutlinedTextField(
  327 + value = messageToSend,
  328 + onValueChange = { messageToSend = it },
  329 + label = { Text("Message") },
  330 + modifier = Modifier.fillMaxWidth(),
  331 + )
  332 + },
  333 + confirmButton = {
  334 + Button(
  335 + onClick = {
  336 + onSendMessage(messageToSend)
  337 + showMessageDialog = false
  338 + messageToSend = ""
  339 + }
  340 + ) { Text("Send") }
  341 + },
  342 + dismissButton = {
  343 + Button(
  344 + onClick = {
  345 + showMessageDialog = false
  346 + messageToSend = ""
  347 + }
  348 + ) { Text("Cancel") }
  349 + },
  350 + backgroundColor = Color.Black,
  351 + )
  352 + }
  353 + Surface(
  354 + onClick = { onExitClick() },
  355 + indication = rememberRipple(false),
  356 + modifier = Modifier
  357 + .size(controlSize)
  358 + .padding(controlPadding)
  359 + ) {
  360 + Icon(
  361 + painterResource(id = R.drawable.ic_baseline_cancel_24),
  362 + contentDescription = "Flip Camera",
  363 + tint = Color.White,
  364 + )
  365 + }
344 } 366 }
345 - Surface(  
346 - onClick = { onExitClick() },  
347 - indication = rememberRipple(false),  
348 - modifier = Modifier  
349 - .size(controlSize)  
350 - .padding(controlPadding) 367 +
  368 + Spacer(modifier = Modifier.height(10.dp))
  369 +
  370 + Row(
  371 + horizontalArrangement = Arrangement.SpaceEvenly,
  372 + verticalAlignment = Alignment.Bottom,
351 ) { 373 ) {
352 - Icon(  
353 - painterResource(id = R.drawable.ic_baseline_cancel_24),  
354 - contentDescription = "Flip Camera",  
355 - tint = Color.White,  
356 - ) 374 + Surface(
  375 + onClick = { viewModel.toggleSubscriptionPermissions() },
  376 + indication = rememberRipple(false),
  377 + modifier = Modifier
  378 + .size(controlSize)
  379 + .padding(controlPadding)
  380 + ) {
  381 + val resource =
  382 + if (permissionAllowed) R.drawable.account_cancel_outline else R.drawable.account_cancel
  383 + Icon(
  384 + painterResource(id = resource),
  385 + contentDescription = "Permissions",
  386 + tint = Color.White,
  387 + )
  388 + }
357 } 389 }
358 } 390 }
359 391