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) - 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 - other data types are opened with external apps if they are able to handle it
- edit profile information and privacy settings - edit profile information and privacy settings
- add grid view for calls, make own video movable
### Changed ### Changed
- improve conversation list design and dark/light theming (@AndyScherzinger) - 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.Handler;
import android.os.Looper; import android.os.Looper;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.DisplayMetrics;
import android.util.Log; import android.util.Log;
import android.view.LayoutInflater; import android.view.LayoutInflater;
import android.view.MotionEvent; import android.view.MotionEvent;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.ViewTreeObserver;
import android.widget.FrameLayout; import android.widget.FrameLayout;
import android.widget.GridView;
import android.widget.ImageView; import android.widget.ImageView;
import android.widget.LinearLayout; import android.widget.LinearLayout;
import android.widget.ProgressBar; import android.widget.ProgressBar;
@ -51,11 +54,11 @@ import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity; import androidx.appcompat.app.AppCompatActivity;
import com.bluelinelabs.logansquare.LoganSquare; 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.facebook.drawee.view.SimpleDraweeView;
import com.nextcloud.talk.R; import com.nextcloud.talk.R;
import com.nextcloud.talk.activities.MagicCallActivity; 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.api.NcApi;
import com.nextcloud.talk.application.NextcloudTalkApplication; import com.nextcloud.talk.application.NextcloudTalkApplication;
import com.nextcloud.talk.controllers.base.BaseController; import com.nextcloud.talk.controllers.base.BaseController;
@ -143,6 +146,7 @@ import javax.inject.Inject;
import autodagger.AutoInjector; import autodagger.AutoInjector;
import butterknife.BindView; import butterknife.BindView;
import butterknife.OnClick; import butterknife.OnClick;
import butterknife.OnItemClick;
import butterknife.OnLongClick; import butterknife.OnLongClick;
import io.reactivex.Observable; import io.reactivex.Observable;
import io.reactivex.Observer; import io.reactivex.Observer;
@ -180,8 +184,8 @@ public class CallController extends BaseController {
SurfaceViewRenderer pipVideoView; SurfaceViewRenderer pipVideoView;
@BindView(R.id.controllerCallLayout) @BindView(R.id.controllerCallLayout)
RelativeLayout controllerCallLayout; RelativeLayout controllerCallLayout;
@BindView(R.id.remote_renderers_layout) @BindView(R.id.gridview)
LinearLayout remoteRenderersLayout; GridView gridView;
@BindView(R.id.callControlsLinearLayout) @BindView(R.id.callControlsLinearLayout)
LinearLayout callControls; LinearLayout callControls;
@ -201,6 +205,9 @@ public class CallController extends BaseController {
@BindView(R.id.callConversationNameTextView) @BindView(R.id.callConversationNameTextView)
TextView callConversationNameTextView; TextView callConversationNameTextView;
@BindView(R.id.selfVideoView)
FrameLayout selfVideoView;
@BindView(R.id.callStateRelativeLayoutView) @BindView(R.id.callStateRelativeLayoutView)
RelativeLayout callStateView; RelativeLayout callStateView;
@ -264,7 +271,6 @@ public class CallController extends BaseController {
// push to talk // push to talk
private boolean isPTTActive = false; private boolean isPTTActive = false;
private PulseAnimation pulseAnimation; private PulseAnimation pulseAnimation;
private View.OnClickListener videoOnClickListener;
private String baseUrl; private String baseUrl;
private String roomId; private String roomId;
@ -286,6 +292,9 @@ public class CallController extends BaseController {
private MediaPlayer mediaPlayer; private MediaPlayer mediaPlayer;
private Map<String, ParticipantDisplayItem> participantDisplayItems;
private ParticipantsAdapter participantsAdapter;
@Parcel @Parcel
public enum CallStatus { public enum CallStatus {
CONNECTING, CALLING_TIMEOUT, JOINED, IN_CONVERSATION, RECONNECTING, OFFLINE, LEAVING, PUBLISHER_FAILED CONNECTING, CALLING_TIMEOUT, JOINED, IN_CONVERSATION, RECONNECTING, OFFLINE, LEAVING, PUBLISHER_FAILED
@ -345,19 +354,18 @@ public class CallController extends BaseController {
} }
} }
@SuppressLint("ClickableViewAccessibility")
@Override @Override
protected void onViewBound(@NonNull View view) { protected void onViewBound(@NonNull View view) {
super.onViewBound(view); super.onViewBound(view);
microphoneControlButton.setOnTouchListener(new MicrophoneButtonTouchListener()); microphoneControlButton.setOnTouchListener(new MicrophoneButtonTouchListener());
videoOnClickListener = new VideoClickListener();
pulseAnimation = PulseAnimation.create().with(microphoneControlButton) pulseAnimation = PulseAnimation.create().with(microphoneControlButton)
.setDuration(310) .setDuration(310)
.setRepeatCount(PulseAnimation.INFINITE) .setRepeatCount(PulseAnimation.INFINITE)
.setRepeatMode(PulseAnimation.REVERSE); .setRepeatMode(PulseAnimation.REVERSE);
setPipVideoViewDimensions();
try { try {
cache.evictAll(); cache.evictAll();
@ -368,7 +376,7 @@ public class CallController extends BaseController {
callControls.setZ(100.0f); callControls.setZ(100.0f);
basicInitialization(); basicInitialization();
initViews(); initViews();
initPipView();
initiateCall(); initiateCall();
} }
@ -467,7 +475,11 @@ public class CallController extends BaseController {
}); });
} }
@SuppressLint("ClickableViewAccessibility")
private void initViews() { private void initViews() {
participantDisplayItems = new HashMap<>();
if (isVoiceOnlyCall) { if (isVoiceOnlyCall) {
callControlEnableSpeaker.setVisibility(View.VISIBLE); callControlEnableSpeaker.setVisibility(View.VISIBLE);
cameraSwitchButton.setVisibility(View.GONE); 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); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT, ViewGroup.LayoutParams.WRAP_CONTENT);
params.addRule(RelativeLayout.BELOW, R.id.callInfosLinearLayout); params.addRule(RelativeLayout.BELOW, R.id.callInfosLinearLayout);
remoteRenderersLayout.setLayoutParams(params); gridView.setLayoutParams(params);
} else { } else {
callControlEnableSpeaker.setVisibility(View.GONE); callControlEnableSpeaker.setVisibility(View.GONE);
if (cameraEnumerator.getDeviceNames().length < 2) { if (cameraEnumerator.getDeviceNames().length < 2) {
@ -488,7 +500,77 @@ public class CallController extends BaseController {
// disabled because it causes some devices to crash // disabled because it causes some devices to crash
pipVideoView.setEnableHardwareScaler(false); pipVideoView.setEnableHardwareScaler(false);
pipVideoView.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); 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);
} }
@ -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() { public void switchCamera() {
CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer; CameraVideoCapturer cameraVideoCapturer = (CameraVideoCapturer) videoCapturer;
if (cameraVideoCapturer != null) { if (cameraVideoCapturer != null) {
@ -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() { public void showCallControls() {
animateCallControls(true, 0); animateCallControls(true, 0);
} }
@ -1417,7 +1499,7 @@ public class CallController extends BaseController {
NCSignalingMessage.class); NCSignalingMessage.class);
processMessage(ncSignalingMessage); processMessage(ncSignalingMessage);
} else { } else {
Log.d(TAG, "Something went very very wrong"); Log.e(TAG, "unexpected message type when receiving signaling message");
} }
} }
@ -1471,7 +1553,7 @@ public class CallController extends BaseController {
} }
} }
} else { } 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() { private void getPeersForCall() {
Log.d(TAG, "getPeersForCall");
ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken)) ncApi.getPeersForCall(credentials, ApiUtils.getUrlForCall(baseUrl, roomToken))
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.subscribe(new Observer<ParticipantsOverall>() { .subscribe(new Observer<ParticipantsOverall>() {
@ -1671,15 +1754,12 @@ public class CallController extends BaseController {
participantMap = new HashMap<>(); participantMap = new HashMap<>();
for (Participant participant : participantsOverall.getOcs().getData()) { for (Participant participant : participantsOverall.getOcs().getData()) {
participantMap.put(participant.getSessionId(), participant); participantMap.put(participant.getSessionId(), participant);
if (getActivity() != null) {
getActivity().runOnUiThread(() -> setupAvatarForSession(participant.getSessionId()));
}
} }
} }
@Override @Override
public void onError(Throwable e) { public void onError(Throwable e) {
Log.e(TAG, "error while executing getPeersForCall", e);
} }
@Override @Override
@ -1756,9 +1836,10 @@ public class CallController extends BaseController {
magicPeerConnectionWrapper = magicPeerConnectionWrappers.get(i); magicPeerConnectionWrapper = magicPeerConnectionWrappers.get(i);
if (magicPeerConnectionWrapper.getSessionId().equals(sessionId)) { if (magicPeerConnectionWrapper.getSessionId().equals(sessionId)) {
if (magicPeerConnectionWrapper.getVideoStreamType().equals("screen") || !justScreen) { 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); deleteMagicPeerConnection(magicPeerConnectionWrapper);
} }
} }
@ -1767,15 +1848,9 @@ public class CallController extends BaseController {
} }
private void removeMediaStream(String sessionId) { private void removeMediaStream(String sessionId) {
if (remoteRenderersLayout != null && remoteRenderersLayout.getChildCount() > 0) { Log.d(TAG, "removeMediaStream");
RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(sessionId); participantDisplayItems.remove(sessionId);
if (relativeLayout != null) { initGridAdapter();
SurfaceViewRenderer surfaceViewRenderer = relativeLayout.findViewById(R.id.surface_view);
surfaceViewRenderer.release();
remoteRenderersLayout.removeView(relativeLayout);
remoteRenderersLayout.invalidate();
}
}
if (callControls != null) { if (callControls != null) {
callControls.setZ(100.0f); callControls.setZ(100.0f);
@ -1785,38 +1860,50 @@ public class CallController extends BaseController {
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(ConfigurationChangeEvent configurationChangeEvent) { public void onMessageEvent(ConfigurationChangeEvent configurationChangeEvent) {
powerManagerUtils.setOrientation(Objects.requireNonNull(getResources()).getConfiguration().orientation); powerManagerUtils.setOrientation(Objects.requireNonNull(getResources()).getConfiguration().orientation);
initGridAdapter();
initPipView();
if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
remoteRenderersLayout.setOrientation(LinearLayout.HORIZONTAL);
} else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
remoteRenderersLayout.setOrientation(LinearLayout.VERTICAL);
} }
setPipVideoViewDimensions(); private void initPipView() {
}
private void setPipVideoViewDimensions() {
FrameLayout.LayoutParams layoutParams = (FrameLayout.LayoutParams) pipVideoView.getLayoutParams(); 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) { if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_LANDSCAPE) {
remoteRenderersLayout.setOrientation(LinearLayout.HORIZONTAL);
layoutParams.height = (int) getResources().getDimension(R.dimen.large_preview_dimension); layoutParams.height = (int) getResources().getDimension(R.dimen.large_preview_dimension);
layoutParams.width = FrameLayout.LayoutParams.WRAP_CONTENT; 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) { } else if (getResources().getConfiguration().orientation == Configuration.ORIENTATION_PORTRAIT) {
remoteRenderersLayout.setOrientation(LinearLayout.VERTICAL);
layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT; layoutParams.height = FrameLayout.LayoutParams.WRAP_CONTENT;
layoutParams.width = (int) getResources().getDimension(R.dimen.large_preview_dimension); 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) @Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(PeerConnectionEvent peerConnectionEvent) { public void onMessageEvent(PeerConnectionEvent peerConnectionEvent) {
String sessionId = peerConnectionEvent.getSessionId();
if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType
.PEER_CLOSED)) { .PEER_CLOSED)) {
endPeerConnection(peerConnectionEvent.getSessionId(), peerConnectionEvent.getVideoStreamType().equals("screen")); endPeerConnection(sessionId, peerConnectionEvent.getVideoStreamType().equals("screen"));
} else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent
.PeerConnectionEventType.SENSOR_FAR) || .PeerConnectionEventType.SENSOR_FAR) ||
peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent
@ -1831,17 +1918,18 @@ public class CallController extends BaseController {
toggleMedia(enableVideo, true); toggleMedia(enableVideo, true);
} }
} }
} else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.NICK_CHANGE)) {
.PeerConnectionEventType.NICK_CHANGE)) { participantDisplayItems.get(sessionId).setNick(peerConnectionEvent.getNick());
gotNick(peerConnectionEvent.getSessionId(), peerConnectionEvent.getNick(), peerConnectionEvent.getVideoStreamType()); participantsAdapter.notifyDataSetChanged();
} else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent
.PeerConnectionEventType.VIDEO_CHANGE) && !isVoiceOnlyCall) { } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.VIDEO_CHANGE) && !isVoiceOnlyCall) {
gotAudioOrVideoChange(true, peerConnectionEvent.getSessionId() + "+" + peerConnectionEvent.getVideoStreamType(), participantDisplayItems.get(sessionId).setStreamEnabled(peerConnectionEvent.getChangeValue());
peerConnectionEvent.getChangeValue()); participantsAdapter.notifyDataSetChanged();
} else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent
.PeerConnectionEventType.AUDIO_CHANGE)) { } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.AUDIO_CHANGE)) {
gotAudioOrVideoChange(false, peerConnectionEvent.getSessionId() + "+" + peerConnectionEvent.getVideoStreamType(), participantDisplayItems.get(sessionId).setAudioEnabled(peerConnectionEvent.getChangeValue());
peerConnectionEvent.getChangeValue()); participantsAdapter.notifyDataSetChanged();
} else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED)) { } else if (peerConnectionEvent.getPeerConnectionEventType().equals(PeerConnectionEvent.PeerConnectionEventType.PUBLISHER_FAILED)) {
currentCallStatus = CallStatus.PUBLISHER_FAILED; currentCallStatus = CallStatus.PUBLISHER_FAILED;
webSocketClient.clearResumeId(); webSocketClient.clearResumeId();
@ -1894,11 +1982,20 @@ public class CallController extends BaseController {
@Subscribe(threadMode = ThreadMode.MAIN) @Subscribe(threadMode = ThreadMode.MAIN)
public void onMessageEvent(MediaStreamEvent mediaStreamEvent) { public void onMessageEvent(MediaStreamEvent mediaStreamEvent) {
if (mediaStreamEvent.getMediaStream() != null) { if (mediaStreamEvent.getMediaStream() != null) {
setupVideoStreamForLayout(mediaStreamEvent.getMediaStream(), mediaStreamEvent.getSession(), boolean hasAtLeastOneVideoStream = mediaStreamEvent.getMediaStream().videoTracks != null
mediaStreamEvent.getMediaStream().videoTracks != null && mediaStreamEvent.getMediaStream().videoTracks.size() > 0;
&& mediaStreamEvent.getMediaStream().videoTracks.size() > 0, mediaStreamEvent.getVideoStreamType());
setupVideoStreamForLayout(
mediaStreamEvent.getMediaStream(),
mediaStreamEvent.getSession(),
hasAtLeastOneVideoStream,
mediaStreamEvent.getVideoStreamType());
} else { } else {
setupVideoStreamForLayout(null, mediaStreamEvent.getSession(), false, mediaStreamEvent.getVideoStreamType()); setupVideoStreamForLayout(
null,
mediaStreamEvent.getSession(),
false,
mediaStreamEvent.getVideoStreamType());
} }
} }
@ -1965,7 +2062,7 @@ public class CallController extends BaseController {
try { try {
receivedSignalingMessage(signalingOverall.getOcs().getSignalings().get(i)); receivedSignalingMessage(signalingOverall.getOcs().getSignalings().get(i));
} catch (IOException e) { } catch (IOException e) {
e.printStackTrace(); Log.e(TAG, "", e);
} }
} }
} }
@ -1973,6 +2070,7 @@ public class CallController extends BaseController {
@Override @Override
public void onError(Throwable e) { public void onError(Throwable e) {
Log.e(TAG, "", e);
} }
@Override @Override
@ -1994,27 +2092,20 @@ public class CallController extends BaseController {
this); this);
} }
private void setupAvatarForSession(String session) { private void setupVideoStreamForLayout(@Nullable MediaStream mediaStream, String session, boolean videoStreamEnabled, String videoStreamType) {
if (remoteRenderersLayout != null) { String nick;
RelativeLayout relativeLayout = remoteRenderersLayout.findViewWithTag(session + "+video"); if (hasExternalSignalingServer) {
if (relativeLayout != null) { nick = webSocketClient.getDisplayNameForSession(session);
SimpleDraweeView avatarImageView = relativeLayout.findViewById(R.id.avatarImageView);
String userId;
String displayName;
if (hasMCU) {
userId = webSocketClient.getUserIdForSession(session);
displayName = getPeerConnectionWrapperForSessionIdAndType(session, "video", false).getNick();
} else { } else {
userId = participantMap.get(session).getUserId(); nick = getPeerConnectionWrapperForSessionIdAndType(session, videoStreamType, false).getNick();
displayName = getPeerConnectionWrapperForSessionIdAndType(session, "video", false).getNick();
} }
if (!TextUtils.isEmpty(userId) || !TextUtils.isEmpty(displayName)) { String userId;
if (hasMCU) {
if (getActivity() != null) { userId = webSocketClient.getUserIdForSession(session);
avatarImageView.setController(null); } else {
userId = participantMap.get(session).getUserId();
}
String urlForAvatar; String urlForAvatar;
if (!TextUtils.isEmpty(userId)) { if (!TextUtils.isEmpty(userId)) {
@ -2023,129 +2114,24 @@ public class CallController extends BaseController {
R.dimen.avatar_size_big); R.dimen.avatar_size_big);
} else { } else {
urlForAvatar = ApiUtils.getUrlForAvatarWithNameForGuests(baseUrl, urlForAvatar = ApiUtils.getUrlForAvatarWithNameForGuests(baseUrl,
displayName, nick,
R.dimen.avatar_size_big); R.dimen.avatar_size_big);
} }
DraweeController draweeController = Fresco.newDraweeControllerBuilder() ParticipantDisplayItem participantDisplayItem = new ParticipantDisplayItem(userId,
.setOldController(avatarImageView.getController()) session,
.setImageRequest(DisplayUtils.getImageRequestForUrl(urlForAvatar, null)) nick,
.build(); urlForAvatar,
avatarImageView.setController(draweeController); mediaStream,
} videoStreamType,
} videoStreamEnabled,
} rootEglBase);
} participantDisplayItems.put(session, participantDisplayItem);
}
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);
} else {
imageView.setVisibility(View.VISIBLE);
surfaceViewRenderer.setVisibility(View.INVISIBLE);
if (isInitialLayoutSetupForPeer && isVoiceOnlyCall) {
gotAudioOrVideoChange(true, session, false);
}
}
initGridAdapter();
callControls.setZ(100.0f); 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) @OnClick(R.id.callStateRelativeLayoutView)
public void onConnectingViewClick() { public void onConnectingViewClick() {
if (currentCallStatus.equals(CallStatus.CALLING_TIMEOUT)) { if (currentCallStatus.equals(CallStatus.CALLING_TIMEOUT)) {
@ -2180,8 +2166,8 @@ public class CallController extends BaseController {
callStateView.setVisibility(View.VISIBLE); callStateView.setVisibility(View.VISIBLE);
} }
if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) { if (gridView.getVisibility() != View.INVISIBLE) {
remoteRenderersLayout.setVisibility(View.INVISIBLE); gridView.setVisibility(View.INVISIBLE);
} }
if (progressBar.getVisibility() != View.VISIBLE) { if (progressBar.getVisibility() != View.VISIBLE) {
@ -2206,8 +2192,8 @@ public class CallController extends BaseController {
progressBar.setVisibility(View.GONE); progressBar.setVisibility(View.GONE);
} }
if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) { if (gridView.getVisibility() != View.INVISIBLE) {
remoteRenderersLayout.setVisibility(View.INVISIBLE); gridView.setVisibility(View.INVISIBLE);
} }
errorImageView.setImageResource(R.drawable.ic_av_timer_timer_24dp); errorImageView.setImageResource(R.drawable.ic_av_timer_timer_24dp);
@ -2225,8 +2211,8 @@ public class CallController extends BaseController {
if (callStateView.getVisibility() != View.VISIBLE) { if (callStateView.getVisibility() != View.VISIBLE) {
callStateView.setVisibility(View.VISIBLE); callStateView.setVisibility(View.VISIBLE);
} }
if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) { if (gridView.getVisibility() != View.INVISIBLE) {
remoteRenderersLayout.setVisibility(View.INVISIBLE); gridView.setVisibility(View.INVISIBLE);
} }
if (progressBar.getVisibility() != View.VISIBLE) { if (progressBar.getVisibility() != View.VISIBLE) {
progressBar.setVisibility(View.VISIBLE); progressBar.setVisibility(View.VISIBLE);
@ -2258,9 +2244,9 @@ public class CallController extends BaseController {
} }
} }
if (remoteRenderersLayout != null) { if (gridView != null) {
if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) { if (gridView.getVisibility() != View.INVISIBLE) {
remoteRenderersLayout.setVisibility(View.INVISIBLE); gridView.setVisibility(View.INVISIBLE);
} }
} }
@ -2296,9 +2282,9 @@ public class CallController extends BaseController {
} }
} }
if (remoteRenderersLayout != null) { if (gridView != null) {
if (remoteRenderersLayout.getVisibility() != View.VISIBLE) { if (gridView.getVisibility() != View.VISIBLE) {
remoteRenderersLayout.setVisibility(View.VISIBLE); gridView.setVisibility(View.VISIBLE);
} }
} }
@ -2322,9 +2308,9 @@ public class CallController extends BaseController {
} }
if (remoteRenderersLayout != null) { if (gridView != null) {
if (remoteRenderersLayout.getVisibility() != View.INVISIBLE) { if (gridView.getVisibility() != View.INVISIBLE) {
remoteRenderersLayout.setVisibility(View.INVISIBLE); gridView.setVisibility(View.INVISIBLE);
} }
} }
@ -2349,7 +2335,7 @@ public class CallController extends BaseController {
callVoiceOrVideoTextView.setText(getDescriptionForCallType()); callVoiceOrVideoTextView.setText(getDescriptionForCallType());
callStateTextView.setText(R.string.nc_leaving_call); callStateTextView.setText(R.string.nc_leaving_call);
callStateView.setVisibility(View.VISIBLE); callStateView.setVisibility(View.VISIBLE);
remoteRenderersLayout.setVisibility(View.INVISIBLE); gridView.setVisibility(View.INVISIBLE);
progressBar.setVisibility(View.VISIBLE); progressBar.setVisibility(View.VISIBLE);
errorImageView.setVisibility(View.GONE); errorImageView.setVisibility(View.GONE);
} }
@ -2362,7 +2348,7 @@ public class CallController extends BaseController {
private String getDescriptionForCallType() { private String getDescriptionForCallType() {
String appName = getResources().getString(R.string.nc_app_name); String appName = getResources().getString(R.string.nc_app_name);
if (isVoiceOnlyCall){ if (isVoiceOnlyCall) {
return String.format(getResources().getString(R.string.nc_call_voice), appName); return String.format(getResources().getString(R.string.nc_call_voice), appName);
} else { } else {
return String.format(getResources().getString(R.string.nc_call_video), appName); return String.format(getResources().getString(R.string.nc_call_video), appName);
@ -2384,8 +2370,10 @@ public class CallController extends BaseController {
try { try {
mediaPlayer.setDataSource(Objects.requireNonNull(getActivity()), ringtoneUri); mediaPlayer.setDataSource(Objects.requireNonNull(getActivity()), ringtoneUri);
mediaPlayer.setLooping(true); mediaPlayer.setLooping(true);
AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(AudioAttributes AudioAttributes audioAttributes = new AudioAttributes.Builder().setContentType(
.CONTENT_TYPE_SONIFICATION).setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION).build(); AudioAttributes.CONTENT_TYPE_SONIFICATION)
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION)
.build();
mediaPlayer.setAudioAttributes(audioAttributes); mediaPlayer.setAudioAttributes(audioAttributes);
mediaPlayer.setOnPreparedListener(mp -> mediaPlayer.start()); 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) @Subscribe(threadMode = ThreadMode.BACKGROUND)
public void onMessageEvent(NetworkEvent networkEvent) { public void onMessageEvent(NetworkEvent networkEvent) {
if (networkEvent.getNetworkConnectionEvent().equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED)) { if (networkEvent.getNetworkConnectionEvent()
.equals(NetworkEvent.NetworkConnectionEvent.NETWORK_CONNECTED)) {
if (handler != null) { if (handler != null) {
handler.removeCallbacksAndMessages(null); handler.removeCallbacksAndMessages(null);
} }
} else if (networkEvent.getNetworkConnectionEvent()
/*if (!hasMCU) { .equals(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED)) {
setCallState(CallStatus.RECONNECTING);
hangupNetworkCalls(false);
}*/
} else if (networkEvent.getNetworkConnectionEvent().equals(NetworkEvent.NetworkConnectionEvent.NETWORK_DISCONNECTED)) {
if (handler != null) { if (handler != null) {
handler.removeCallbacksAndMessages(null); handler.removeCallbacksAndMessages(null);
} }
}
}
/* if (!hasMCU) { private class SelfVideoTouchListener implements View.OnTouchListener {
setCallState(CallStatus.OFFLINE);
hangup(false); @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();
}
return true;
} }
} }
} }

View file

@ -52,9 +52,7 @@ import android.util.TypedValue;
import android.view.View; import android.view.View;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.Window; import android.view.Window;
import android.view.WindowManager;
import android.widget.EditText; import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView; import android.widget.TextView;
import androidx.annotation.ColorInt; import androidx.annotation.ColorInt;
@ -63,15 +61,12 @@ import androidx.annotation.DrawableRes;
import androidx.annotation.NonNull; import androidx.annotation.NonNull;
import androidx.annotation.Nullable; import androidx.annotation.Nullable;
import androidx.annotation.XmlRes; import androidx.annotation.XmlRes;
import androidx.appcompat.app.AppCompatDelegate;
import androidx.appcompat.widget.AppCompatDrawableManager; import androidx.appcompat.widget.AppCompatDrawableManager;
import androidx.appcompat.widget.SearchView; import androidx.appcompat.widget.SearchView;
import androidx.core.content.ContextCompat; import androidx.core.content.ContextCompat;
import androidx.core.content.res.ResourcesCompat;
import androidx.core.graphics.ColorUtils; import androidx.core.graphics.ColorUtils;
import androidx.core.graphics.drawable.DrawableCompat; import androidx.core.graphics.drawable.DrawableCompat;
import androidx.emoji.text.EmojiCompat; import androidx.emoji.text.EmojiCompat;
import androidx.viewpager.widget.ViewPager;
import com.facebook.common.executors.UiThreadImmediateExecutorService; import com.facebook.common.executors.UiThreadImmediateExecutorService;
import com.facebook.common.references.CloseableReference; 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.application.NextcloudTalkApplication;
import com.nextcloud.talk.events.UserMentionClickEvent; import com.nextcloud.talk.events.UserMentionClickEvent;
import com.nextcloud.talk.models.database.UserEntity; import com.nextcloud.talk.models.database.UserEntity;
import com.nextcloud.talk.utils.preferences.AppPreferences;
import com.nextcloud.talk.utils.text.Spans; import com.nextcloud.talk.utils.text.Spans;
import org.greenrobot.eventbus.EventBus; import org.greenrobot.eventbus.EventBus;
@ -108,17 +102,6 @@ import java.util.Map;
import java.util.regex.Matcher; import java.util.regex.Matcher;
import java.util.regex.Pattern; 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 { public class DisplayUtils {
private static final String TAG = "DisplayUtils"; private static final String TAG = "DisplayUtils";
@ -239,6 +222,10 @@ public class DisplayUtils {
context.getResources().getDisplayMetrics()) + 0.5f); 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 // Solution inspired by https://stackoverflow.com/questions/34936590/why-isnt-my-vector-drawable-scaling-as-expected
public static void useCompatVectorIfNeeded() { public static void useCompatVectorIfNeeded() {
if (Build.VERSION.SDK_INT < 23) { if (Build.VERSION.SDK_INT < 23) {

View file

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

View file

@ -2,7 +2,9 @@
~ Nextcloud Talk application ~ Nextcloud Talk application
~ ~
~ @author Mario Danic ~ @author Mario Danic
~ @author Marcel Hibbe
~ @author Andy Scherzinger ~ @author Andy Scherzinger
~ Copyright (C) 2021 Marcel Hibbe <dev@mhibbe.de>
~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de> ~ Copyright (C) 2021 Andy Scherzinger <info@andy-scherzinger.de>
~ Copyright (C) 2017 Mario Danic <mario@lovelyhq.com> ~ Copyright (C) 2017 Mario Danic <mario@lovelyhq.com>
~ ~
@ -25,43 +27,8 @@
android:id="@+id/relative_layout" android:id="@+id/relative_layout"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:layout_weight="1" android:orientation="vertical"
android:orientation="vertical"> android:gravity="center">
<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" />
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/avatarImageView" android:id="@+id/avatarImageView"
@ -76,4 +43,25 @@
android:layout_height="match_parent" android:layout_height="match_parent"
android:visibility="invisible" /> 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> </RelativeLayout>

View file

@ -45,13 +45,39 @@
android:layout_weight="1" android:layout_weight="1"
tools:visibility="visible"> tools:visibility="visible">
<LinearLayout <GridView
android:id="@+id/remote_renderers_layout" android:id="@+id/gridview"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="match_parent" android:layout_height="match_parent"
android:animateLayoutChanges="true" android:gravity="center"
android:orientation="vertical"> android:stretchMode="columnWidth"
</LinearLayout> 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 <LinearLayout
android:id="@+id/callInfosLinearLayout" android:id="@+id/callInfosLinearLayout"
@ -83,35 +109,11 @@
android:ellipsize="marquee" android:ellipsize="marquee"
android:textAlignment="center" android:textAlignment="center"
android:textColor="@color/white" android:textColor="@color/white"
android:textSize="28sp" android:textSize="22sp"
android:textStyle="bold"
tools:text="Marsellus Wallace" /> tools:text="Marsellus Wallace" />
</LinearLayout> </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" <View android:id="@+id/verticalCenter"
android:layout_width="0dp" android:layout_width="0dp"
android:layout_height="0dp" android:layout_height="0dp"
@ -137,7 +139,7 @@
android:background="@android:color/transparent" android:background="@android:color/transparent"
android:gravity="center" android:gravity="center"
android:layout_alignBottom="@id/linearWrapperLayout" android:layout_alignBottom="@id/linearWrapperLayout"
android:layout_marginBottom="10dp"> android:layout_marginBottom="16dp">
<com.facebook.drawee.view.SimpleDraweeView <com.facebook.drawee.view.SimpleDraweeView
android:id="@+id/callControlToggleChat" android:id="@+id/callControlToggleChat"

View file

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

View file

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