diff --git a/CHANGELOG.md b/CHANGELOG.md index 9659f94b3..e19a0213f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -10,6 +10,7 @@ Types of changes can be: Added/Changed/Deprecated/Removed/Fixed/Security - open files inside app (jpg, .png, .gif, .mp3, .mp4, .mov, .wav, .txt, .md) - other data types are opened with external apps if they are able to handle it - edit profile information and privacy settings +- add grid view for calls, make own video movable ### Changed - improve conversation list design and dark/light theming (@AndyScherzinger) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/ParticipantDisplayItem.java b/app/src/main/java/com/nextcloud/talk/adapters/ParticipantDisplayItem.java new file mode 100644 index 000000000..aefb742d8 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/ParticipantDisplayItem.java @@ -0,0 +1,115 @@ +package com.nextcloud.talk.adapters; + +import org.webrtc.EglBase; +import org.webrtc.MediaStream; + +public class ParticipantDisplayItem { + private String userId; + private String session; + private String nick; + private String urlForAvatar; + private MediaStream mediaStream; + private String streamType; + private boolean streamEnabled; + private EglBase rootEglBase; + private boolean isAudioEnabled; + + public ParticipantDisplayItem(String userId, String session, String nick, String urlForAvatar, MediaStream mediaStream, String streamType, boolean streamEnabled, EglBase rootEglBase) { + this.userId = userId; + this.session = session; + this.nick = nick; + this.urlForAvatar = urlForAvatar; + this.mediaStream = mediaStream; + this.streamType = streamType; + this.streamEnabled = streamEnabled; + this.rootEglBase = rootEglBase; + } + + public String getUserId() { + return userId; + } + + public void setUserId(String userId) { + this.userId = userId; + } + + public String getSession() { + return session; + } + + public void setSession(String session) { + this.session = session; + } + + public String getNick() { + return nick; + } + + public void setNick(String nick) { + this.nick = nick; + } + + public String getUrlForAvatar() { + return urlForAvatar; + } + + public void setUrlForAvatar(String urlForAvatar) { + this.urlForAvatar = urlForAvatar; + } + + public MediaStream getMediaStream() { + return mediaStream; + } + + public void setMediaStream(MediaStream mediaStream) { + this.mediaStream = mediaStream; + } + + public String getStreamType() { + return streamType; + } + + public void setStreamType(String streamType) { + this.streamType = streamType; + } + + public boolean isStreamEnabled() { + return streamEnabled; + } + + public void setStreamEnabled(boolean streamEnabled) { + this.streamEnabled = streamEnabled; + } + + public EglBase getRootEglBase() { + return rootEglBase; + } + + public void setRootEglBase(EglBase rootEglBase) { + this.rootEglBase = rootEglBase; + } + + public boolean isAudioEnabled() { + return isAudioEnabled; + } + + public void setAudioEnabled(boolean audioEnabled) { + isAudioEnabled = audioEnabled; + } + + @Override + public String toString() { + return "ParticipantSession{" + + "userId='" + userId + '\'' + + ", session='" + session + '\'' + + ", nick='" + nick + '\'' + + ", urlForAvatar='" + urlForAvatar + '\'' + + ", mediaStream=" + mediaStream + + ", streamType='" + streamType + '\'' + + ", streamEnabled=" + streamEnabled + + ", rootEglBase=" + rootEglBase + + '}'; + } +} + + diff --git a/app/src/main/java/com/nextcloud/talk/adapters/ParticipantsAdapter.java b/app/src/main/java/com/nextcloud/talk/adapters/ParticipantsAdapter.java new file mode 100644 index 000000000..2a29274cb --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/adapters/ParticipantsAdapter.java @@ -0,0 +1,154 @@ +package com.nextcloud.talk.adapters; + +import android.content.Context; +import android.graphics.Color; +import android.util.Log; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.BaseAdapter; +import android.widget.ImageView; +import android.widget.LinearLayout; +import android.widget.RelativeLayout; +import android.widget.TextView; + +import com.facebook.drawee.backends.pipeline.Fresco; +import com.facebook.drawee.interfaces.DraweeController; +import com.facebook.drawee.view.SimpleDraweeView; +import com.nextcloud.talk.R; +import com.nextcloud.talk.utils.DisplayUtils; + +import org.webrtc.MediaStream; +import org.webrtc.RendererCommon; +import org.webrtc.SurfaceViewRenderer; +import org.webrtc.VideoTrack; + +import java.util.ArrayList; +import java.util.Map; + +public class ParticipantsAdapter extends BaseAdapter { + + private static final String TAG = "ParticipantsAdapter"; + + private final Context mContext; + private final ArrayList participantDisplayItems; + private final RelativeLayout gridViewWrapper; + private final LinearLayout callInfosLinearLayout; + private final int columns; + private final boolean isVoiceOnlyCall; + + public ParticipantsAdapter(Context mContext, + Map participantDisplayItems, + RelativeLayout gridViewWrapper, + LinearLayout linearLayout, + int columns, + boolean isVoiceOnlyCall) { + this.mContext = mContext; + this.gridViewWrapper = gridViewWrapper; + this.callInfosLinearLayout = linearLayout; + this.columns = columns; + this.isVoiceOnlyCall = isVoiceOnlyCall; + + this.participantDisplayItems = new ArrayList<>(); + this.participantDisplayItems.addAll(participantDisplayItems.values()); + } + + + @Override + public int getCount() { + return participantDisplayItems.size(); + } + + @Override + public ParticipantDisplayItem getItem(int position) { + return participantDisplayItems.get(position); + } + + @Override + public long getItemId(int position) { + return 0; + } + + @Override + public View getView(int position, View convertView, ViewGroup parent) { + ParticipantDisplayItem participantDisplayItem = getItem(position); + + SurfaceViewRenderer surfaceViewRenderer; + if (convertView == null) { + convertView = LayoutInflater.from(mContext).inflate(R.layout.call_item, parent, false); + convertView.setVisibility(View.VISIBLE); + + surfaceViewRenderer = convertView.findViewById(R.id.surface_view); + try { + surfaceViewRenderer.setMirror(false); + surfaceViewRenderer.init(participantDisplayItem.getRootEglBase().getEglBaseContext(), null); + surfaceViewRenderer.setZOrderMediaOverlay(false); + // disabled because it causes some devices to crash + surfaceViewRenderer.setEnableHardwareScaler(false); + surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); + } catch (Exception e) { + Log.e(TAG, "error while initializing surfaceViewRenderer", e); + } + } else { + surfaceViewRenderer = convertView.findViewById(R.id.surface_view); + } + + ViewGroup.LayoutParams layoutParams = convertView.getLayoutParams(); + layoutParams.height = scaleGridViewItemHeight(); + convertView.setLayoutParams(layoutParams); + + + TextView nickTextView = convertView.findViewById(R.id.peer_nick_text_view); + SimpleDraweeView imageView = convertView.findViewById(R.id.avatarImageView); + + MediaStream mediaStream = participantDisplayItem.getMediaStream(); + if (hasVideoStream(participantDisplayItem, mediaStream)) { + VideoTrack videoTrack = mediaStream.videoTracks.get(0); + videoTrack.addSink(surfaceViewRenderer); + imageView.setVisibility(View.INVISIBLE); + surfaceViewRenderer.setVisibility(View.VISIBLE); + nickTextView.setVisibility(View.GONE); + } else { + imageView.setVisibility(View.VISIBLE); + surfaceViewRenderer.setVisibility(View.INVISIBLE); + nickTextView.setVisibility(View.VISIBLE); + nickTextView.setText(participantDisplayItem.getNick()); + + imageView.setController(null); + DraweeController draweeController = Fresco.newDraweeControllerBuilder() + .setOldController(imageView.getController()) + .setImageRequest(DisplayUtils.getImageRequestForUrl(participantDisplayItem.getUrlForAvatar(), null)) + .build(); + imageView.setController(draweeController); + } + + ImageView audioOffView = convertView.findViewById(R.id.remote_audio_off); + if (!participantDisplayItem.isAudioEnabled()) { + audioOffView.setVisibility(View.VISIBLE); + } else { + audioOffView.setVisibility(View.INVISIBLE); + } + return convertView; + } + + private boolean hasVideoStream(ParticipantDisplayItem participantDisplayItem, MediaStream mediaStream) { + return mediaStream != null && mediaStream.videoTracks != null && mediaStream.videoTracks.size() > 0 && participantDisplayItem.isStreamEnabled(); + } + + private int scaleGridViewItemHeight() { + int headerHeight = 0; + if (callInfosLinearLayout.getVisibility() == View.VISIBLE && isVoiceOnlyCall) { + headerHeight = callInfosLinearLayout.getHeight(); + } + int itemHeight = (gridViewWrapper.getHeight() - headerHeight) / getRowsCount(getCount()); + return itemHeight; + } + + private int getRowsCount(int items) { + int rows = (int) Math.ceil((double) items / (double) columns); + if (rows == 0) { + rows = 1; + } + return rows; + } +} diff --git a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java index 781a1d81a..21416baca 100644 --- a/app/src/main/java/com/nextcloud/talk/controllers/CallController.java +++ b/app/src/main/java/com/nextcloud/talk/controllers/CallController.java @@ -34,12 +34,15 @@ import android.os.Bundle; import android.os.Handler; import android.os.Looper; import android.text.TextUtils; +import android.util.DisplayMetrics; import android.util.Log; import android.view.LayoutInflater; import android.view.MotionEvent; import android.view.View; import android.view.ViewGroup; +import android.view.ViewTreeObserver; import android.widget.FrameLayout; +import android.widget.GridView; import android.widget.ImageView; import android.widget.LinearLayout; import android.widget.ProgressBar; @@ -51,11 +54,11 @@ import androidx.annotation.Nullable; import androidx.appcompat.app.AppCompatActivity; import com.bluelinelabs.logansquare.LoganSquare; -import com.facebook.drawee.backends.pipeline.Fresco; -import com.facebook.drawee.interfaces.DraweeController; import com.facebook.drawee.view.SimpleDraweeView; import com.nextcloud.talk.R; import com.nextcloud.talk.activities.MagicCallActivity; +import com.nextcloud.talk.adapters.ParticipantDisplayItem; +import com.nextcloud.talk.adapters.ParticipantsAdapter; import com.nextcloud.talk.api.NcApi; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.controllers.base.BaseController; @@ -143,6 +146,7 @@ import javax.inject.Inject; import autodagger.AutoInjector; import butterknife.BindView; import butterknife.OnClick; +import butterknife.OnItemClick; import butterknife.OnLongClick; import io.reactivex.Observable; import io.reactivex.Observer; @@ -180,8 +184,8 @@ public class CallController extends BaseController { SurfaceViewRenderer pipVideoView; @BindView(R.id.controllerCallLayout) RelativeLayout controllerCallLayout; - @BindView(R.id.remote_renderers_layout) - LinearLayout remoteRenderersLayout; + @BindView(R.id.gridview) + GridView gridView; @BindView(R.id.callControlsLinearLayout) LinearLayout callControls; @@ -201,6 +205,9 @@ public class CallController extends BaseController { @BindView(R.id.callConversationNameTextView) TextView callConversationNameTextView; + @BindView(R.id.selfVideoView) + FrameLayout selfVideoView; + @BindView(R.id.callStateRelativeLayoutView) RelativeLayout callStateView; @@ -264,7 +271,6 @@ public class CallController extends BaseController { // push to talk private boolean isPTTActive = false; private PulseAnimation pulseAnimation; - private View.OnClickListener videoOnClickListener; private String baseUrl; private String roomId; @@ -286,6 +292,9 @@ public class CallController extends BaseController { private MediaPlayer mediaPlayer; + private Map participantDisplayItems; + private ParticipantsAdapter participantsAdapter; + @Parcel public enum CallStatus { CONNECTING, CALLING_TIMEOUT, JOINED, IN_CONVERSATION, RECONNECTING, OFFLINE, LEAVING, PUBLISHER_FAILED @@ -345,19 +354,18 @@ public class CallController extends BaseController { } } + @SuppressLint("ClickableViewAccessibility") @Override protected void onViewBound(@NonNull View view) { super.onViewBound(view); microphoneControlButton.setOnTouchListener(new MicrophoneButtonTouchListener()); - videoOnClickListener = new VideoClickListener(); pulseAnimation = PulseAnimation.create().with(microphoneControlButton) .setDuration(310) .setRepeatCount(PulseAnimation.INFINITE) .setRepeatMode(PulseAnimation.REVERSE); - setPipVideoViewDimensions(); try { cache.evictAll(); @@ -368,7 +376,7 @@ public class CallController extends BaseController { callControls.setZ(100.0f); basicInitialization(); initViews(); - + initPipView(); initiateCall(); } @@ -467,7 +475,11 @@ public class CallController extends BaseController { }); } + + @SuppressLint("ClickableViewAccessibility") private void initViews() { + participantDisplayItems = new HashMap<>(); + if (isVoiceOnlyCall) { callControlEnableSpeaker.setVisibility(View.VISIBLE); cameraSwitchButton.setVisibility(View.GONE); @@ -476,7 +488,7 @@ public class CallController extends BaseController { RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT); params.addRule(RelativeLayout.BELOW, R.id.callInfosLinearLayout); - remoteRenderersLayout.setLayoutParams(params); + gridView.setLayoutParams(params); } else { callControlEnableSpeaker.setVisibility(View.GONE); if (cameraEnumerator.getDeviceNames().length < 2) { @@ -488,7 +500,77 @@ public class CallController extends BaseController { // disabled because it causes some devices to crash pipVideoView.setEnableHardwareScaler(false); pipVideoView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); + + pipVideoView.setOnTouchListener(new SelfVideoTouchListener()); + } + + gridView.setOnTouchListener(new View.OnTouchListener() { + public boolean onTouch(View v, MotionEvent me) { + int action = me.getActionMasked(); + if (action == MotionEvent.ACTION_DOWN) { + showCallControls(); + } + return true; + } + }); + + initGridAdapter(); + } + + private void initGridAdapter() { + GridView gridView = conversationView.findViewById(R.id.gridview); + + int columns; + int participantsInGrid = participantDisplayItems.size(); + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { + if (participantsInGrid > 8) { + columns = 3; + } else if (participantsInGrid > 2) { + columns = 2; + } else { + columns = 1; + } + } else { + if (participantsInGrid > 8) { + columns = 4; + } else if (participantsInGrid > 2) { + columns = 3; + } else if (participantsInGrid > 1) { + columns = 2; + } else { + columns = 1; + } + } + + gridView.setNumColumns(columns); + + RelativeLayout gridViewWrapper = conversationView.findViewById(R.id.conversationRelativeLayoutView); + gridViewWrapper.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + gridViewWrapper.getViewTreeObserver().removeOnGlobalLayoutListener(this); + int height = gridViewWrapper.getMeasuredHeight(); + gridView.setMinimumHeight(height); + } + }); + + LinearLayout callInfosLinearLayout = conversationView.findViewById(R.id.callInfosLinearLayout); + callInfosLinearLayout.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { + @Override + public void onGlobalLayout() { + callInfosLinearLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this); + } + }); + + participantsAdapter = new ParticipantsAdapter( + this.getActivity(), + participantDisplayItems, + gridViewWrapper, + callInfosLinearLayout, + columns, + isVoiceOnlyCall); + gridView.setAdapter(participantsAdapter); } @@ -534,7 +616,7 @@ public class CallController extends BaseController { fetchSignalingSettings(); } } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied(getActivity(), - PERMISSIONS_CALL)) { + PERMISSIONS_CALL)) { checkIfSomeAreApproved(); } @@ -744,7 +826,7 @@ public class CallController extends BaseController { } } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied(getActivity(), - PERMISSIONS_MICROPHONE)) { + PERMISSIONS_MICROPHONE)) { // Microphone permission is permanently denied so we cannot request it normally. OpenAppDetailsDialogFragment.show( @@ -787,7 +869,7 @@ public class CallController extends BaseController { toggleMedia(videoOn, true); } else if (getActivity() != null && EffortlessPermissions.somePermissionPermanentlyDenied(getActivity(), - PERMISSIONS_CAMERA)) { + PERMISSIONS_CAMERA)) { // Camera permission is permanently denied so we cannot request it normally. OpenAppDetailsDialogFragment.show( R.string.nc_camera_permission_permanently_denied, @@ -803,7 +885,7 @@ public class CallController extends BaseController { } - @OnClick({R.id.call_control_switch_camera, R.id.pip_video_view}) + @OnClick({R.id.call_control_switch_camera}) public void switchCamera() { CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer; if (cameraVideoCapturer != null) { @@ -1046,7 +1128,7 @@ public class CallController extends BaseController { if (!conversationUser.getUserId().equals("?")) { try { userUtils.createOrUpdateUser(null, null, null, null, null, null, null, - conversationUser.getId(), null, null, LoganSquare.serialize(externalSignalingServer)) + conversationUser.getId(), null, null, LoganSquare.serialize(externalSignalingServer)) .subscribeOn(Schedulers.io()) .subscribe(); } catch (IOException exception) { @@ -1059,11 +1141,11 @@ public class CallController extends BaseController { i++) { iceServer = signalingSettingsOverall.getOcs().getSettings().getStunServers().get(i); if (TextUtils.isEmpty(iceServer.getUsername()) || TextUtils.isEmpty(iceServer - .getCredential())) { + .getCredential())) { iceServers.add(new PeerConnection.IceServer(iceServer.getUrl())); } else { iceServers.add(new PeerConnection.IceServer(iceServer.getUrl(), - iceServer.getUsername(), iceServer.getCredential())); + iceServer.getUsername(), iceServer.getCredential())); } } } @@ -1074,11 +1156,11 @@ public class CallController extends BaseController { iceServer = signalingSettingsOverall.getOcs().getSettings().getTurnServers().get(i); for (int j = 0; j < iceServer.getUrls().size(); j++) { if (TextUtils.isEmpty(iceServer.getUsername()) || TextUtils.isEmpty(iceServer - .getCredential())) { + .getCredential())) { iceServers.add(new PeerConnection.IceServer(iceServer.getUrls().get(j))); } else { iceServers.add(new PeerConnection.IceServer(iceServer.getUrls().get(j), - iceServer.getUsername(), iceServer.getCredential())); + iceServer.getUsername(), iceServer.getCredential())); } } } @@ -1154,7 +1236,7 @@ public class CallController extends BaseController { if (TextUtils.isEmpty(callSession)) { ncApi.joinRoom(credentials, ApiUtils.getUrlForSettingMyselfAsActiveParticipant(baseUrl, - roomToken), conversationPassword) + roomToken), conversationPassword) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(3) @@ -1207,7 +1289,7 @@ public class CallController extends BaseController { } ncApi.joinCall(credentials, - ApiUtils.getUrlForCall(baseUrl, roomToken), inCallFlag) + ApiUtils.getUrlForCall(baseUrl, roomToken), inCallFlag) .subscribeOn(Schedulers.io()) .retry(3) .observeOn(AndroidSchedulers.mainThread()) @@ -1381,7 +1463,7 @@ public class CallController extends BaseController { } } - @OnClick({R.id.pip_video_view, R.id.remote_renderers_layout}) + @OnItemClick({R.id.gridview}) public void showCallControls() { animateCallControls(true, 0); } @@ -1414,10 +1496,10 @@ public class CallController extends BaseController { processUsersInRoom((List>) signaling.getMessageWrapper()); } else if ("message".equals(messageType)) { NCSignalingMessage ncSignalingMessage = LoganSquare.parse(signaling.getMessageWrapper().toString(), - NCSignalingMessage.class); + NCSignalingMessage.class); processMessage(ncSignalingMessage); } else { - Log.d(TAG, "Something went very very wrong"); + Log.e(TAG, "unexpected message type when receiving signaling message"); } } @@ -1425,7 +1507,7 @@ public class CallController extends BaseController { if (ncSignalingMessage.getRoomType().equals("video") || ncSignalingMessage.getRoomType().equals("screen")) { MagicPeerConnectionWrapper magicPeerConnectionWrapper = getPeerConnectionWrapperForSessionIdAndType(ncSignalingMessage.getFrom(), - ncSignalingMessage.getRoomType(), false); + ncSignalingMessage.getRoomType(), false); String type = null; if (ncSignalingMessage.getPayload() != null && ncSignalingMessage.getPayload().getType() != null) { @@ -1446,7 +1528,7 @@ public class CallController extends BaseController { String sessionDescriptionStringWithPreferredCodec = MagicWebRTCUtils.preferCodec (ncSignalingMessage.getPayload().getSdp(), - "H264", false); + "H264", false); sessionDescriptionWithPreferredCodec = new SessionDescription( SessionDescription.Type.fromCanonicalForm(type), @@ -1454,13 +1536,13 @@ public class CallController extends BaseController { if (magicPeerConnectionWrapper.getPeerConnection() != null) { magicPeerConnectionWrapper.getPeerConnection().setRemoteDescription(magicPeerConnectionWrapper - .getMagicSdpObserver(), sessionDescriptionWithPreferredCodec); + .getMagicSdpObserver(), sessionDescriptionWithPreferredCodec); } break; case "candidate": NCIceCandidate ncIceCandidate = ncSignalingMessage.getPayload().getIceCandidate(); IceCandidate iceCandidate = new IceCandidate(ncIceCandidate.getSdpMid(), - ncIceCandidate.getSdpMLineIndex(), ncIceCandidate.getCandidate()); + ncIceCandidate.getSdpMLineIndex(), ncIceCandidate.getCandidate()); magicPeerConnectionWrapper.addCandidate(iceCandidate); break; case "endOfCandidates": @@ -1471,7 +1553,7 @@ public class CallController extends BaseController { } } } else { - Log.d(TAG, "Something went very very wrong"); + Log.e(TAG, "unexpected RoomType while processing NCSignalingMessage"); } } @@ -1658,6 +1740,7 @@ public class CallController extends BaseController { } private void getPeersForCall() { + Log.d(TAG, "getPeersForCall"); ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) .subscribeOn(Schedulers.io()) .subscribe(new Observer() { @@ -1671,15 +1754,12 @@ public class CallController extends BaseController { participantMap = new HashMap<>(); for (Participant participant : participantsOverall.getOcs().getData()) { participantMap.put(participant.getSessionId(), participant); - if (getActivity() != null) { - getActivity().runOnUiThread(() -> setupAvatarForSession(participant.getSessionId())); - } } } @Override public void onError(Throwable e) { - + Log.e(TAG, "error while executing getPeersForCall", e); } @Override @@ -1711,18 +1791,18 @@ public class CallController extends BaseController { } else { if (hasMCU && publisher) { magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, - iceServers, sdpConstraintsForMCU, sessionId, callSession, localMediaStream, true, true, type); + iceServers, sdpConstraintsForMCU, sessionId, callSession, localMediaStream, true, true, type); } else if (hasMCU) { magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, - iceServers, sdpConstraints, sessionId, callSession, null, false, true, type); + iceServers, sdpConstraints, sessionId, callSession, null, false, true, type); } else { if (!"screen".equals(type)) { magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, - iceServers, sdpConstraints, sessionId, callSession, localMediaStream, false, false, type); + iceServers, sdpConstraints, sessionId, callSession, localMediaStream, false, false, type); } else { magicPeerConnectionWrapper = new MagicPeerConnectionWrapper(peerConnectionFactory, - iceServers, sdpConstraints, sessionId, callSession, null, false, false, type); + iceServers, sdpConstraints, sessionId, callSession, null, false, false, type); } } @@ -1756,9 +1836,10 @@ public class CallController extends BaseController { magicPeerConnectionWrapper = magicPeerConnectionWrappers.get(i); if (magicPeerConnectionWrapper.getSessionId().equals(sessionId)) { if (magicPeerConnectionWrapper.getVideoStreamType().equals("screen") || !justScreen) { - MagicPeerConnectionWrapper finalMagicPeerConnectionWrapper = magicPeerConnectionWrapper; - getActivity().runOnUiThread(() -> removeMediaStream(sessionId + "+" + - finalMagicPeerConnectionWrapper.getVideoStreamType())); + + + // TODO runOnUiThread not necessary??? + getActivity().runOnUiThread(() -> removeMediaStream(sessionId)); deleteMagicPeerConnection(magicPeerConnectionWrapper); } } @@ -1767,15 +1848,9 @@ public class CallController extends BaseController { } private void removeMediaStream(String sessionId) { - if (remoteRenderersLayout != null && remoteRenderersLayout.getChildCount() > 0) { - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionId); - if (relativeLayout != null) { - SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view); - surfaceViewRenderer.release(); - remoteRenderersLayout.removeView(relativeLayout); - remoteRenderersLayout.invalidate(); - } - } + Log.d(TAG, "removeMediaStream"); + participantDisplayItems.remove(sessionId); + initGridAdapter(); if (callControls != null) { callControls.setZ(100.0f); @@ -1785,63 +1860,76 @@ public class CallController extends BaseController { @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(ConfigurationChangeEvent configurationChangeEvent) { powerManagerUtils.setOrientation(Objects.requireNonNull(getResources()).getConfiguration().orientation); - - - if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { - remoteRenderersLayout.setOrientation(LinearLayout.HORIZONTAL); - } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - remoteRenderersLayout.setOrientation(LinearLayout.VERTICAL); - } - - setPipVideoViewDimensions(); + initGridAdapter(); + initPipView(); } - private void setPipVideoViewDimensions() { + private void initPipView() { FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) pipVideoView.getLayoutParams(); + DisplayMetrics displayMetrics = getApplicationContext().getResources().getDisplayMetrics(); + int screenWidthPx = displayMetrics.widthPixels; + + int screenWidthDp = (int) DisplayUtils.convertPixelToDp(screenWidthPx, getApplicationContext()); + + float newXafterRotate = 0; + float newYafterRotate; + if (callInfosLinearLayout.getVisibility() == View.VISIBLE) { + newYafterRotate = 250; + } else { + newYafterRotate = 20; + } + if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) { - remoteRenderersLayout.setOrientation(LinearLayout.HORIZONTAL); layoutParams.height = (int) getResources().getDimension(R.dimen.large_preview_dimension); layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; - pipVideoView.setLayoutParams(layoutParams); + newXafterRotate = (float) (screenWidthDp - getResources().getDimension(R.dimen.large_preview_dimension) * 0.8); + } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) { - remoteRenderersLayout.setOrientation(LinearLayout.VERTICAL); layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; layoutParams.width = (int) getResources().getDimension(R.dimen.large_preview_dimension); - pipVideoView.setLayoutParams(layoutParams); + newXafterRotate = (float) (screenWidthDp - getResources().getDimension(R.dimen.large_preview_dimension) * 0.5); } + pipVideoView.setLayoutParams(layoutParams); + + int newXafterRotatePx = (int) DisplayUtils.convertDpToPixel(newXafterRotate, getApplicationContext()); + selfVideoView.setY(newYafterRotate); + selfVideoView.setX(newXafterRotatePx); } @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(PeerConnectionEvent peerConnectionEvent) { + String sessionId = peerConnectionEvent.getSessionId(); + if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType - .PEER_CLOSED)) { - endPeerConnection(peerConnectionEvent.getSessionId(), peerConnectionEvent.getVideoStreamType().equals("screen")); + .PEER_CLOSED)) { + endPeerConnection(sessionId, peerConnectionEvent.getVideoStreamType().equals("screen")); } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.SENSOR_FAR) || + .PeerConnectionEventType.SENSOR_FAR) || peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.SENSOR_NEAR)) { + .PeerConnectionEventType.SENSOR_NEAR)) { if (!isVoiceOnlyCall) { boolean enableVideo = peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.SENSOR_FAR) && videoOn; + .PeerConnectionEventType.SENSOR_FAR) && videoOn; if (getActivity() != null && EffortlessPermissions.hasPermissions(getActivity(), PERMISSIONS_CAMERA) && (currentCallStatus.equals(CallStatus.CONNECTING) || isConnectionEstablished()) && videoOn && enableVideo != localVideoTrack.enabled()) { toggleMedia(enableVideo, true); } } - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.NICK_CHANGE)) { - gotNick(peerConnectionEvent.getSessionId(), peerConnectionEvent.getNick(), peerConnectionEvent.getVideoStreamType()); - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.VIDEO_CHANGE) && !isVoiceOnlyCall) { - gotAudioOrVideoChange(true, peerConnectionEvent.getSessionId() + "+" + peerConnectionEvent.getVideoStreamType(), - peerConnectionEvent.getChangeValue()); - } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent - .PeerConnectionEventType.AUDIO_CHANGE)) { - gotAudioOrVideoChange(false, peerConnectionEvent.getSessionId() + "+" + peerConnectionEvent.getVideoStreamType(), - peerConnectionEvent.getChangeValue()); + } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE)) { + participantDisplayItems.get(sessionId).setNick(peerConnectionEvent.getNick()); + participantsAdapter.notifyDataSetChanged(); + + } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.VIDEO_CHANGE) && !isVoiceOnlyCall) { + participantDisplayItems.get(sessionId).setStreamEnabled(peerConnectionEvent.getChangeValue()); + participantsAdapter.notifyDataSetChanged(); + + } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.AUDIO_CHANGE)) { + participantDisplayItems.get(sessionId).setAudioEnabled(peerConnectionEvent.getChangeValue()); + participantsAdapter.notifyDataSetChanged(); + } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED)) { currentCallStatus = CallStatus.PUBLISHER_FAILED; webSocketClient.clearResumeId(); @@ -1894,11 +1982,20 @@ public class CallController extends BaseController { @Subscribe(threadMode = ThreadMode.MAIN) public void onMessageEvent(MediaStreamEvent mediaStreamEvent) { if (mediaStreamEvent.getMediaStream() != null) { - setupVideoStreamForLayout(mediaStreamEvent.getMediaStream(), mediaStreamEvent.getSession(), - mediaStreamEvent.getMediaStream().videoTracks != null - && mediaStreamEvent.getMediaStream().videoTracks.size() > 0, mediaStreamEvent.getVideoStreamType()); + boolean hasAtLeastOneVideoStream = mediaStreamEvent.getMediaStream().videoTracks != null + && mediaStreamEvent.getMediaStream().videoTracks.size() > 0; + + setupVideoStreamForLayout( + mediaStreamEvent.getMediaStream(), + mediaStreamEvent.getSession(), + hasAtLeastOneVideoStream, + mediaStreamEvent.getVideoStreamType()); } else { - setupVideoStreamForLayout(null, mediaStreamEvent.getSession(), false, mediaStreamEvent.getVideoStreamType()); + setupVideoStreamForLayout( + null, + mediaStreamEvent.getSession(), + false, + mediaStreamEvent.getVideoStreamType()); } } @@ -1949,7 +2046,7 @@ public class CallController extends BaseController { } ncApi.sendSignalingMessages(credentials, ApiUtils.getUrlForSignaling(baseUrl, urlToken), - strings.toString()) + strings.toString()) .retry(3) .subscribeOn(Schedulers.io()) .subscribe(new Observer() { @@ -1965,7 +2062,7 @@ public class CallController extends BaseController { try { receivedSignalingMessage(signalingOverall.getOcs().getSignalings().get(i)); } catch (IOException e) { - e.printStackTrace(); + Log.e(TAG, "", e); } } } @@ -1973,6 +2070,7 @@ public class CallController extends BaseController { @Override public void onError(Throwable e) { + Log.e(TAG, "", e); } @Override @@ -1991,161 +2089,49 @@ public class CallController extends BaseController { super.onRequestPermissionsResult(requestCode, permissions, grantResults); EffortlessPermissions.onRequestPermissionsResult(requestCode, permissions, grantResults, - this); + this); } - private void setupAvatarForSession(String session) { - if (remoteRenderersLayout != null) { - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(session + "+video"); - if (relativeLayout != null) { - SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); - - String userId; - String displayName; - - if (hasMCU) { - userId = webSocketClient.getUserIdForSession(session); - displayName = getPeerConnectionWrapperForSessionIdAndType(session, "video", false).getNick(); - } else { - userId = participantMap.get(session).getUserId(); - displayName = getPeerConnectionWrapperForSessionIdAndType(session, "video", false).getNick(); - } - - if (!TextUtils.isEmpty(userId) || !TextUtils.isEmpty(displayName)) { - - if (getActivity() != null) { - avatarImageView.setController(null); - - String urlForAvatar; - if (!TextUtils.isEmpty(userId)) { - urlForAvatar = ApiUtils.getUrlForAvatarWithName(baseUrl, - userId, - R.dimen.avatar_size_big); - } else { - urlForAvatar = ApiUtils.getUrlForAvatarWithNameForGuests(baseUrl, - displayName, - R.dimen.avatar_size_big); - } - - DraweeController draweeController = Fresco.newDraweeControllerBuilder() - .setOldController(avatarImageView.getController()) - .setImageRequest(DisplayUtils.getImageRequestForUrl(urlForAvatar, null)) - .build(); - avatarImageView.setController(draweeController); - } - } - } - } - } - - private void setupVideoStreamForLayout(@Nullable MediaStream mediaStream, String session, boolean enable, String videoStreamType) { - boolean isInitialLayoutSetupForPeer = false; - if (remoteRenderersLayout.findViewWithTag(session) == null) { - setupNewPeerLayout(session, videoStreamType); - isInitialLayoutSetupForPeer = true; - } - - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(session + "+" + videoStreamType); - SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view); - SimpleDraweeView imageView = relativeLayout.findViewById(R.id.avatarImageView); - - if (mediaStream != null && mediaStream.videoTracks != null && mediaStream.videoTracks.size() > 0 && enable) { - VideoTrack videoTrack = mediaStream.videoTracks.get(0); - - videoTrack.addSink(surfaceViewRenderer); - - imageView.setVisibility(View.INVISIBLE); - surfaceViewRenderer.setVisibility(View.VISIBLE); + private void setupVideoStreamForLayout(@Nullable MediaStream mediaStream, String session, boolean videoStreamEnabled, String videoStreamType) { + String nick; + if (hasExternalSignalingServer) { + nick = webSocketClient.getDisplayNameForSession(session); } else { - imageView.setVisibility(View.VISIBLE); - surfaceViewRenderer.setVisibility(View.INVISIBLE); - - if (isInitialLayoutSetupForPeer && isVoiceOnlyCall) { - gotAudioOrVideoChange(true, session, false); - } + nick = getPeerConnectionWrapperForSessionIdAndType(session, videoStreamType, false).getNick(); } + String userId; + if (hasMCU) { + userId = webSocketClient.getUserIdForSession(session); + } else { + userId = participantMap.get(session).getUserId(); + } + + String urlForAvatar; + if (!TextUtils.isEmpty(userId)) { + urlForAvatar = ApiUtils.getUrlForAvatarWithName(baseUrl, + userId, + R.dimen.avatar_size_big); + } else { + urlForAvatar = ApiUtils.getUrlForAvatarWithNameForGuests(baseUrl, + nick, + R.dimen.avatar_size_big); + } + + ParticipantDisplayItem participantDisplayItem = new ParticipantDisplayItem(userId, + session, + nick, + urlForAvatar, + mediaStream, + videoStreamType, + videoStreamEnabled, + rootEglBase); + participantDisplayItems.put(session, participantDisplayItem); + + initGridAdapter(); callControls.setZ(100.0f); } - private void gotAudioOrVideoChange(boolean video, String sessionId, boolean change) { - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionId); - if (relativeLayout != null) { - ImageView imageView; - SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView); - SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view); - - if (video) { - imageView = relativeLayout.findViewById(R.id.remote_video_off); - - if (change) { - avatarImageView.setVisibility(View.INVISIBLE); - surfaceViewRenderer.setVisibility(View.VISIBLE); - } else { - avatarImageView.setVisibility(View.VISIBLE); - surfaceViewRenderer.setVisibility(View.INVISIBLE); - } - } else { - imageView = relativeLayout.findViewById(R.id.remote_audio_off); - } - - if (change && imageView.getVisibility() != View.INVISIBLE) { - imageView.setVisibility(View.INVISIBLE); - } else if (!change && imageView.getVisibility() != View.VISIBLE) { - imageView.setVisibility(View.VISIBLE); - } - } - } - - private void setupNewPeerLayout(String session, String type) { - if (remoteRenderersLayout.findViewWithTag(session + "+" + type) == null && getActivity() != null) { - getActivity().runOnUiThread(() -> { - RelativeLayout relativeLayout = (RelativeLayout) - getActivity().getLayoutInflater().inflate(R.layout.call_item, remoteRenderersLayout, - false); - relativeLayout.setTag(session + "+" + type); - SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id - .surface_view); - - surfaceViewRenderer.setMirror(false); - surfaceViewRenderer.init(rootEglBase.getEglBaseContext(), null); - surfaceViewRenderer.setZOrderMediaOverlay(false); - // disabled because it causes some devices to crash - surfaceViewRenderer.setEnableHardwareScaler(false); - surfaceViewRenderer.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); - surfaceViewRenderer.setOnClickListener(videoOnClickListener); - remoteRenderersLayout.addView(relativeLayout); - if (hasExternalSignalingServer) { - gotNick(session, webSocketClient.getDisplayNameForSession(session), type); - } else { - gotNick(session, getPeerConnectionWrapperForSessionIdAndType(session, type, false).getNick(), type); - } - - if ("video".equals(type)) { - setupAvatarForSession(session); - } - - callControls.setZ(100.0f); - }); - } - } - - private void gotNick(String sessionId, String nick, String type) { - String remoteRendererTag = sessionId + "+" + type; - - if (controllerCallLayout != null) { - RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(remoteRendererTag); - TextView textView = relativeLayout.findViewById(R.id.peer_nick_text_view); - if (!textView.getText().equals(nick)) { - textView.setText(nick); - - if (getActivity() != null && type.equals("video")) { - getActivity().runOnUiThread(() -> setupAvatarForSession(sessionId)); - } - } - } - } - @OnClick(R.id.callStateRelativeLayoutView) public void onConnectingViewClick() { if (currentCallStatus.equals(CallStatus.CALLING_TIMEOUT)) { @@ -2180,8 +2166,8 @@ public class CallController extends BaseController { callStateView.setVisibility(View.VISIBLE); } - if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) { - remoteRenderersLayout.setVisibility(View.INVISIBLE); + if (gridView.getVisibility() != View.INVISIBLE) { + gridView.setVisibility(View.INVISIBLE); } if (progressBar.getVisibility() != View.VISIBLE) { @@ -2206,8 +2192,8 @@ public class CallController extends BaseController { progressBar.setVisibility(View.GONE); } - if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) { - remoteRenderersLayout.setVisibility(View.INVISIBLE); + if (gridView.getVisibility() != View.INVISIBLE) { + gridView.setVisibility(View.INVISIBLE); } errorImageView.setImageResource(R.drawable.ic_av_timer_timer_24dp); @@ -2225,8 +2211,8 @@ public class CallController extends BaseController { if (callStateView.getVisibility() != View.VISIBLE) { callStateView.setVisibility(View.VISIBLE); } - if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) { - remoteRenderersLayout.setVisibility(View.INVISIBLE); + if (gridView.getVisibility() != View.INVISIBLE) { + gridView.setVisibility(View.INVISIBLE); } if (progressBar.getVisibility() != View.VISIBLE) { progressBar.setVisibility(View.VISIBLE); @@ -2258,9 +2244,9 @@ public class CallController extends BaseController { } } - if (remoteRenderersLayout != null) { - if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) { - remoteRenderersLayout.setVisibility(View.INVISIBLE); + if (gridView != null) { + if (gridView.getVisibility() != View.INVISIBLE) { + gridView.setVisibility(View.INVISIBLE); } } @@ -2296,9 +2282,9 @@ public class CallController extends BaseController { } } - if (remoteRenderersLayout != null) { - if (remoteRenderersLayout.getVisibility() != View.VISIBLE) { - remoteRenderersLayout.setVisibility(View.VISIBLE); + if (gridView != null) { + if (gridView.getVisibility() != View.VISIBLE) { + gridView.setVisibility(View.VISIBLE); } } @@ -2322,9 +2308,9 @@ public class CallController extends BaseController { } - if (remoteRenderersLayout != null) { - if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) { - remoteRenderersLayout.setVisibility(View.INVISIBLE); + if (gridView != null) { + if (gridView.getVisibility() != View.INVISIBLE) { + gridView.setVisibility(View.INVISIBLE); } } @@ -2349,7 +2335,7 @@ public class CallController extends BaseController { callVoiceOrVideoTextView.setText(getDescriptionForCallType()); callStateTextView.setText(R.string.nc_leaving_call); callStateView.setVisibility(View.VISIBLE); - remoteRenderersLayout.setVisibility(View.INVISIBLE); + gridView.setVisibility(View.INVISIBLE); progressBar.setVisibility(View.VISIBLE); errorImageView.setVisibility(View.GONE); } @@ -2362,7 +2348,7 @@ public class CallController extends BaseController { private String getDescriptionForCallType() { String appName = getResources().getString(R.string.nc_app_name); - if (isVoiceOnlyCall){ + if (isVoiceOnlyCall) { return String.format(getResources().getString(R.string.nc_call_voice), appName); } else { return String.format(getResources().getString(R.string.nc_call_video), appName); @@ -2374,18 +2360,20 @@ public class CallController extends BaseController { Uri ringtoneUri; if (isIncomingCallFromNotification) { ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + - "/raw/librem_by_feandesign_call"); + "/raw/librem_by_feandesign_call"); } else { ringtoneUri = Uri.parse("android.resource://" + getApplicationContext().getPackageName() + "/raw" + - "/tr110_1_kap8_3_freiton1"); + "/tr110_1_kap8_3_freiton1"); } if (getActivity() != null) { mediaPlayer = new MediaPlayer(); try { mediaPlayer.setDataSource(Objects.requireNonNull(getActivity()), ringtoneUri); mediaPlayer.setLooping(true); - AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes - .CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).build(); + AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType( + AudioAttributes.CONTENT_TYPE_SONIFICATION) + .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) + .build(); mediaPlayer.setAudioAttributes(audioAttributes); mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start()); @@ -2438,35 +2426,37 @@ public class CallController extends BaseController { } } - private class VideoClickListener implements View.OnClickListener { - - @Override - public void onClick(View v) { - showCallControls(); + @Subscribe(threadMode = ThreadMode.BACKGROUND) + public void onMessageEvent(NetworkEvent networkEvent) { + if (networkEvent.getNetworkConnectionEvent() + .equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED)) { + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } + } else if (networkEvent.getNetworkConnectionEvent() + .equals(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED)) { + if (handler != null) { + handler.removeCallbacksAndMessages(null); + } } } - @Subscribe(threadMode = ThreadMode.BACKGROUND) - public void onMessageEvent(NetworkEvent networkEvent) { - if (networkEvent.getNetworkConnectionEvent().equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED)) { - if (handler != null) { - handler.removeCallbacksAndMessages(null); + private class SelfVideoTouchListener implements View.OnTouchListener { + + @SuppressLint("ClickableViewAccessibility") + @Override + public boolean onTouch(View view, MotionEvent event) { + long duration = event.getEventTime() - event.getDownTime(); + + if (event.getActionMasked() == MotionEvent.ACTION_MOVE) { + float newY = event.getRawY() - selfVideoView.getHeight() / (float) 2; + float newX = event.getRawX() - selfVideoView.getWidth() / (float) 2; + selfVideoView.setY(newY); + selfVideoView.setX(newX); + } else if (event.getActionMasked() == MotionEvent.ACTION_UP && duration < 100) { + switchCamera(); } - - /*if (!hasMCU) { - setCallState(CallStatus.RECONNECTING); - hangupNetworkCalls(false); - }*/ - - } else if (networkEvent.getNetworkConnectionEvent().equals(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED)) { - if (handler != null) { - handler.removeCallbacksAndMessages(null); - } - - /* if (!hasMCU) { - setCallState(CallStatus.OFFLINE); - hangup(false); - }*/ + return true; } } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java index f8c0815dd..397c084ea 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java @@ -52,9 +52,7 @@ import android.util.TypedValue; import android.view.View; import android.view.ViewGroup; import android.view.Window; -import android.view.WindowManager; import android.widget.EditText; -import android.widget.ImageView; import android.widget.TextView; import androidx.annotation.ColorInt; @@ -63,15 +61,12 @@ import androidx.annotation.DrawableRes; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.annotation.XmlRes; -import androidx.appcompat.app.AppCompatDelegate; import androidx.appcompat.widget.AppCompatDrawableManager; import androidx.appcompat.widget.SearchView; import androidx.core.content.ContextCompat; -import androidx.core.content.res.ResourcesCompat; import androidx.core.graphics.ColorUtils; import androidx.core.graphics.drawable.DrawableCompat; import androidx.emoji.text.EmojiCompat; -import androidx.viewpager.widget.ViewPager; import com.facebook.common.executors.UiThreadImmediateExecutorService; import com.facebook.common.references.CloseableReference; @@ -95,7 +90,6 @@ import com.nextcloud.talk.R; import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.events.UserMentionClickEvent; import com.nextcloud.talk.models.database.UserEntity; -import com.nextcloud.talk.utils.preferences.AppPreferences; import com.nextcloud.talk.utils.text.Spans; import org.greenrobot.eventbus.EventBus; @@ -108,17 +102,6 @@ import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; -import androidx.annotation.ColorInt; -import androidx.annotation.ColorRes; -import androidx.annotation.DrawableRes; -import androidx.annotation.NonNull; -import androidx.annotation.Nullable; -import androidx.annotation.XmlRes; -import androidx.appcompat.widget.AppCompatDrawableManager; -import androidx.core.content.ContextCompat; -import androidx.core.graphics.drawable.DrawableCompat; -import androidx.emoji.text.EmojiCompat; - public class DisplayUtils { private static final String TAG = "DisplayUtils"; @@ -239,6 +222,10 @@ public class DisplayUtils { context.getResources().getDisplayMetrics()) + 0.5f); } + public static float convertPixelToDp(float px, Context context) { + return px / context.getResources().getDisplayMetrics().density; + } + // Solution inspired by https://stackoverflow.com/questions/34936590/why-isnt-my-vector-drawable-scaling-as-expected public static void useCompatVectorIfNeeded() { if (Build.VERSION.SDK_INT < 23) { diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java index 8d1041c83..1d971e7a4 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java +++ b/app/src/main/java/com/nextcloud/talk/webrtc/MagicPeerConnectionWrapper.java @@ -50,6 +50,7 @@ import java.util.List; @AutoInjector(NextcloudTalkApplication.class) public class MagicPeerConnectionWrapper { private static String TAG = "MagicPeerConnectionWrapper"; + private List iceCandidates = new ArrayList<>(); private PeerConnection peerConnection; private String sessionId; diff --git a/app/src/main/res/layout/call_item.xml b/app/src/main/res/layout/call_item.xml index 1701238f2..4cfe5ede0 100644 --- a/app/src/main/res/layout/call_item.xml +++ b/app/src/main/res/layout/call_item.xml @@ -2,7 +2,9 @@ ~ Nextcloud Talk application ~ ~ @author Mario Danic + ~ @author Marcel Hibbe ~ @author Andy Scherzinger + ~ Copyright (C) 2021 Marcel Hibbe ~ Copyright (C) 2021 Andy Scherzinger ~ Copyright (C) 2017 Mario Danic ~ @@ -25,43 +27,8 @@ android:id="@+id/relative_layout" android:layout_width="match_parent" android:layout_height="match_parent" - android:layout_weight="1" - android:orientation="vertical"> - - - - - - + android:orientation="vertical" + android:gravity="center"> + + + + diff --git a/app/src/main/res/layout/controller_call.xml b/app/src/main/res/layout/controller_call.xml index 2ec859973..573f91bdf 100644 --- a/app/src/main/res/layout/controller_call.xml +++ b/app/src/main/res/layout/controller_call.xml @@ -45,13 +45,39 @@ android:layout_weight="1" tools:visibility="visible"> - - + android:gravity="center" + android:stretchMode="columnWidth" + android:numColumns="2" + /> + + + + + + + - - - - - - - + android:layout_marginBottom="16dp"> You: %1$s Message read Message sent - Remote video off Remote audio off Add attachment diff --git a/scripts/analysis/findbugs-results.txt b/scripts/analysis/findbugs-results.txt index d9bf67ee8..9301519f5 100644 --- a/scripts/analysis/findbugs-results.txt +++ b/scripts/analysis/findbugs-results.txt @@ -1 +1 @@ -484 \ No newline at end of file +483 \ No newline at end of file