Respect can publish audio & video permission

With this implementation the can publish audio & video permission are
set during the creation of the 'CallActivity'. This permissions are
fixed for the complete call. If the permissions are changed by a
moderator the call must be left and joined again.

Resolves: #1783

Signed-off-by: Tim Krüger <t@timkrueger.me>
This commit is contained in:
Tim Krüger 2022-09-21 20:05:27 +02:00
parent dda5a9e3da
commit c554535bae
No known key found for this signature in database
GPG Key ID: FECE3A7222C52A4E
6 changed files with 108 additions and 37 deletions

View File

@ -170,6 +170,8 @@ import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_NAME;
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_PASSWORD; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_CONVERSATION_PASSWORD;
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_FROM_NOTIFICATION_START_CALL;
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MODIFIED_BASE_URL; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_MODIFIED_BASE_URL;
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO;
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO;
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_ID;
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_ROOM_TOKEN;
import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY; import static com.nextcloud.talk.utils.bundle.BundleKeys.KEY_USER_ENTITY;
@ -293,6 +295,9 @@ public class CallActivity extends CallBaseActivity {
} }
}); });
private boolean canPublishAudioStream;
private boolean canPublishVideoStream;
@SuppressLint("ClickableViewAccessibility") @SuppressLint("ClickableViewAccessibility")
@Override @Override
public void onCreate(Bundle savedInstanceState) { public void onCreate(Bundle savedInstanceState) {
@ -314,6 +319,8 @@ public class CallActivity extends CallBaseActivity {
conversationName = extras.getString(KEY_CONVERSATION_NAME, ""); conversationName = extras.getString(KEY_CONVERSATION_NAME, "");
isVoiceOnlyCall = extras.getBoolean(KEY_CALL_VOICE_ONLY, false); isVoiceOnlyCall = extras.getBoolean(KEY_CALL_VOICE_ONLY, false);
isCallWithoutNotification = extras.getBoolean(KEY_CALL_WITHOUT_NOTIFICATION, false); isCallWithoutNotification = extras.getBoolean(KEY_CALL_WITHOUT_NOTIFICATION, false);
canPublishAudioStream = extras.getBoolean(KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO);
canPublishVideoStream = extras.getBoolean(KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO);
if (extras.containsKey(KEY_FROM_NOTIFICATION_START_CALL)) { if (extras.containsKey(KEY_FROM_NOTIFICATION_START_CALL)) {
isIncomingCallFromNotification = extras.getBoolean(KEY_FROM_NOTIFICATION_START_CALL); isIncomingCallFromNotification = extras.getBoolean(KEY_FROM_NOTIFICATION_START_CALL);
@ -387,23 +394,41 @@ public class CallActivity extends CallBaseActivity {
audioOutputDialog.show(); audioOutputDialog.show();
}); });
binding.microphoneButton.setOnClickListener(l -> onMicrophoneClick()); if (canPublishAudioStream) {
binding.microphoneButton.setOnLongClickListener(l -> { binding.microphoneButton.setOnClickListener(l -> onMicrophoneClick());
if (!microphoneOn) { binding.microphoneButton.setOnLongClickListener(l -> {
callControlHandler.removeCallbacksAndMessages(null); if (!microphoneOn) {
callInfosHandler.removeCallbacksAndMessages(null); callControlHandler.removeCallbacksAndMessages(null);
cameraSwitchHandler.removeCallbacksAndMessages(null); callInfosHandler.removeCallbacksAndMessages(null);
isPTTActive = true; cameraSwitchHandler.removeCallbacksAndMessages(null);
binding.callControls.setVisibility(View.VISIBLE); isPTTActive = true;
if (!isVoiceOnlyCall) { binding.callControls.setVisibility(View.VISIBLE);
binding.switchSelfVideoButton.setVisibility(View.VISIBLE); if (!isVoiceOnlyCall) {
binding.switchSelfVideoButton.setVisibility(View.VISIBLE);
}
} }
} onMicrophoneClick();
onMicrophoneClick(); return true;
return true; });
}); } else {
binding.microphoneButton.setOnClickListener(
l -> Toast.makeText(context,
R.string.nc_not_allowed_to_activate_audio,
Toast.LENGTH_SHORT
).show()
);
}
binding.cameraButton.setOnClickListener(l -> onCameraClick()); if (canPublishVideoStream) {
binding.cameraButton.setOnClickListener(l -> onCameraClick());
} else {
binding.cameraButton.setOnClickListener(
l -> Toast.makeText(context,
R.string.nc_not_allowed_to_activate_video,
Toast.LENGTH_SHORT
).show()
);
}
binding.hangupButton.setOnClickListener(l -> { binding.hangupButton.setOnClickListener(l -> {
hangup(true); hangup(true);
@ -558,7 +583,7 @@ public class CallActivity extends CallBaseActivity {
} }
} }
checkPermissions(); checkDevicePermissions();
} }
@Override @Override
@ -705,7 +730,7 @@ public class CallActivity extends CallBaseActivity {
} }
private void checkPermissions() { private void checkDevicePermissions() {
if (isVoiceOnlyCall) { if (isVoiceOnlyCall) {
onMicrophoneClick(); onMicrophoneClick();
} else { } else {
@ -764,7 +789,7 @@ public class CallActivity extends CallBaseActivity {
binding.switchSelfVideoButton.setVisibility(View.VISIBLE); binding.switchSelfVideoButton.setVisibility(View.VISIBLE);
} }
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CAMERA)) { if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CAMERA) && canPublishVideoStream) {
if (!videoOn) { if (!videoOn) {
onCameraClick(); onCameraClick();
} }
@ -775,7 +800,7 @@ public class CallActivity extends CallBaseActivity {
} }
} }
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_MICROPHONE)) { if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_MICROPHONE) && canPublishAudioStream) {
if (!microphoneOn) { if (!microphoneOn) {
onMicrophoneClick(); onMicrophoneClick();
} }
@ -888,6 +913,18 @@ public class CallActivity extends CallBaseActivity {
} }
public void onMicrophoneClick() { public void onMicrophoneClick() {
if (isVoiceOnlyCall && !isConnectionEstablished()) {
fetchSignalingSettings();
}
if (!canPublishAudioStream) {
microphoneOn = false;
binding.microphoneButton.getHierarchy().setPlaceholderImage(R.drawable.ic_mic_off_white_24px);
// In the case no audio stream will be published it's not needed to check microphone permissions
return;
}
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_MICROPHONE)) { if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_MICROPHONE)) {
if (!appPreferences.getPushToTalkIntroShown()) { if (!appPreferences.getPushToTalkIntroShown()) {
@ -936,11 +973,6 @@ public class CallActivity extends CallBaseActivity {
pulseAnimation.start(); pulseAnimation.start();
toggleMedia(true, false); toggleMedia(true, false);
} }
if (isVoiceOnlyCall && !isConnectionEstablished()) {
fetchSignalingSettings();
}
} else if (EffortlessPermissions.somePermissionPermanentlyDenied(this, PERMISSIONS_MICROPHONE)) { } else if (EffortlessPermissions.somePermissionPermanentlyDenied(this, PERMISSIONS_MICROPHONE)) {
// Microphone permission is permanently denied so we cannot request it normally. // Microphone permission is permanently denied so we cannot request it normally.
@ -957,6 +989,14 @@ public class CallActivity extends CallBaseActivity {
} }
public void onCameraClick() { public void onCameraClick() {
if (!canPublishVideoStream) {
videoOn = false;
binding.cameraButton.getHierarchy().setPlaceholderImage(R.drawable.ic_videocam_off_white_24px);
binding.switchSelfVideoButton.setVisibility(View.GONE);
return;
}
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CAMERA)) { if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CAMERA)) {
videoOn = !videoOn; videoOn = !videoOn;
@ -1384,12 +1424,14 @@ public class CallActivity extends CallBaseActivity {
} }
private void performCall() { private void performCall() {
int inCallFlag; int inCallFlag = Participant.InCallFlags.IN_CALL;
if (isVoiceOnlyCall) {
inCallFlag = Participant.InCallFlags.IN_CALL + Participant.InCallFlags.WITH_AUDIO; if (canPublishAudioStream) {
} else { inCallFlag += Participant.InCallFlags.WITH_AUDIO;
inCallFlag = }
Participant.InCallFlags.IN_CALL + Participant.InCallFlags.WITH_AUDIO + Participant.InCallFlags.WITH_VIDEO;
if (!isVoiceOnlyCall && canPublishVideoStream) {
inCallFlag += Participant.InCallFlags.WITH_VIDEO;
} }
int apiVersion = ApiUtils.getCallApiVersion(conversationUser, new int[]{ApiUtils.APIv4, 1}); int apiVersion = ApiUtils.getCallApiVersion(conversationUser, new int[]{ApiUtils.APIv4, 1});
@ -1495,7 +1537,7 @@ public class CallActivity extends CallBaseActivity {
private void initiateCall() { private void initiateCall() {
if (!TextUtils.isEmpty(roomToken)) { if (!TextUtils.isEmpty(roomToken)) {
checkPermissions(); checkDevicePermissions();
} else { } else {
handleFromNotification(); handleFromNotification();
} }
@ -2302,7 +2344,7 @@ public class CallActivity extends CallBaseActivity {
if (peerConnectionWrapper != null) { if (peerConnectionWrapper != null) {
PeerConnection.IceConnectionState iceConnectionState = peerConnectionWrapper.getPeerConnection().iceConnectionState(); PeerConnection.IceConnectionState iceConnectionState = peerConnectionWrapper.getPeerConnection().iceConnectionState();
connected = iceConnectionState == PeerConnection.IceConnectionState.CONNECTED || connected = iceConnectionState == PeerConnection.IceConnectionState.CONNECTED ||
iceConnectionState == PeerConnection.IceConnectionState.COMPLETED; iceConnectionState == PeerConnection.IceConnectionState.COMPLETED;
} }
String nick; String nick;

View File

@ -2747,6 +2747,14 @@ class ChatController(args: Bundle) :
bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword) bundle.putString(BundleKeys.KEY_CONVERSATION_PASSWORD, roomPassword)
bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, conversationUser?.baseUrl) bundle.putString(BundleKeys.KEY_MODIFIED_BASE_URL, conversationUser?.baseUrl)
bundle.putString(KEY_CONVERSATION_NAME, it.displayName) bundle.putString(KEY_CONVERSATION_NAME, it.displayName)
bundle.putBoolean(
BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO,
participantPermissions.canPublishAudio()
)
bundle.putBoolean(
BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO,
participantPermissions.canPublishVideo()
)
if (isVoiceOnlyCall) { if (isVoiceOnlyCall) {
bundle.putBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, true) bundle.putBoolean(BundleKeys.KEY_CALL_VOICE_ONLY, true)

View File

@ -39,8 +39,8 @@ class ParticipantPermissions(
private val canStartCall = (conversation.permissions and START_CALL) == START_CALL private val canStartCall = (conversation.permissions and START_CALL) == START_CALL
val canJoinCall = (conversation.permissions and JOIN_CALL) == JOIN_CALL val canJoinCall = (conversation.permissions and JOIN_CALL) == JOIN_CALL
private val canIgnoreLobby = (conversation.permissions and CAN_IGNORE_LOBBY) == CAN_IGNORE_LOBBY private val canIgnoreLobby = (conversation.permissions and CAN_IGNORE_LOBBY) == CAN_IGNORE_LOBBY
val canPublishAudio = (conversation.permissions and PUBLISH_AUDIO) == PUBLISH_AUDIO private val canPublishAudio = (conversation.permissions and PUBLISH_AUDIO) == PUBLISH_AUDIO
val canPublishVideo = (conversation.permissions and PUBLISH_VIDEO) == PUBLISH_VIDEO private val canPublishVideo = (conversation.permissions and PUBLISH_VIDEO) == PUBLISH_VIDEO
val canPublishScreen = (conversation.permissions and PUBLISH_SCREEN) == PUBLISH_SCREEN val canPublishScreen = (conversation.permissions and PUBLISH_SCREEN) == PUBLISH_SCREEN
private val hasChatPermission = (conversation.permissions and CHAT) == CHAT private val hasChatPermission = (conversation.permissions and CHAT) == CHAT
@ -67,6 +67,22 @@ class ParticipantPermissions(
} }
} }
fun canPublishAudio(): Boolean {
return if (hasConversationPermissions()) {
canPublishAudio
} else {
true
}
}
fun canPublishVideo(): Boolean {
return if (hasConversationPermissions()) {
canPublishVideo
} else {
true
}
}
fun hasChatPermission(): Boolean { fun hasChatPermission(): Boolean {
if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "chat-permission")) { if (CapabilitiesUtilNew.hasSpreedFeatureCapability(user, "chat-permission")) {
return hasChatPermission return hasChatPermission

View File

@ -75,4 +75,6 @@ object BundleKeys {
const val KEY_SYSTEM_NOTIFICATION_ID = "KEY_SYSTEM_NOTIFICATION_ID" const val KEY_SYSTEM_NOTIFICATION_ID = "KEY_SYSTEM_NOTIFICATION_ID"
const val KEY_MESSAGE_ID = "KEY_MESSAGE_ID" const val KEY_MESSAGE_ID = "KEY_MESSAGE_ID"
const val KEY_MIME_TYPE_FILTER = "KEY_MIME_TYPE_FILTER" const val KEY_MIME_TYPE_FILTER = "KEY_MIME_TYPE_FILTER"
const val KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO = "KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO"
const val KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO = "KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_VIDEO"
} }

View File

@ -602,4 +602,7 @@
<string name="nc_expire_message_one_hour">1 hour</string> <string name="nc_expire_message_one_hour">1 hour</string>
<string name="nc_expire_messages_explanation">Chat messages can be expired after a certain time. Note: Files shared in chat will not be deleted for the owner, but will no longer be shared in the conversation.</string> <string name="nc_expire_messages_explanation">Chat messages can be expired after a certain time. Note: Files shared in chat will not be deleted for the owner, but will no longer be shared in the conversation.</string>
<string name="nc_not_allowed_to_activate_audio">You\'re not allowed to activate audio!</string>
<string name="nc_not_allowed_to_activate_video">You\'re not allowed to activate video!</string>
</resources> </resources>

View File

@ -50,8 +50,8 @@ class ParticipantPermissionsTest : TestCase() {
assertFalse(attendeePermissions.isCustom) assertFalse(attendeePermissions.isCustom)
assertFalse(attendeePermissions.canStartCall()) assertFalse(attendeePermissions.canStartCall())
assertFalse(attendeePermissions.canIgnoreLobby) assertFalse(attendeePermissions.canIgnoreLobby())
assertFalse(attendeePermissions.canPublishAudio) assertTrue(attendeePermissions.canPublishAudio())
assertFalse(attendeePermissions.canPublishVideo) assertTrue(attendeePermissions.canPublishVideo())
} }
} }