package com.quickblox.sample.groupchatwebrtc.fragments; import android.content.Context; import android.graphics.Color; import android.graphics.Rect; import android.os.Bundle; import android.os.SystemClock; import android.support.annotation.DimenRes; import android.support.annotation.NonNull; import android.support.v4.content.ContextCompat; import android.support.v7.app.AppCompatActivity; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.util.DisplayMetrics; import android.util.Log; import android.util.SparseArray; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuInflater; import android.view.MenuItem; import android.view.View; import android.view.ViewGroup; import android.view.ViewTreeObserver; import android.widget.Chronometer; import android.widget.CompoundButton; import android.widget.LinearLayout; import android.widget.RelativeLayout; import android.widget.TextView; import android.widget.ToggleButton; import com.quickblox.sample.core.utils.Toaster; import com.quickblox.sample.groupchatwebrtc.R; import com.quickblox.sample.groupchatwebrtc.activities.CallActivity; import com.quickblox.sample.groupchatwebrtc.adapters.OpponentsFromCallAdapter; import com.quickblox.users.model.QBUser; import com.quickblox.videochat.webrtc.QBRTCSession; import com.quickblox.videochat.webrtc.QBRTCTypes; import com.quickblox.videochat.webrtc.callbacks.QBRTCClientVideoTracksCallbacks; import com.quickblox.videochat.webrtc.callbacks.QBRTCSessionEventsCallback; import com.quickblox.videochat.webrtc.callbacks.QBRTCSessionStateCallback; import com.quickblox.videochat.webrtc.view.QBRTCSurfaceView; import com.quickblox.videochat.webrtc.view.QBRTCVideoTrack; import org.webrtc.CameraVideoCapturer; import org.webrtc.RendererCommon; import org.webrtc.SurfaceViewRenderer; import org.webrtc.VideoRenderer; import java.io.Serializable; import java.util.ArrayList; import java.util.Collections; import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map; import static android.support.v7.widget.LinearLayoutManager.HORIZONTAL; /** * QuickBlox team */ public class VideoConversationFragment extends BaseConversationFragment implements Serializable, QBRTCClientVideoTracksCallbacks, QBRTCSessionStateCallback, QBRTCSessionEventsCallback, OpponentsFromCallAdapter.OnAdapterEventListener { private static final int DEFAULT_ROWS_COUNT = 2; private static final int DEFAULT_COLS_COUNT = 3; private static final long TOGGLE_CAMERA_DELAY = 1000; private static final long LOCAL_TRACk_INITIALIZE_DELAY = 500; private static final int RECYCLE_VIEW_PADDING = 2; private static final long UPDATING_USERS_DELAY = 2000; private static final long FULL_SCREEN_CLICK_DELAY = 1000; private static final int REQUEST_CODE_ATTACHMENT = 100; private String TAG = VideoConversationFragment.class.getSimpleName(); private ToggleButton cameraToggle; private View view; private LinearLayout actionVideoButtonsLayout; private QBRTCSurfaceView remoteFullScreenVideoView; private QBRTCSurfaceView localVideoView; private CameraState cameraState = CameraState.DISABLED_FROM_USER; private RecyclerView recyclerView; private SparseArray<OpponentsFromCallAdapter.ViewHolder> opponentViewHolders; private boolean isPeerToPeerCall; private QBRTCVideoTrack localVideoTrack; private TextView connectionStatusLocal; private Map<Integer, QBRTCVideoTrack> videoTrackMap; private OpponentsFromCallAdapter opponentsAdapter; private LocalViewOnClickListener localViewOnClickListener; private boolean isRemoteShown; private int amountOpponents; private int userIDFullScreen; private List<QBUser> allOpponents; private boolean connectionEstablished; private boolean allCallbacksInit; private boolean isCurrentCameraFront; private boolean isLocalVideoFullScreen; @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { view = super.onCreateView(inflater, container, savedInstanceState); return view; } @Override protected void configureOutgoingScreen() { outgoingOpponentsRelativeLayout.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.grey_transparent_50)); allOpponentsTextView.setTextColor(ContextCompat.getColor(getActivity(), R.color.white)); ringingTextView.setTextColor(ContextCompat.getColor(getActivity(), R.color.white)); } @Override protected void configureActionBar() { actionBar = ((AppCompatActivity) getActivity()).getDelegate().getSupportActionBar(); actionBar.setDisplayShowTitleEnabled(false); } @Override protected void configureToolbar() { toolbar.setVisibility(View.VISIBLE); toolbar.setBackgroundColor(ContextCompat.getColor(getActivity(), R.color.black_transparent_50)); toolbar.setTitleTextColor(ContextCompat.getColor(getActivity(), R.color.white)); toolbar.setSubtitleTextColor(ContextCompat.getColor(getActivity(), R.color.white)); } @Override int getFragmentLayout() { return R.layout.fragment_video_conversation; } @Override protected void initFields() { super.initFields(); localViewOnClickListener = new LocalViewOnClickListener(); amountOpponents = opponents.size(); allOpponents = Collections.synchronizedList(new ArrayList<QBUser>(opponents.size())); allOpponents.addAll(opponents); timerChronometer = (Chronometer) getActivity().findViewById(R.id.timer_chronometer_action_bar); isPeerToPeerCall = opponents.size() == 1; } public void setDuringCallActionBar() { actionBar.setDisplayShowTitleEnabled(true); actionBar.setTitle(currentUser.getFullName()); if (isPeerToPeerCall) { actionBar.setSubtitle(getString(R.string.opponent, opponents.get(0).getFullName())); } else { actionBar.setSubtitle(getString(R.string.opponents, amountOpponents)); } actionButtonsEnabled(true); } private void initVideoTrackSListener() { if (currentSession != null) { currentSession.addVideoTrackCallbacksListener(this); } } private void removeVideoTrackSListener() { if (currentSession != null) { currentSession.removeVideoTrackCallbacksListener(this); } } @Override protected void actionButtonsEnabled(boolean inability) { super.actionButtonsEnabled(inability); cameraToggle.setEnabled(inability); // inactivate toggle buttons cameraToggle.setActivated(inability); } @Override public void onStart() { super.onStart(); Log.i(TAG, "onStart"); if (!allCallbacksInit) { conversationFragmentCallbackListener.addTCClientConnectionCallback(this); conversationFragmentCallbackListener.addRTCSessionEventsCallback(this); initVideoTrackSListener(); allCallbacksInit = true; } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); Log.i(TAG, "onCreate"); setHasOptionsMenu(true); } @Override protected void initViews(View view) { super.initViews(view); Log.i(TAG, "initViews"); opponentViewHolders = new SparseArray<>(opponents.size()); isRemoteShown = false; isCurrentCameraFront = true; localVideoView = (QBRTCSurfaceView) view.findViewById(R.id.local_video_view); initCorrectSizeForLocalView(); localVideoView.setZOrderMediaOverlay(true); remoteFullScreenVideoView = (QBRTCSurfaceView) view.findViewById(R.id.remote_video_view); remoteFullScreenVideoView.setOnClickListener(localViewOnClickListener); if (!isPeerToPeerCall) { recyclerView = (RecyclerView) view.findViewById(R.id.grid_opponents); recyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), R.dimen.grid_item_divider)); recyclerView.setHasFixedSize(true); final int columnsCount = defineColumnsCount(); LinearLayoutManager layoutManager = new LinearLayoutManager(getActivity(), HORIZONTAL, false); recyclerView.setLayoutManager(layoutManager); // for correct removing item in adapter recyclerView.setItemAnimator(null); recyclerView.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { setGrid(columnsCount); recyclerView.getViewTreeObserver().removeGlobalOnLayoutListener(this); } }); } connectionStatusLocal = (TextView) view.findViewById(R.id.connectionStatusLocal); cameraToggle = (ToggleButton) view.findViewById(R.id.toggle_camera); cameraToggle.setVisibility(View.VISIBLE); actionVideoButtonsLayout = (LinearLayout) view.findViewById(R.id.element_set_video_buttons); actionButtonsEnabled(false); restoreSession(); } private void restoreSession() { Log.d(TAG, "restoreSession "); if (currentSession.getState() != QBRTCSession.QBRTCSessionState.QB_RTC_SESSION_ACTIVE) { return; } onCallStarted(); Map<Integer, QBRTCVideoTrack> videoTrackMap = getVideoTrackMap(); if (!videoTrackMap.isEmpty()) { for (final Iterator<Map.Entry<Integer, QBRTCVideoTrack>> entryIterator = videoTrackMap.entrySet().iterator(); entryIterator.hasNext();){ final Map.Entry<Integer, QBRTCVideoTrack> entry = entryIterator.next(); Log.d(TAG, "check ability to restoreSession for user:"+entry.getKey()); //if connection with peer wasn't closed do restore it otherwise remove from collection if (currentSession.getPeerChannel(entry.getKey()).getState()!= QBRTCTypes.QBRTCConnectionState.QB_RTC_CONNECTION_CLOSED){ Log.d(TAG, "execute restoreSession for user:"+entry.getKey()); mainHandler.postDelayed(new Runnable() { @Override public void run() { onConnectedToUser(currentSession, entry.getKey()); onRemoteVideoTrackReceive(currentSession, entry.getValue(), entry.getKey()); } }, LOCAL_TRACk_INITIALIZE_DELAY); } else { entryIterator.remove(); } } } } private void initCorrectSizeForLocalView() { ViewGroup.LayoutParams params = localVideoView.getLayoutParams(); DisplayMetrics displaymetrics = getResources().getDisplayMetrics(); int screenWidthPx = displaymetrics.widthPixels; Log.d(TAG, "screenWidthPx " + screenWidthPx); params.width = (int) (screenWidthPx * 0.3); params.height = (params.width / 2) * 3; localVideoView.setLayoutParams(params); } private void setGrid(int columnsCount) { int gridWidth = view.getMeasuredWidth(); Log.i(TAG, "onGlobalLayout : gridWidth=" + gridWidth + " columnsCount= " + columnsCount); float itemMargin = getResources().getDimension(R.dimen.grid_item_divider); int cellSizeWidth = defineSize(gridWidth, columnsCount, itemMargin); Log.i(TAG, "onGlobalLayout : cellSize=" + cellSizeWidth); opponentsAdapter = new OpponentsFromCallAdapter(getActivity(), currentSession, opponents, cellSizeWidth, (int) getResources().getDimension(R.dimen.item_height)); opponentsAdapter.setAdapterListener(VideoConversationFragment.this); recyclerView.setAdapter(opponentsAdapter); } private Map<Integer, QBRTCVideoTrack> getVideoTrackMap() { if (videoTrackMap == null) { videoTrackMap = new HashMap<>(); } return videoTrackMap; } private int defineSize(int measuredWidth, int columnsCount, float padding) { return measuredWidth / columnsCount - (int) (padding * 2) - RECYCLE_VIEW_PADDING; } private int defineColumnsCount() { return opponents.size() - 1; } @Override public void onResume() { super.onResume(); Log.d(TAG, "onResume"); // If user changed camera state few times and last state was CameraState.ENABLED_FROM_USER // than we turn on cam, else we nothing change if (cameraState != CameraState.DISABLED_FROM_USER) { toggleCamera(true); } } @Override public void onPause() { // If camera state is CameraState.ENABLED_FROM_USER or CameraState.NONE // than we turn off cam if (cameraState != CameraState.DISABLED_FROM_USER) { toggleCamera(false); } super.onPause(); } @Override public void onStop() { super.onStop(); Log.d(TAG, "onStop"); if (connectionEstablished) { allCallbacksInit = false; } else { Log.d(TAG, "We are in dialing process yet!"); } } private void removeVideoTrackRenderers() { Log.d(TAG, "removeVideoTrackRenderers"); Log.d(TAG, "remove opponents video Tracks"); Map<Integer, QBRTCVideoTrack> videoTrackMap = getVideoTrackMap(); for (QBRTCVideoTrack videoTrack : videoTrackMap.values()) { if (videoTrack.getRenderer() != null) { Log.d(TAG, "remove opponent video Tracks"); videoTrack.removeRenderer(videoTrack.getRenderer()); } } } @Override public void onDetach() { super.onDetach(); Log.d(TAG, "onDetach"); } @Override public void onDestroyView() { super.onDestroyView(); Log.d(TAG, "onDestroyView"); releaseViewHolders(); removeConnectionStateListeners(); removeVideoTrackSListener(); removeVideoTrackRenderers(); releaseViews(); } private void releaseViewHolders() { opponentViewHolders.clear(); } private void removeConnectionStateListeners(){ conversationFragmentCallbackListener.removeRTCClientConnectionCallback(this); conversationFragmentCallbackListener.removeRTCSessionEventsCallback(this); } private void releaseViews() { if (localVideoView != null){ localVideoView.release(); } if (remoteFullScreenVideoView != null) { remoteFullScreenVideoView.release(); } remoteFullScreenVideoView = null; if (!isPeerToPeerCall){ releseOpponentsViews(); } } @Override public void onCallStopped() { super.onCallStopped(); Log.i(TAG, "onCallStopped"); } protected void initButtonsListener() { super.initButtonsListener(); cameraToggle.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton buttonView, boolean isChecked) { if (cameraState != CameraState.DISABLED_FROM_USER) { toggleCamera(isChecked); } } }); } private void switchCamera(final MenuItem item) { if (cameraState == CameraState.DISABLED_FROM_USER) { return; } cameraToggle.setEnabled(false); conversationFragmentCallbackListener.onSwitchCamera(new CameraVideoCapturer.CameraSwitchHandler() { @Override public void onCameraSwitchDone(boolean b) { Log.d(TAG, "camera switched, bool = " + b); isCurrentCameraFront = b; updateSwitchCameraIcon(item); toggleCameraInternal(); } @Override public void onCameraSwitchError(String s) { Log.d(TAG, "camera switch error " + s); Toaster.shortToast(getString(R.string.camera_swicth_failed) + s); cameraToggle.setEnabled(true); } }); } private void updateSwitchCameraIcon(final MenuItem item) { if (isCurrentCameraFront) { Log.d(TAG, "CameraFront now!"); item.setIcon(R.drawable.ic_camera_front); } else { Log.d(TAG, "CameraRear now!"); item.setIcon(R.drawable.ic_camera_rear); } } private void toggleCameraInternal() { Log.d(TAG, "Camera was switched!"); updateVideoView(isLocalVideoFullScreen ? remoteFullScreenVideoView : localVideoView, isCurrentCameraFront); toggleCamera(true); } private void runOnUiThread(Runnable runnable) { mainHandler.post(runnable); } private void toggleCamera(boolean isNeedEnableCam) { if (currentSession != null && currentSession.getMediaStreamManager() != null) { conversationFragmentCallbackListener.onSetVideoEnabled(isNeedEnableCam); } if (connectionEstablished && !cameraToggle.isEnabled()) { cameraToggle.setEnabled(true); } } //////////////////////////// callbacks from QBRTCClientVideoTracksCallbacks /////////////////// @Override public void onLocalVideoTrackReceive(QBRTCSession qbrtcSession, final QBRTCVideoTrack videoTrack) { Log.d(TAG, "onLocalVideoTrackReceive() run"); localVideoTrack = videoTrack; isLocalVideoFullScreen = true; cameraState = CameraState.NONE; if (remoteFullScreenVideoView != null) { fillVideoView(remoteFullScreenVideoView, localVideoTrack, false); } } @Override public void onRemoteVideoTrackReceive(QBRTCSession session, final QBRTCVideoTrack videoTrack, final Integer userID) { Log.d(TAG, "onRemoteVideoTrackReceive for opponent= " + userID); if (localVideoTrack != null) { fillVideoView(localVideoView, localVideoTrack, false); } isLocalVideoFullScreen = false; if (isPeerToPeerCall) { setDuringCallActionBar(); fillVideoView(userID, remoteFullScreenVideoView, videoTrack, true); updateVideoView(remoteFullScreenVideoView, false); } else { mainHandler.postDelayed(new Runnable() { @Override public void run() { setRemoteViewMultiCall(userID, videoTrack); } }, LOCAL_TRACk_INITIALIZE_DELAY); } } ///////////////////////////////////////// end //////////////////////////////////////////// //last opponent view is bind @Override public void OnBindLastViewHolder(final OpponentsFromCallAdapter.ViewHolder holder, final int position) { Log.i(TAG, "OnBindLastViewHolder position=" + position); } @Override public void onItemClick(int position) { int userId = opponentsAdapter.getItem(position); Log.d(TAG, "USer onItemClick= " + userId); if (!getVideoTrackMap().containsKey(userId) || currentSession.getPeerChannel(userId).getState().ordinal() == QBRTCTypes.QBRTCConnectionState.QB_RTC_CONNECTION_CLOSED.ordinal()) { return; } replaceUsersInAdapter(position); updateViewHolders(position); swapUsersFullscreenToPreview(userId); } private void replaceUsersInAdapter(int position) { for (QBUser qbUser : allOpponents) { if (qbUser.getId() == userIDFullScreen) { opponentsAdapter.replaceUsers(position, qbUser); break; } } } private void updateViewHolders(int position) { View childView = recyclerView.getChildAt(position); OpponentsFromCallAdapter.ViewHolder childViewHolder = (OpponentsFromCallAdapter.ViewHolder) recyclerView.getChildViewHolder(childView); opponentViewHolders.put(position, childViewHolder); } @SuppressWarnings("ConstantConditions") private void swapUsersFullscreenToPreview(int userId) { // get opponentVideoTrack - opponent's video track from recyclerView QBRTCVideoTrack opponentVideoTrack = getVideoTrackMap().get(userId); // get mainVideoTrack - opponent's video track from full screen QBRTCVideoTrack mainVideoTrack = getVideoTrackMap().get(userIDFullScreen); QBRTCSurfaceView remoteVideoView = findHolder(userId).getOpponentView(); if (mainVideoTrack != null) { fillVideoView(0, remoteVideoView, mainVideoTrack); Log.d(TAG, "_remoteVideoView enabled"); } if (opponentVideoTrack != null) { fillVideoView(userId, remoteFullScreenVideoView, opponentVideoTrack); Log.d(TAG, "fullscreen enabled"); } } private void setRemoteViewMultiCall(int userID, QBRTCVideoTrack videoTrack) { Log.d(TAG, "setRemoteViewMultiCall fillVideoView"); final OpponentsFromCallAdapter.ViewHolder itemHolder = getViewHolderForOpponent(userID); if (itemHolder == null) { Log.d(TAG, "itemHolder == null - true"); return; } final QBRTCSurfaceView remoteVideoView = itemHolder.getOpponentView(); if (remoteVideoView != null) { remoteVideoView.setZOrderMediaOverlay(true); updateVideoView(remoteVideoView, false); Log.d(TAG, "onRemoteVideoTrackReceive fillVideoView"); if (isRemoteShown) { Log.d(TAG, "USer onRemoteVideoTrackReceive = " + userID); fillVideoView(userID, remoteVideoView, videoTrack, true); } else { isRemoteShown = true; opponentsAdapter.removeItem(itemHolder.getAdapterPosition()); setDuringCallActionBar(); setRecyclerViewVisibleState(); //setOpponentsVisibility(View.VISIBLE); fillVideoView(userID, remoteFullScreenVideoView, videoTrack); updateVideoView(remoteFullScreenVideoView, false); } } } private void setRecyclerViewVisibleState() { ViewGroup.LayoutParams params = recyclerView.getLayoutParams(); params.height = (int) getResources().getDimension(R.dimen.item_height); recyclerView.setLayoutParams(params); recyclerView.setVisibility(View.VISIBLE); } private OpponentsFromCallAdapter.ViewHolder getViewHolderForOpponent(Integer userID) { OpponentsFromCallAdapter.ViewHolder holder = opponentViewHolders.get(userID); if (holder == null) { Log.d(TAG, "holder not found in cache"); holder = findHolder(userID); if (holder != null) { opponentViewHolders.append(userID, holder); } } return holder; } private OpponentsFromCallAdapter.ViewHolder findHolder(Integer userID) { Log.d(TAG, "findHolder for "+userID); int childCount = recyclerView.getChildCount(); for (int i = 0; i < childCount; i++) { View childView = recyclerView.getChildAt(i); OpponentsFromCallAdapter.ViewHolder childViewHolder = (OpponentsFromCallAdapter.ViewHolder) recyclerView.getChildViewHolder(childView); if (userID.equals(childViewHolder.getUserId())) { return childViewHolder; } } return null; }; private void releseOpponentsViews(){ RecyclerView.LayoutManager layoutManager = recyclerView.getLayoutManager(); int childCount = layoutManager.getChildCount(); Log.d(TAG, " releseOpponentsViews for "+childCount + " views"); for (int i = 0; i < childCount; i++) { View childView = layoutManager.getChildAt(i); Log.d(TAG, " relese View for " + i +", "+childView); OpponentsFromCallAdapter.ViewHolder childViewHolder = (OpponentsFromCallAdapter.ViewHolder) recyclerView.getChildViewHolder(childView); childViewHolder.getOpponentView().release(); } } /** * @param userId set userId if it from fullscreen videoTrack */ private void fillVideoView(int userId, QBRTCSurfaceView videoView, QBRTCVideoTrack videoTrack, boolean remoteRenderer) { videoTrack.removeRenderer(videoTrack.getRenderer()); videoTrack.addRenderer(new VideoRenderer(videoView)); if (userId != 0) { getVideoTrackMap().put(userId, videoTrack); } if (!remoteRenderer) { updateVideoView(videoView, isCurrentCameraFront); } Log.d(TAG, (remoteRenderer ? "remote" : "local") + " Track is rendering"); } private void fillVideoView(QBRTCSurfaceView videoView, QBRTCVideoTrack videoTrack, boolean remoteRenderer) { fillVideoView(0, videoView, videoTrack, remoteRenderer); } /** * @param userId set userId if it from fullscreen videoTrack */ private void fillVideoView(int userId, QBRTCSurfaceView videoView, QBRTCVideoTrack videoTrack) { if (userId != 0) { userIDFullScreen = userId; } fillVideoView(userId, videoView, videoTrack, true); } protected void updateVideoView(SurfaceViewRenderer surfaceViewRenderer, boolean mirror) { updateVideoView(surfaceViewRenderer, mirror, RendererCommon.ScalingType.SCALE_ASPECT_FILL); } protected void updateVideoView(SurfaceViewRenderer surfaceViewRenderer, boolean mirror, RendererCommon.ScalingType scalingType) { Log.i(TAG, "updateVideoView mirror:" + mirror + ", scalingType = " + scalingType); surfaceViewRenderer.setScalingType(scalingType); surfaceViewRenderer.setMirror(mirror); surfaceViewRenderer.requestLayout(); } private void setStatusForOpponent(int userId, final String status) { if (isPeerToPeerCall) { connectionStatusLocal.setText(status); return; } final OpponentsFromCallAdapter.ViewHolder holder = findHolder(userId); if (holder == null) { return; } holder.setStatus(status); } private void updateNameForOpponent(int userId, String newUserName) { if (isPeerToPeerCall) { actionBar.setSubtitle(getString(R.string.opponent, newUserName)); } else { OpponentsFromCallAdapter.ViewHolder holder = findHolder(userId); if (holder == null) { Log.d("UPDATE_USERS", "holder == null"); return; } Log.d("UPDATE_USERS", "holder != null"); holder.setUserName(newUserName); } } private void setProgressBarForOpponentGone(int userId) { if (isPeerToPeerCall) { return; } final OpponentsFromCallAdapter.ViewHolder holder = getViewHolderForOpponent(userId); if (holder == null) { return; } holder.getProgressBar().setVisibility(View.GONE); } private void setBackgroundOpponentView(final Integer userId) { final OpponentsFromCallAdapter.ViewHolder holder = findHolder(userId); if (holder == null) { return; } if (userId != userIDFullScreen) { holder.getOpponentView().setBackgroundColor(Color.parseColor("#000000")); } } /////////////////////////////// QBRTCSessionConnectionCallbacks /////////////////////////// @Override public void onConnectedToUser(QBRTCSession qbrtcSession, final Integer userId) { connectionEstablished = true; setStatusForOpponent(userId, getString(R.string.text_status_connected)); setProgressBarForOpponentGone(userId); } @Override public void onConnectionClosedForUser(QBRTCSession qbrtcSession, Integer userId) { setStatusForOpponent(userId, getString(R.string.text_status_closed)); if (!isPeerToPeerCall) { Log.d(TAG, "onConnectionClosedForUser videoTrackMap.remove(userId)= " + userId); getVideoTrackMap().remove(userId); setBackgroundOpponentView(userId); } } @Override public void onDisconnectedFromUser(QBRTCSession qbrtcSession, Integer integer) { setStatusForOpponent(integer, getString(R.string.text_status_disconnected)); } ////////////////////////////////// end ////////////////////////////////////////// /////////////////// Callbacks from CallActivity.QBRTCSessionUserCallback ////////////////////// @Override public void onUserNotAnswer(QBRTCSession session, Integer userId) { setProgressBarForOpponentGone(userId); setStatusForOpponent(userId, getString(R.string.text_status_no_answer)); } @Override public void onCallRejectByUser(QBRTCSession session, Integer userId, Map<String, String> userInfo) { setStatusForOpponent(userId, getString(R.string.text_status_rejected)); } @Override public void onCallAcceptByUser(QBRTCSession session, Integer userId, Map<String, String> userInfo) { setStatusForOpponent(userId, getString(R.string.accepted)); } @Override public void onReceiveHangUpFromUser(QBRTCSession session, Integer userId, Map<String, String> userInfo) { setStatusForOpponent(userId, getString(R.string.text_status_hang_up)); Log.d(TAG, "onReceiveHangUpFromUser userId= " + userId); if (!isPeerToPeerCall) { if (userId == userIDFullScreen) { Log.d(TAG, "setAnotherUserToFullScreen call userId= " + userId); setAnotherUserToFullScreen(); } } } @Override public void onSessionClosed(QBRTCSession session) { } ////////////////////////////////// end ////////////////////////////////////////// private void setAnotherUserToFullScreen() { if (opponentsAdapter.getOpponents().isEmpty()) { return; } int userId = opponentsAdapter.getItem(0); // get opponentVideoTrack - opponent's video track from recyclerView QBRTCVideoTrack opponentVideoTrack = getVideoTrackMap().get(userId); if (opponentVideoTrack == null) { Log.d(TAG, "setAnotherUserToFullScreen opponentVideoTrack == null"); return; } fillVideoView(userId, remoteFullScreenVideoView, opponentVideoTrack); Log.d(TAG, "fullscreen enabled"); OpponentsFromCallAdapter.ViewHolder itemHolder = findHolder(userId); if (itemHolder != null) { opponentsAdapter.removeItem(itemHolder.getAdapterPosition()); itemHolder.getOpponentView().release(); Log.d(TAG, "onConnectionClosedForUser opponentsAdapter.removeItem= " + userId); } } @Override public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) { inflater.inflate(R.menu.conversation_fragment, menu); super.onCreateOptionsMenu(menu, inflater); } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case R.id.camera_switch: Log.d("Conversation", "camera_switch"); switchCamera(item); return true; case R.id.screen_share: startScreenSharing(); return true; default: return super.onOptionsItemSelected(item); } } private void startScreenSharing() { conversationFragmentCallbackListener.onStartScreenSharing(); } @Override public void onOpponentsListUpdated(ArrayList<QBUser> newUsers) { super.onOpponentsListUpdated(newUsers); updateAllOpponentsList(newUsers); Log.d(TAG, "updateOpponentsList(), newUsers = " + newUsers); runUpdateUsersNames(newUsers); } private void updateAllOpponentsList(ArrayList<QBUser> newUsers) { for (int i = 0; i < allOpponents.size(); i++) { for (QBUser updatedUser : newUsers) { if (updatedUser.equals(allOpponents.get(i))) { allOpponents.set(i, updatedUser); } } } } private void runUpdateUsersNames(final ArrayList<QBUser> newUsers) { //need delayed for synchronization with recycler view initialization mainHandler.postDelayed(new Runnable() { @Override public void run() { for (QBUser user : newUsers) { Log.d(TAG, "runUpdateUsersNames. foreach, user = " + user.getFullName()); updateNameForOpponent(user.getId(), user.getFullName()); } } }, UPDATING_USERS_DELAY); } private enum CameraState { NONE, DISABLED_FROM_USER, ENABLED_FROM_USER } class DividerItemDecoration extends RecyclerView.ItemDecoration { private int space; public DividerItemDecoration(@NonNull Context context, @DimenRes int dimensionDivider) { this.space = context.getResources().getDimensionPixelSize(dimensionDivider); } @Override public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) { outRect.set(space, space, space, space); } } class LocalViewOnClickListener implements View.OnClickListener { private long lastFullScreenClickTime = 0L; @Override public void onClick(View v) { if ((SystemClock.uptimeMillis() - lastFullScreenClickTime) < FULL_SCREEN_CLICK_DELAY) { return; } lastFullScreenClickTime = SystemClock.uptimeMillis(); if (connectionEstablished) { setFullScreenOnOff(); } } private void setFullScreenOnOff() { if (actionBar.isShowing()) { hideToolBarAndButtons(); } else { showToolBarAndButtons(); } } private void hideToolBarAndButtons() { actionBar.hide(); localVideoView.setVisibility(View.INVISIBLE); actionVideoButtonsLayout.setVisibility(View.GONE); if (!isPeerToPeerCall) { shiftBottomListOpponents(); } } private void showToolBarAndButtons() { actionBar.show(); localVideoView.setVisibility(View.VISIBLE); actionVideoButtonsLayout.setVisibility(View.VISIBLE); if (!isPeerToPeerCall) { shiftMarginListOpponents(); } } private void shiftBottomListOpponents() { RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams(); params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM); params.setMargins(0, 0, 0, 0); recyclerView.setLayoutParams(params); } private void shiftMarginListOpponents() { RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) recyclerView.getLayoutParams(); params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 0); params.setMargins(0, 0, 0, (int) getResources().getDimension(R.dimen.margin_common)); recyclerView.setLayoutParams(params); } } }