mirror of
https://github.com/nextcloud/talk-android
synced 2025-06-19 19:49:33 +01:00
Initial work on better permissions handling
Signed-off-by: Mario Danic <mario@lovelyhq.com>
This commit is contained in:
parent
d7c7d34a62
commit
df462a00b9
@ -121,6 +121,7 @@ import io.reactivex.android.schedulers.AndroidSchedulers;
|
|||||||
import io.reactivex.disposables.Disposable;
|
import io.reactivex.disposables.Disposable;
|
||||||
import io.reactivex.functions.BooleanSupplier;
|
import io.reactivex.functions.BooleanSupplier;
|
||||||
import io.reactivex.schedulers.Schedulers;
|
import io.reactivex.schedulers.Schedulers;
|
||||||
|
import me.zhanghai.android.effortlesspermissions.AfterPermissionDenied;
|
||||||
import me.zhanghai.android.effortlesspermissions.EffortlessPermissions;
|
import me.zhanghai.android.effortlesspermissions.EffortlessPermissions;
|
||||||
import me.zhanghai.android.effortlesspermissions.OpenAppDetailsDialogFragment;
|
import me.zhanghai.android.effortlesspermissions.OpenAppDetailsDialogFragment;
|
||||||
import pub.devrel.easypermissions.AfterPermissionGranted;
|
import pub.devrel.easypermissions.AfterPermissionGranted;
|
||||||
@ -131,8 +132,16 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
private static final String[] PERMISSIONS_CALL = {
|
private static final String[] PERMISSIONS_CALL = {
|
||||||
android.Manifest.permission.CAMERA,
|
android.Manifest.permission.CAMERA,
|
||||||
android.Manifest.permission.RECORD_AUDIO,
|
android.Manifest.permission.RECORD_AUDIO,
|
||||||
Manifest.permission.MODIFY_AUDIO_SETTINGS
|
|
||||||
};
|
};
|
||||||
|
|
||||||
|
private static final String[] PERMISSIONS_CAMERA = {
|
||||||
|
Manifest.permission.CAMERA
|
||||||
|
};
|
||||||
|
|
||||||
|
private static final String[] PERMISSIONS_MICROPHONE = {
|
||||||
|
Manifest.permission.RECORD_AUDIO
|
||||||
|
};
|
||||||
|
|
||||||
@BindView(R.id.pip_video_view)
|
@BindView(R.id.pip_video_view)
|
||||||
SurfaceViewRenderer pipVideoView;
|
SurfaceViewRenderer pipVideoView;
|
||||||
@BindView(R.id.full_screen_surface_view)
|
@BindView(R.id.full_screen_surface_view)
|
||||||
@ -173,6 +182,7 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
VideoRenderer localRenderer;
|
VideoRenderer localRenderer;
|
||||||
EglBase rootEglBase;
|
EglBase rootEglBase;
|
||||||
boolean leavingCall = false;
|
boolean leavingCall = false;
|
||||||
|
boolean inCall = false;
|
||||||
BooleanSupplier booleanSupplier = () -> leavingCall;
|
BooleanSupplier booleanSupplier = () -> leavingCall;
|
||||||
Disposable signalingDisposable;
|
Disposable signalingDisposable;
|
||||||
Disposable pingDisposable;
|
Disposable pingDisposable;
|
||||||
@ -185,8 +195,11 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
private String credentials;
|
private String credentials;
|
||||||
private List<MagicPeerConnectionWrapper> magicPeerConnectionWrapperList = new ArrayList<>();
|
private List<MagicPeerConnectionWrapper> magicPeerConnectionWrapperList = new ArrayList<>();
|
||||||
|
|
||||||
private boolean videoOn = true;
|
private boolean videoOn = false;
|
||||||
private boolean audioOn = true;
|
private boolean audioOn = false;
|
||||||
|
|
||||||
|
private boolean cameraInitialized = false;
|
||||||
|
private boolean microphoneInitialized = false;
|
||||||
|
|
||||||
private static int getSystemUiVisibility() {
|
private static int getSystemUiVisibility() {
|
||||||
int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
|
int flags = View.SYSTEM_UI_FLAG_HIDE_NAVIGATION | View.SYSTEM_UI_FLAG_FULLSCREEN;
|
||||||
@ -214,6 +227,8 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
callSession = "0";
|
callSession = "0";
|
||||||
credentials = ApiHelper.getCredentials(userEntity.getUsername(), userEntity.getToken());
|
credentials = ApiHelper.getCredentials(userEntity.getUsername(), userEntity.getToken());
|
||||||
|
|
||||||
|
basicInitialization();
|
||||||
|
|
||||||
if (userUtils.getCurrentUser() != null && userUtils.getCurrentUser() != userEntity) {
|
if (userUtils.getCurrentUser() != null && userUtils.getCurrentUser() != userEntity) {
|
||||||
userUtils.createOrUpdateUser(userEntity.getUsername(),
|
userUtils.createOrUpdateUser(userEntity.getUsername(),
|
||||||
userEntity.getToken(), userEntity.getBaseUrl(), null,
|
userEntity.getToken(), userEntity.getBaseUrl(), null,
|
||||||
@ -265,7 +280,9 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
localMediaStream.videoTracks.get(0).setEnabled(enable);
|
if (localMediaStream.videoTracks.size() > 0) {
|
||||||
|
localMediaStream.videoTracks.get(0).setEnabled(enable);
|
||||||
|
}
|
||||||
|
|
||||||
if (enable) {
|
if (enable) {
|
||||||
pipVideoView.setVisibility(View.VISIBLE);
|
pipVideoView.setVisibility(View.VISIBLE);
|
||||||
@ -278,7 +295,9 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
message = "audioOn";
|
message = "audioOn";
|
||||||
}
|
}
|
||||||
|
|
||||||
localMediaStream.audioTracks.get(0).setEnabled(enable);
|
if (localMediaStream.audioTracks.size() > 0) {
|
||||||
|
localMediaStream.audioTracks.get(0).setEnabled(enable);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) {
|
for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) {
|
||||||
@ -288,15 +307,25 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@OnClick(R.id.call_control_microphone)
|
@OnClick(R.id.call_control_microphone)
|
||||||
public void onMicrophoneClick() {
|
public void onMicrophoneClick() {
|
||||||
audioOn = !audioOn;
|
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_MICROPHONE)) {
|
||||||
|
audioOn = !audioOn;
|
||||||
|
|
||||||
if (audioOn) {
|
if (audioOn) {
|
||||||
microphoneControlButton.setImageResource(R.drawable.ic_mic_white_24px);
|
microphoneControlButton.setImageResource(R.drawable.ic_mic_white_24px);
|
||||||
|
} else {
|
||||||
|
microphoneControlButton.setImageResource(R.drawable.ic_mic_off_white_24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMedia(audioOn, false);
|
||||||
|
} else if (EffortlessPermissions.somePermissionPermanentlyDenied(this, PERMISSIONS_MICROPHONE)) {
|
||||||
|
// Some permission is permanently denied so we cannot request them normally.
|
||||||
|
OpenAppDetailsDialogFragment.show(
|
||||||
|
R.string.nc_microphone_permission_permanently_denied,
|
||||||
|
R.string.nc_permissions_settings, this);
|
||||||
} else {
|
} else {
|
||||||
microphoneControlButton.setImageResource(R.drawable.ic_mic_off_white_24px);
|
EffortlessPermissions.requestPermissions(this, R.string.nc_permissions_audio,
|
||||||
|
100, PERMISSIONS_MICROPHONE);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleMedia(audioOn, false);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@OnClick(R.id.call_control_hangup)
|
@OnClick(R.id.call_control_hangup)
|
||||||
@ -307,15 +336,26 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
|
|
||||||
@OnClick(R.id.call_control_camera)
|
@OnClick(R.id.call_control_camera)
|
||||||
public void onCameraClick() {
|
public void onCameraClick() {
|
||||||
videoOn = !videoOn;
|
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CAMERA)) {
|
||||||
|
videoOn = !videoOn;
|
||||||
|
|
||||||
if (videoOn) {
|
if (videoOn) {
|
||||||
cameraControlButton.setImageResource(R.drawable.ic_videocam_white_24px);
|
cameraControlButton.setImageResource(R.drawable.ic_videocam_white_24px);
|
||||||
|
} else {
|
||||||
|
cameraControlButton.setImageResource(R.drawable.ic_videocam_off_white_24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
toggleMedia(videoOn, true);
|
||||||
|
} else if (EffortlessPermissions.somePermissionPermanentlyDenied(this, PERMISSIONS_CAMERA)) {
|
||||||
|
// Some permission is permanently denied so we cannot request them normally.
|
||||||
|
OpenAppDetailsDialogFragment.show(
|
||||||
|
R.string.nc_camera_permission_permanently_denied,
|
||||||
|
R.string.nc_permissions_settings, this);
|
||||||
} else {
|
} else {
|
||||||
cameraControlButton.setImageResource(R.drawable.ic_videocam_off_white_24px);
|
EffortlessPermissions.requestPermissions(this, R.string.nc_permissions_video,
|
||||||
|
100, PERMISSIONS_CAMERA);
|
||||||
}
|
}
|
||||||
|
|
||||||
toggleMedia(videoOn, true);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -391,20 +431,53 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
@AfterPermissionGranted(100)
|
@AfterPermissionGranted(100)
|
||||||
private void checkPermissions() {
|
private void checkPermissions() {
|
||||||
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CALL)) {
|
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CALL)) {
|
||||||
initializeEverything();
|
if (!cameraInitialized) {
|
||||||
|
cameraInitialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!microphoneInitialized) {
|
||||||
|
microphoneInitialization();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inCall) {
|
||||||
|
startCall();
|
||||||
|
}
|
||||||
} else if (EffortlessPermissions.somePermissionPermanentlyDenied(this,
|
} else if (EffortlessPermissions.somePermissionPermanentlyDenied(this,
|
||||||
PERMISSIONS_CALL)) {
|
PERMISSIONS_CALL)) {
|
||||||
// Some permission is permanently denied so we cannot request them normally.
|
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_CAMERA)) {
|
||||||
OpenAppDetailsDialogFragment.show(
|
cameraInitialization();
|
||||||
R.string.nc_permissions_permanently_denied,
|
onCameraClick();
|
||||||
R.string.nc_permissions_settings, this);
|
} else if (!EffortlessPermissions.hasPermissions(this, PERMISSIONS_CAMERA)) {
|
||||||
|
cameraControlButton.setImageResource(R.drawable.ic_videocam_off_white_24px);
|
||||||
|
cameraSwitchButton.setEnabled(false);
|
||||||
|
cameraSwitchButton.setAlpha(0.5f);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (EffortlessPermissions.hasPermissions(this, PERMISSIONS_MICROPHONE)) {
|
||||||
|
microphoneInitialization();
|
||||||
|
onMicrophoneClick();
|
||||||
|
} else if (!EffortlessPermissions.hasPermissions(this, PERMISSIONS_MICROPHONE)) {
|
||||||
|
microphoneControlButton.setImageResource(R.drawable.ic_mic_off_white_24px);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!inCall) {
|
||||||
|
startCall();
|
||||||
|
}
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
EffortlessPermissions.requestPermissions(this, R.string.nc_permissions,
|
EffortlessPermissions.requestPermissions(this, R.string.nc_permissions,
|
||||||
100, PERMISSIONS_CALL);
|
100, PERMISSIONS_CALL);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void initializeEverything() {
|
@AfterPermissionDenied(100)
|
||||||
|
private void onPermissionsDenied() {
|
||||||
|
if (!inCall) {
|
||||||
|
startCall();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private void basicInitialization() {
|
||||||
//Initialize PeerConnectionFactory globals.
|
//Initialize PeerConnectionFactory globals.
|
||||||
PeerConnectionFactory.InitializationOptions initializationOptions = PeerConnectionFactory.InitializationOptions
|
PeerConnectionFactory.InitializationOptions initializationOptions = PeerConnectionFactory.InitializationOptions
|
||||||
.builder(this)
|
.builder(this)
|
||||||
@ -416,23 +489,11 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
|
PeerConnectionFactory.Options options = new PeerConnectionFactory.Options();
|
||||||
peerConnectionFactory = new PeerConnectionFactory(options);
|
peerConnectionFactory = new PeerConnectionFactory(options);
|
||||||
|
|
||||||
videoCapturer = createCameraCapturer(cameraEnumerator);
|
|
||||||
|
|
||||||
//Create MediaConstraints - Will be useful for specifying video and audio constraints.
|
//Create MediaConstraints - Will be useful for specifying video and audio constraints.
|
||||||
audioConstraints = new MediaConstraints();
|
audioConstraints = new MediaConstraints();
|
||||||
videoConstraints = new MediaConstraints();
|
videoConstraints = new MediaConstraints();
|
||||||
|
|
||||||
//Create a VideoSource instance
|
|
||||||
videoSource = peerConnectionFactory.createVideoSource(videoCapturer);
|
|
||||||
localVideoTrack = peerConnectionFactory.createVideoTrack("NCv0", videoSource);
|
|
||||||
|
|
||||||
//create an AudioSource instance
|
|
||||||
audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
|
|
||||||
localAudioTrack = peerConnectionFactory.createAudioTrack("NCa0", audioSource);
|
|
||||||
|
|
||||||
localMediaStream = peerConnectionFactory.createLocalMediaStream("NCMS");
|
localMediaStream = peerConnectionFactory.createLocalMediaStream("NCMS");
|
||||||
localMediaStream.addTrack(localAudioTrack);
|
|
||||||
localMediaStream.addTrack(localVideoTrack);
|
|
||||||
|
|
||||||
// Create and audio manager that will take care of audio routing,
|
// Create and audio manager that will take care of audio routing,
|
||||||
// audio modes, audio device enumeration etc.
|
// audio modes, audio device enumeration etc.
|
||||||
@ -448,14 +509,6 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
startVideoCapture();
|
|
||||||
|
|
||||||
//create a videoRenderer based on SurfaceViewRenderer instance
|
|
||||||
localRenderer = new VideoRenderer(fullScreenVideoView);
|
|
||||||
// And finally, with our VideoRenderer ready, we
|
|
||||||
// can add our renderer to the VideoTrack.
|
|
||||||
localVideoTrack.addRenderer(localRenderer);
|
|
||||||
|
|
||||||
iceServers = new ArrayList<>();
|
iceServers = new ArrayList<>();
|
||||||
|
|
||||||
//create sdpConstraints
|
//create sdpConstraints
|
||||||
@ -464,13 +517,41 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
|
sdpConstraints.mandatory.add(new MediaConstraints.KeyValuePair("OfferToReceiveVideo", "true"));
|
||||||
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("internalSctpDataChannels", "true"));
|
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("internalSctpDataChannels", "true"));
|
||||||
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
|
sdpConstraints.optional.add(new MediaConstraints.KeyValuePair("DtlsSrtpKeyAgreement", "true"));
|
||||||
|
}
|
||||||
|
|
||||||
|
private void cameraInitialization() {
|
||||||
|
videoCapturer = createCameraCapturer(cameraEnumerator);
|
||||||
|
|
||||||
|
//Create a VideoSource instance
|
||||||
|
videoSource = peerConnectionFactory.createVideoSource(videoCapturer);
|
||||||
|
localVideoTrack = peerConnectionFactory.createVideoTrack("NCv0", videoSource);
|
||||||
|
localMediaStream.addTrack(localVideoTrack);
|
||||||
|
|
||||||
|
//create a videoRenderer based on SurfaceViewRenderer instance
|
||||||
|
localRenderer = new VideoRenderer(fullScreenVideoView);
|
||||||
|
// And finally, with our VideoRenderer ready, we
|
||||||
|
// can add our renderer to the VideoTrack.
|
||||||
|
localVideoTrack.addRenderer(localRenderer);
|
||||||
|
|
||||||
|
cameraInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void microphoneInitialization() {
|
||||||
|
//create an AudioSource instance
|
||||||
|
audioSource = peerConnectionFactory.createAudioSource(audioConstraints);
|
||||||
|
localAudioTrack = peerConnectionFactory.createAudioTrack("NCa0", audioSource);
|
||||||
|
localMediaStream.addTrack(localAudioTrack);
|
||||||
|
|
||||||
|
microphoneInitialized = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
private void startCall() {
|
||||||
|
inCall = true;
|
||||||
animateCallControls(false, 5000);
|
animateCallControls(false, 5000);
|
||||||
startPullingSignalingMessages(false);
|
startPullingSignalingMessages(false);
|
||||||
registerNetworkReceiver();
|
registerNetworkReceiver();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@OnClick({R.id.full_screen_surface_view, R.id.remote_renderers_layout})
|
@OnClick({R.id.full_screen_surface_view, R.id.remote_renderers_layout})
|
||||||
public void showCallControls() {
|
public void showCallControls() {
|
||||||
if (callControls.getVisibility() != View.VISIBLE) {
|
if (callControls.getVisibility() != View.VISIBLE) {
|
||||||
@ -810,6 +891,7 @@ public class CallActivity extends AppCompatActivity {
|
|||||||
private void hangup(boolean dueToNetworkChange) {
|
private void hangup(boolean dueToNetworkChange) {
|
||||||
|
|
||||||
leavingCall = true;
|
leavingCall = true;
|
||||||
|
inCall = false;
|
||||||
dispose(null);
|
dispose(null);
|
||||||
|
|
||||||
for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) {
|
for (int i = 0; i < magicPeerConnectionWrapperList.size(); i++) {
|
||||||
|
@ -74,7 +74,7 @@
|
|||||||
android:layout_marginEnd="@dimen/margin_between_elements"
|
android:layout_marginEnd="@dimen/margin_between_elements"
|
||||||
android:layout_toEndOf="@id/call_control_hangup"
|
android:layout_toEndOf="@id/call_control_hangup"
|
||||||
android:background="?android:selectableItemBackgroundBorderless"
|
android:background="?android:selectableItemBackgroundBorderless"
|
||||||
android:src="@drawable/ic_videocam_white_24px"/>
|
android:src="@drawable/ic_videocam_off_white_24px"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/call_control_microphone"
|
android:id="@+id/call_control_microphone"
|
||||||
@ -83,7 +83,7 @@
|
|||||||
android:layout_marginEnd="@dimen/margin_between_elements"
|
android:layout_marginEnd="@dimen/margin_between_elements"
|
||||||
android:layout_toEndOf="@id/call_control_camera"
|
android:layout_toEndOf="@id/call_control_camera"
|
||||||
android:background="?android:selectableItemBackgroundBorderless"
|
android:background="?android:selectableItemBackgroundBorderless"
|
||||||
android:src="@drawable/ic_mic_white_24px"/>
|
android:src="@drawable/ic_mic_off_white_24px"/>
|
||||||
|
|
||||||
<ImageButton
|
<ImageButton
|
||||||
android:id="@+id/call_control_switch_camera"
|
android:id="@+id/call_control_switch_camera"
|
||||||
|
@ -80,9 +80,18 @@
|
|||||||
<string name="nc_more_contacts_selected">contacts selected</string>
|
<string name="nc_more_contacts_selected">contacts selected</string>
|
||||||
|
|
||||||
<!-- Permissions -->
|
<!-- Permissions -->
|
||||||
<string name="nc_permissions">Permissions may need to be granted to establish a video call. Please click \"ALLOW\" in
|
<string name="nc_permissions">Permissions need to be granted to establish a video and/or audio call. Please
|
||||||
the upcoming system dialog.</string>
|
click \"ALLOW\" in the upcoming system dialog.</string>
|
||||||
|
<string name="nc_permissions_audio">Microphone permission needs to be granted to enable audio calls. Please
|
||||||
|
click \"ALLOW\" in the upcoming system dialog.</string>
|
||||||
|
<string name="nc_permissions_video">Camera permission needs to be granted to enable video calls. Please
|
||||||
|
click \"ALLOW\" in the upcoming system dialog.</string>
|
||||||
<string name="nc_permissions_permanently_denied">To make video calls, grant \"Camera\" and \"Microphone\" permissions in the system settings.</string>
|
<string name="nc_permissions_permanently_denied">To make video calls, grant \"Camera\" and \"Microphone\" permissions in the system settings.</string>
|
||||||
|
<string name="nc_camera_permission_permanently_denied">To enable video communication, grant \"Camera\" permission
|
||||||
|
the system
|
||||||
|
settings.</string>
|
||||||
|
<string name="nc_microphone_permission_permanently_denied">To enable voice communication, grant \"Microphone\"
|
||||||
|
permission the system settings.</string>
|
||||||
<string name="nc_permissions_settings">Open settings</string>
|
<string name="nc_permissions_settings">Open settings</string>
|
||||||
|
|
||||||
</resources>
|
</resources>
|
||||||
|
Loading…
Reference in New Issue
Block a user