In-call audio/camera permissions improvements

This commit is contained in:
Sylvain Berfini 2020-09-10 11:19:38 +02:00
parent 1ad5d31426
commit 19fe8dd57b
5 changed files with 108 additions and 33 deletions

View file

@ -19,9 +19,11 @@
*/ */
package org.linphone.activities.call package org.linphone.activities.call
import android.Manifest
import android.annotation.TargetApi import android.annotation.TargetApi
import android.app.KeyguardManager import android.app.KeyguardManager
import android.content.Context import android.content.Context
import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
@ -113,10 +115,12 @@ class IncomingCallActivity : GenericActivity() {
Log.i("[Incoming Call Activity] Asking for RECORD_AUDIO permission") Log.i("[Incoming Call Activity] Asking for RECORD_AUDIO permission")
permissionsRequiredList.add(android.Manifest.permission.RECORD_AUDIO) permissionsRequiredList.add(android.Manifest.permission.RECORD_AUDIO)
} }
if (viewModel.call.currentParams.videoEnabled() && !PermissionHelper.get().hasCameraPermission()) { if (viewModel.call.currentParams.videoEnabled() && !PermissionHelper.get().hasCameraPermission()) {
Log.i("[Incoming Call Activity] Asking for CAMERA permission") Log.i("[Incoming Call Activity] Asking for CAMERA permission")
permissionsRequiredList.add(android.Manifest.permission.CAMERA) permissionsRequiredList.add(android.Manifest.permission.CAMERA)
} }
if (permissionsRequiredList.isNotEmpty()) { if (permissionsRequiredList.isNotEmpty()) {
val permissionsRequired = arrayOfNulls<String>(permissionsRequiredList.size) val permissionsRequired = arrayOfNulls<String>(permissionsRequiredList.size)
permissionsRequiredList.toArray(permissionsRequired) permissionsRequiredList.toArray(permissionsRequired)
@ -124,6 +128,26 @@ class IncomingCallActivity : GenericActivity() {
} }
} }
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == 0) {
for (i in permissions.indices) {
when (permissions[i]) {
Manifest.permission.RECORD_AUDIO -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Incoming Call Activity] RECORD_AUDIO permission has been granted")
}
Manifest.permission.CAMERA -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Incoming Call Activity] CAMERA permission has been granted")
coreContext.core.reloadVideoDevices()
}
}
}
}
}
private fun findIncomingCall(): Call? { private fun findIncomingCall(): Call? {
for (call in coreContext.core.calls) { for (call in coreContext.core.calls) {
if (call.state == Call.State.IncomingReceived || if (call.state == Call.State.IncomingReceived ||

View file

@ -19,7 +19,9 @@
*/ */
package org.linphone.activities.call package org.linphone.activities.call
import android.Manifest
import android.annotation.TargetApi import android.annotation.TargetApi
import android.content.pm.PackageManager
import android.os.Bundle import android.os.Bundle
import android.view.WindowManager import android.view.WindowManager
import androidx.databinding.DataBindingUtil import androidx.databinding.DataBindingUtil
@ -64,22 +66,6 @@ class OutgoingCallActivity : ProximitySensorActivity() {
controlsViewModel = ViewModelProvider(this).get(ControlsViewModel::class.java) controlsViewModel = ViewModelProvider(this).get(ControlsViewModel::class.java)
binding.controlsViewModel = controlsViewModel binding.controlsViewModel = controlsViewModel
binding.setTerminateCallClickListener {
viewModel.terminateCall()
}
binding.setToggleMicrophoneClickListener {
if (PermissionHelper.get().hasRecordAudioPermission()) {
controlsViewModel.toggleMuteMicrophone()
} else {
checkPermissions()
}
}
binding.setToggleSpeakerClickListener {
controlsViewModel.toggleSpeaker()
}
viewModel.callEndedEvent.observe(this, { viewModel.callEndedEvent.observe(this, {
it.consume { it.consume {
Log.i("[Outgoing Call Activity] Call ended, finish activity") Log.i("[Outgoing Call Activity] Call ended, finish activity")
@ -98,6 +84,12 @@ class OutgoingCallActivity : ProximitySensorActivity() {
enableProximitySensor(!it) enableProximitySensor(!it)
}) })
controlsViewModel.askPermissionEvent.observe(this, {
it.consume { permission ->
requestPermissions(arrayOf(permission), 0)
}
})
if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) { if (Version.sdkAboveOrEqual(Version.API23_MARSHMALLOW_60)) {
checkPermissions() checkPermissions()
} }
@ -131,6 +123,27 @@ class OutgoingCallActivity : ProximitySensorActivity() {
} }
} }
override fun onRequestPermissionsResult(
requestCode: Int,
permissions: Array<out String>,
grantResults: IntArray
) {
if (requestCode == 0) {
for (i in permissions.indices) {
when (permissions[i]) {
Manifest.permission.RECORD_AUDIO -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Outgoing Call Activity] RECORD_AUDIO permission has been granted")
controlsViewModel.updateMuteMicState()
}
Manifest.permission.CAMERA -> if (grantResults[i] == PackageManager.PERMISSION_GRANTED) {
Log.i("[Outgoing Call Activity] CAMERA permission has been granted")
coreContext.core.reloadVideoDevices()
}
}
}
}
}
private fun findOutgoingCall(): Call? { private fun findOutgoingCall(): Call? {
for (call in coreContext.core.calls) { for (call in coreContext.core.calls) {
if (call.state == Call.State.OutgoingInit || if (call.state == Call.State.OutgoingInit ||

View file

@ -19,13 +19,15 @@
*/ */
package org.linphone.activities.call.fragments package org.linphone.activities.call.fragments
import android.Manifest
import android.annotation.TargetApi import android.annotation.TargetApi
import android.app.Dialog import android.app.Dialog
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager.PERMISSION_GRANTED
import android.os.Bundle import android.os.Bundle
import android.os.SystemClock import android.os.SystemClock
import androidx.lifecycle.ViewModelProvider import androidx.lifecycle.ViewModelProvider
import org.linphone.LinphoneApplication.Companion.coreContext
import org.linphone.R import org.linphone.R
import org.linphone.activities.GenericFragment import org.linphone.activities.GenericFragment
import org.linphone.activities.call.viewmodels.CallsViewModel import org.linphone.activities.call.viewmodels.CallsViewModel
@ -122,6 +124,13 @@ class ControlsFragment : GenericFragment<CallControlsFragmentBinding>() {
} }
}) })
controlsViewModel.askPermissionEvent.observe(viewLifecycleOwner, {
it.consume { permission ->
Log.i("[Controls Fragment] Asking for $permission permission")
requestPermissions(arrayOf(permission), 0)
}
})
controlsViewModel.somethingClickedEvent.observe(viewLifecycleOwner, { controlsViewModel.somethingClickedEvent.observe(viewLifecycleOwner, {
it.consume { it.consume {
sharedViewModel.resetHiddenInterfaceTimerInVideoCallEvent.value = Event(true) sharedViewModel.resetHiddenInterfaceTimerInVideoCallEvent.value = Event(true)
@ -138,20 +147,37 @@ class ControlsFragment : GenericFragment<CallControlsFragmentBinding>() {
permissions: Array<out String>, permissions: Array<out String>,
grantResults: IntArray grantResults: IntArray
) { ) {
if (requestCode == 0 && grantResults[0] == PackageManager.PERMISSION_GRANTED) { if (requestCode == 0) {
for (i in permissions.indices) {
when (permissions[i]) {
Manifest.permission.RECORD_AUDIO -> if (grantResults[i] == PERMISSION_GRANTED) {
Log.i("[Controls Fragment] RECORD_AUDIO permission has been granted") Log.i("[Controls Fragment] RECORD_AUDIO permission has been granted")
controlsViewModel.updateMuteMicState() controlsViewModel.updateMuteMicState()
} }
Manifest.permission.CAMERA -> if (grantResults[i] == PERMISSION_GRANTED) {
Log.i("[Controls Fragment] CAMERA permission has been granted")
coreContext.core.reloadVideoDevices()
}
}
}
}
super.onRequestPermissionsResult(requestCode, permissions, grantResults) super.onRequestPermissionsResult(requestCode, permissions, grantResults)
} }
@TargetApi(Version.API23_MARSHMALLOW_60) @TargetApi(Version.API23_MARSHMALLOW_60)
private fun checkPermissions() { private fun checkPermissions() {
val permissionsRequiredList = arrayListOf<String>() val permissionsRequiredList = arrayListOf<String>()
if (!PermissionHelper.get().hasRecordAudioPermission()) { if (!PermissionHelper.get().hasRecordAudioPermission()) {
Log.i("[Controls Fragment] Asking for RECORD_AUDIO permission") Log.i("[Controls Fragment] Asking for RECORD_AUDIO permission")
permissionsRequiredList.add(android.Manifest.permission.RECORD_AUDIO) permissionsRequiredList.add(android.Manifest.permission.RECORD_AUDIO)
} }
if (coreContext.isVideoCallOrConferenceActive() && !PermissionHelper.get().hasCameraPermission()) {
Log.i("[Controls Fragment] Asking for CAMERA permission")
permissionsRequiredList.add(android.Manifest.permission.CAMERA)
}
if (permissionsRequiredList.isNotEmpty()) { if (permissionsRequiredList.isNotEmpty()) {
val permissionsRequired = arrayOfNulls<String>(permissionsRequiredList.size) val permissionsRequired = arrayOfNulls<String>(permissionsRequiredList.size)
permissionsRequiredList.toArray(permissionsRequired) permissionsRequiredList.toArray(permissionsRequired)

View file

@ -19,6 +19,7 @@
*/ */
package org.linphone.activities.call.viewmodels package org.linphone.activities.call.viewmodels
import android.Manifest
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import kotlin.math.max import kotlin.math.max
@ -74,6 +75,10 @@ class ControlsViewModel : ViewModel() {
MutableLiveData<Event<Boolean>>() MutableLiveData<Event<Boolean>>()
} }
val askPermissionEvent: MutableLiveData<Event<String>> by lazy {
MutableLiveData<Event<String>>()
}
val somethingClickedEvent = MutableLiveData<Event<Boolean>>() val somethingClickedEvent = MutableLiveData<Event<Boolean>>()
val onKeyClick: NumpadDigitListener = object : NumpadDigitListener { val onKeyClick: NumpadDigitListener = object : NumpadDigitListener {
@ -105,6 +110,10 @@ class ControlsViewModel : ViewModel() {
) { ) {
if (state == Call.State.StreamsRunning) isVideoUpdateInProgress.value = false if (state == Call.State.StreamsRunning) isVideoUpdateInProgress.value = false
if (coreContext.isVideoCallOrConferenceActive() && !PermissionHelper.get().hasCameraPermission()) {
askPermissionEvent.value = Event(Manifest.permission.CAMERA)
}
updateUI() updateUI()
} }
@ -147,6 +156,11 @@ class ControlsViewModel : ViewModel() {
} }
fun toggleMuteMicrophone() { fun toggleMuteMicrophone() {
if (!PermissionHelper.get().hasRecordAudioPermission()) {
askPermissionEvent.value = Event(Manifest.permission.RECORD_AUDIO)
return
}
somethingClickedEvent.value = Event(true) somethingClickedEvent.value = Event(true)
val micEnabled = coreContext.core.micEnabled() val micEnabled = coreContext.core.micEnabled()
coreContext.core.enableMic(!micEnabled) coreContext.core.enableMic(!micEnabled)
@ -178,6 +192,11 @@ class ControlsViewModel : ViewModel() {
} }
fun toggleVideo() { fun toggleVideo() {
if (!PermissionHelper.get().hasCameraPermission()) {
askPermissionEvent.value = Event(Manifest.permission.CAMERA)
return
}
val core = coreContext.core val core = coreContext.core
val currentCall = core.currentCall val currentCall = core.currentCall
val conference = core.conference val conference = core.conference
@ -314,6 +333,7 @@ class ControlsViewModel : ViewModel() {
fun updateMuteMicState() { fun updateMuteMicState() {
isMicrophoneMuted.value = !PermissionHelper.get().hasRecordAudioPermission() || !coreContext.core.micEnabled() isMicrophoneMuted.value = !PermissionHelper.get().hasRecordAudioPermission() || !coreContext.core.micEnabled()
isMuteMicrophoneEnabled.value = coreContext.core.currentCall != null || coreContext.core.isInConference
} }
private fun updateSpeakerState() { private fun updateSpeakerState() {
@ -349,7 +369,8 @@ class ControlsViewModel : ViewModel() {
} }
private fun updateVideoEnabled() { private fun updateVideoEnabled() {
isVideoEnabled.value = coreContext.isVideoCallOrConferenceActive() val enabled = coreContext.isVideoCallOrConferenceActive()
isVideoEnabled.value = enabled
} }
private fun updateConferenceState() { private fun updateConferenceState() {

View file

@ -5,15 +5,6 @@
<data> <data>
<import type="android.view.View" /> <import type="android.view.View" />
<variable
name="toggleMicrophoneClickListener"
type="android.view.View.OnClickListener" />
<variable
name="toggleSpeakerClickListener"
type="android.view.View.OnClickListener" />
<variable
name="terminateCallClickListener"
type="android.view.View.OnClickListener" />
<variable <variable
name="viewModel" name="viewModel"
type="org.linphone.activities.call.viewmodels.CallViewModel" /> type="org.linphone.activities.call.viewmodels.CallViewModel" />
@ -99,7 +90,7 @@
android:orientation="horizontal"> android:orientation="horizontal">
<ImageView <ImageView
android:onClick="@{toggleMicrophoneClickListener}" android:onClick="@{() -> controlsViewModel.toggleMuteMicrophone()}"
android:selected="@{controlsViewModel.isMicrophoneMuted}" android:selected="@{controlsViewModel.isMicrophoneMuted}"
android:contentDescription="@{controlsViewModel.isMicrophoneMuted ? @string/content_description_disable_mic_mute : @string/content_description_enable_mic_mute}" android:contentDescription="@{controlsViewModel.isMicrophoneMuted ? @string/content_description_disable_mic_mute : @string/content_description_enable_mic_mute}"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -110,7 +101,7 @@
android:src="@drawable/micro" /> android:src="@drawable/micro" />
<ImageView <ImageView
android:onClick="@{toggleSpeakerClickListener}" android:onClick="@{() -> controlsViewModel.toggleSpeaker()}"
android:selected="@{controlsViewModel.isSpeakerSelected}" android:selected="@{controlsViewModel.isSpeakerSelected}"
android:contentDescription="@{controlsViewModel.isSpeakerSelected ? @string/content_description_disable_speaker : @string/content_description_enable_speaker}" android:contentDescription="@{controlsViewModel.isSpeakerSelected ? @string/content_description_disable_speaker : @string/content_description_enable_speaker}"
android:layout_width="match_parent" android:layout_width="match_parent"
@ -121,7 +112,7 @@
android:src="@drawable/speaker" /> android:src="@drawable/speaker" />
<ImageView <ImageView
android:onClick="@{terminateCallClickListener}" android:onClick="@{() -> viewModel.terminateCall()}"
android:contentDescription="@string/content_description_terminate_call" android:contentDescription="@string/content_description_terminate_call"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"