add grid view for calls. make own video movable

Signed-off-by: Marcel Hibbe <dev@mhibbe.de>
This commit is contained in:
Marcel Hibbe 2021-04-01 16:33:40 +02:00
parent 7a2b908c98
commit b8e4c4da56
No known key found for this signature in database
GPG key ID: C793F8B59F43CE7B
10 changed files with 603 additions and 365 deletions

View file

@ -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)

View file

@ -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 +
'}';
}
}

View file

@ -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<ParticipantDisplayItem> participantDisplayItems;
private final RelativeLayout gridViewWrapper;
private final LinearLayout callInfosLinearLayout;
private final int columns;
private final boolean isVoiceOnlyCall;
public ParticipantsAdapter(Context mContext,
Map<String, ParticipantDisplayItem> 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;
}
}

View file

@ -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<String, ParticipantDisplayItem> 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<HashMap<String, Object>>) 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<ParticipantsOverall>() {
@ -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<SignalingOverall>() {
@ -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;
}
}
}

View file

@ -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) {

View file

@ -50,6 +50,7 @@ import java.util.List;
@AutoInjector(NextcloudTalkApplication.class)
public class MagicPeerConnectionWrapper {
private static String TAG = "MagicPeerConnectionWrapper";
private List<IceCandidate> iceCandidates = new ArrayList<>();
private PeerConnection peerConnection;
private String sessionId;

View file

@ -2,7 +2,9 @@
~ Nextcloud Talk application
~
~ @author Mario Danic
~ @author Marcel Hibbe
~ @author Andy Scherzinger
~ Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~ Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
~
@ -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">
<TextView
android:id="@+id/peer_nick_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentStart="true"
android:layout_alignParentTop="true"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:layout_marginBottom="8dp"
android:textColor="@android:color/white"
android:layout_centerHorizontal="true"/>
<ImageView
android:id="@+id/remote_audio_off"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_below="@id/peer_nick_text_view"
android:layout_alignParentStart="true"
android:layout_marginStart="8dp"
android:layout_marginEnd="8dp"
android:contentDescription="@string/nc_remote_audio_off"
android:src="@drawable/ic_mic_off_white_24px"
android:visibility="invisible" />
<ImageView
android:id="@+id/remote_video_off"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_below="@id/peer_nick_text_view"
android:layout_marginStart="8dp"
android:layout_toEndOf="@id/remote_audio_off"
android:contentDescription="@string/nc_remote_video_off"
android:src="@drawable/ic_videocam_off_white_24px"
android:visibility="invisible" />
android:orientation="vertical"
android:gravity="center">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/avatarImageView"
@ -76,4 +43,25 @@
android:layout_height="match_parent"
android:visibility="invisible" />
<TextView
android:id="@+id/peer_nick_text_view"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentBottom="true"
android:layout_marginBottom="6dp"
android:layout_marginStart="10dp"
android:textColor="@android:color/white" />
<ImageView
android:id="@+id/remote_audio_off"
android:layout_width="16dp"
android:layout_height="16dp"
android:layout_marginBottom="6dp"
android:layout_marginStart="10dp"
android:layout_alignParentBottom="true"
android:layout_toEndOf="@id/peer_nick_text_view"
android:src="@drawable/ic_mic_off_white_24px"
android:contentDescription="@string/nc_remote_audio_off"
android:visibility="invisible" />
</RelativeLayout>

View file

@ -45,13 +45,39 @@
android:layout_weight="1"
tools:visibility="visible">
<LinearLayout
android:id="@+id/remote_renderers_layout"
<GridView
android:id="@+id/gridview"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:animateLayoutChanges="true"
android:orientation="vertical">
</LinearLayout>
android:gravity="center"
android:stretchMode="columnWidth"
android:numColumns="2"
/>
<FrameLayout
android:id="@+id/selfVideoView"
android:layout_width="wrap_content"
android:layout_height="wrap_content">
<org.webrtc.SurfaceViewRenderer
android:id="@+id/pip_video_view"
android:layout_width="@dimen/large_preview_dimension"
android:layout_height="150dp"
android:layout_gravity="center"
android:layout_margin="16dp"
android:visibility="invisible"
android:clickable="false"
tools:visibility="visible" />
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/call_control_switch_camera"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="20dp"
app:placeholderImage="@drawable/ic_switch_video_white_24px"
app:roundAsCircle="true" />
</FrameLayout>
<LinearLayout
android:id="@+id/callInfosLinearLayout"
@ -83,35 +109,11 @@
android:ellipsize="marquee"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="28sp"
android:textSize="22sp"
android:textStyle="bold"
tools:text="Marsellus Wallace" />
</LinearLayout>
<FrameLayout
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:layout_alignParentEnd="true"
android:layout_below="@id/callInfosLinearLayout">
<org.webrtc.SurfaceViewRenderer
android:id="@+id/pip_video_view"
android:layout_width="@dimen/large_preview_dimension"
android:layout_height="150dp"
android:layout_gravity="center"
android:layout_margin="16dp"
android:visibility="invisible"
tools:visibility="visible"/>
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/call_control_switch_camera"
android:layout_width="40dp"
android:layout_height="40dp"
android:layout_gravity="center_horizontal|bottom"
android:layout_marginBottom="20dp"
app:placeholderImage="@drawable/ic_switch_video_white_24px"
app:roundAsCircle="true" />
</FrameLayout>
<View android:id="@+id/verticalCenter"
android:layout_width="0dp"
android:layout_height="0dp"
@ -137,7 +139,7 @@
android:background="@android:color/transparent"
android:gravity="center"
android:layout_alignBottom="@id/linearWrapperLayout"
android:layout_marginBottom="10dp">
android:layout_marginBottom="16dp">
<com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/callControlToggleChat"

View file

@ -101,7 +101,8 @@
android:ellipsize="marquee"
android:textAlignment="center"
android:textColor="@color/white"
android:textSize="28sp"
android:textSize="22sp"
android:textStyle="bold"
tools:text="Victor Gregorius Magnus" />
<TextView

View file

@ -285,7 +285,6 @@
<string name="nc_formatted_message_you">You: %1$s</string>
<string name="nc_message_read">Message read</string>
<string name="nc_message_sent">Message sent</string>
<string name="nc_remote_video_off">Remote video off</string>
<string name="nc_remote_audio_off">Remote audio off</string>
<string name="nc_add_attachment">Add attachment</string>