mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-14 21:15:37 +00:00
android, desktop: calls switching from audio to video and back (#4814)
* android, desktop: calls switching from audio to video and back
* refactor
* working all 4 streams with mute handling differently
* changes
* changes
* wrong file
* changes
* padding
* android camera service type
* icons, sizes, clickable
* refactor
* Revert "android camera service type"
This reverts commit 9878ff38e9.
* late init camera permissions
* enabling camera sooner than call establishes (not fully done)
* changes
* alpha
* fixes for Safari
* enhancements
* fix Safari sound
* padding between buttons on desktop
* android default values for padding
* changes
* calls without encryption are supported and flipping camera on some devices works
* unused param
* logs
* background color
* play local video in Safari
* no line height
* removed one listener from per frame processing
* enhancements
---------
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
committed by
GitHub
parent
4a39b481b1
commit
95c1d8d798
@@ -2,6 +2,7 @@ package chat.simplex.app
|
||||
|
||||
import android.app.*
|
||||
import android.content.*
|
||||
import android.content.pm.PackageManager
|
||||
import android.content.pm.ServiceInfo
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.BitmapFactory
|
||||
@@ -83,7 +84,7 @@ class CallService: Service() {
|
||||
generalGetString(MR.strings.notification_preview_somebody)
|
||||
else
|
||||
call?.contact?.profile?.displayName ?: ""
|
||||
val text = generalGetString(if (call?.supportsVideo() == true) MR.strings.call_service_notification_video_call else MR.strings.call_service_notification_audio_call)
|
||||
val text = generalGetString(if (call?.hasVideo == true) MR.strings.call_service_notification_video_call else MR.strings.call_service_notification_audio_call)
|
||||
val image = call?.contact?.image
|
||||
val largeIcon = if (image == null || previewMode == NotificationPreviewMode.HIDDEN.name)
|
||||
BitmapFactory.decodeResource(resources, R.drawable.icon)
|
||||
@@ -105,7 +106,7 @@ class CallService: Service() {
|
||||
0
|
||||
}
|
||||
} else if (Build.VERSION.SDK_INT >= 30) {
|
||||
if (call.supportsVideo()) {
|
||||
if (call.hasVideo && ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED) {
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE or ServiceInfo.FOREGROUND_SERVICE_TYPE_CAMERA
|
||||
} else {
|
||||
ServiceInfo.FOREGROUND_SERVICE_TYPE_MEDIA_PLAYBACK or ServiceInfo.FOREGROUND_SERVICE_TYPE_MICROPHONE
|
||||
|
||||
+27
-12
@@ -116,7 +116,7 @@ class CallActivity: ComponentActivity(), ServiceConnection {
|
||||
|
||||
private fun hasGrantedPermissions(): Boolean {
|
||||
val grantedAudio = ContextCompat.checkSelfPermission(this, android.Manifest.permission.RECORD_AUDIO) == PackageManager.PERMISSION_GRANTED
|
||||
val grantedCamera = !callSupportsVideo() || ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
|
||||
val grantedCamera = !callHasVideo() || ContextCompat.checkSelfPermission(this, android.Manifest.permission.CAMERA) == PackageManager.PERMISSION_GRANTED
|
||||
return grantedAudio && grantedCamera
|
||||
}
|
||||
|
||||
@@ -124,7 +124,7 @@ class CallActivity: ComponentActivity(), ServiceConnection {
|
||||
override fun onBackPressed() {
|
||||
if (isOnLockScreenNow()) {
|
||||
super.onBackPressed()
|
||||
} else if (!hasGrantedPermissions() && !callSupportsVideo()) {
|
||||
} else if (!hasGrantedPermissions() && !callHasVideo()) {
|
||||
val call = m.activeCall.value
|
||||
if (call != null) {
|
||||
withBGApi { chatModel.callManager.endCall(call) }
|
||||
@@ -142,7 +142,7 @@ class CallActivity: ComponentActivity(), ServiceConnection {
|
||||
override fun onUserLeaveHint() {
|
||||
super.onUserLeaveHint()
|
||||
// On Android 12+ PiP is enabled automatically when a user hides the app
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R && callSupportsVideo() && platform.androidPictureInPictureAllowed()) {
|
||||
if (Build.VERSION.SDK_INT <= Build.VERSION_CODES.R && callHasVideo() && platform.androidPictureInPictureAllowed()) {
|
||||
enterPictureInPictureMode()
|
||||
}
|
||||
}
|
||||
@@ -198,7 +198,7 @@ class CallActivity: ComponentActivity(), ServiceConnection {
|
||||
fun getKeyguardManager(context: Context): KeyguardManager =
|
||||
context.getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager
|
||||
|
||||
private fun callSupportsVideo() = m.activeCall.value?.supportsVideo() == true || m.activeCallInvitation.value?.callType?.media == CallMediaType.Video
|
||||
private fun callHasVideo() = m.activeCall.value?.hasVideo == true || m.activeCallInvitation.value?.callType?.media == CallMediaType.Video
|
||||
|
||||
@Composable
|
||||
fun CallActivityView() {
|
||||
@@ -212,7 +212,7 @@ fun CallActivityView() {
|
||||
.collect { collapsed ->
|
||||
when {
|
||||
collapsed -> {
|
||||
if (!platform.androidPictureInPictureAllowed() || !callSupportsVideo()) {
|
||||
if (!platform.androidPictureInPictureAllowed() || !callHasVideo()) {
|
||||
activity.moveTaskToBack(true)
|
||||
activity.startActivity(Intent(activity, MainActivity::class.java))
|
||||
} else if (!activity.isInPictureInPictureMode && activity.lifecycle.currentState == Lifecycle.State.RESUMED) {
|
||||
@@ -221,7 +221,7 @@ fun CallActivityView() {
|
||||
activity.enterPictureInPictureMode()
|
||||
}
|
||||
}
|
||||
callSupportsVideo() && !platform.androidPictureInPictureAllowed() -> {
|
||||
callHasVideo() && !platform.androidPictureInPictureAllowed() -> {
|
||||
// PiP disabled by user
|
||||
platform.androidStartCallActivity(false)
|
||||
}
|
||||
@@ -242,28 +242,43 @@ fun CallActivityView() {
|
||||
Box(Modifier.background(Color.Black)) {
|
||||
if (call != null) {
|
||||
val permissionsState = rememberMultiplePermissionsState(
|
||||
permissions = if (callSupportsVideo()) {
|
||||
permissions = if (callHasVideo()) {
|
||||
listOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO)
|
||||
} else {
|
||||
listOf(Manifest.permission.RECORD_AUDIO)
|
||||
}
|
||||
)
|
||||
if (permissionsState.allPermissionsGranted) {
|
||||
// callState == connected is needed in a situation when a peer enabled camera in audio call while a user didn't grant camera permission yet,
|
||||
// so no need to hide active call view in this case
|
||||
if (permissionsState.allPermissionsGranted || call.callState == CallState.Connected) {
|
||||
ActiveCallView()
|
||||
LaunchedEffect(Unit) {
|
||||
activity.startServiceAndBind()
|
||||
}
|
||||
} else {
|
||||
CallPermissionsView(remember { m.activeCallViewIsCollapsed }.value, callSupportsVideo()) {
|
||||
}
|
||||
if ((!permissionsState.allPermissionsGranted && call.callState != CallState.Connected) || call.wantsToEnableCamera) {
|
||||
CallPermissionsView(remember { m.activeCallViewIsCollapsed }.value, callHasVideo() || call.wantsToEnableCamera) {
|
||||
withBGApi { chatModel.callManager.endCall(call) }
|
||||
}
|
||||
val cameraAndMicPermissions = rememberMultiplePermissionsState(permissions = listOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO))
|
||||
DisposableEffect(cameraAndMicPermissions.allPermissionsGranted) {
|
||||
onDispose {
|
||||
if (call.wantsToEnableCamera && cameraAndMicPermissions.allPermissionsGranted) {
|
||||
val activeCall = chatModel.activeCall.value
|
||||
if (activeCall != null && activeCall.contact.apiId == call.contact.apiId) {
|
||||
chatModel.activeCall.value = activeCall.copy(wantsToEnableCamera = false)
|
||||
chatModel.callCommand.add(WCallCommand.Media(CallMediaSource.Camera, enable = true))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
val view = LocalView.current
|
||||
if (callSupportsVideo()) {
|
||||
if (callHasVideo()) {
|
||||
val scope = rememberCoroutineScope()
|
||||
LaunchedEffect(Unit) {
|
||||
scope.launch {
|
||||
activity.setPipParams(callSupportsVideo(), viewRatio = Rational(view.width, view.height))
|
||||
activity.setPipParams(callHasVideo(), viewRatio = Rational(view.width, view.height))
|
||||
activity.trackPipAnimationHintView(view)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user