package com.example.secretvault.Fragments; import android.content.Context; import android.os.Bundle; import android.os.Handler; 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.CompoundButton; import android.widget.ImageButton; import android.widget.TextView; import android.widget.Toast; import android.widget.ToggleButton; import androidx.annotation.NonNull; import androidx.annotation.Nullable; import androidx.recyclerview.widget.GridLayoutManager; import androidx.recyclerview.widget.RecyclerView; import com.connectycube.chat.ConnectycubeChatService; import com.connectycube.users.ConnectycubeUsers; import com.connectycube.users.model.ConnectycubeUser; import com.connectycube.videochat.AppRTCAudioManager; import com.connectycube.videochat.BaseSession; import com.connectycube.videochat.RTCAudioTrack; import com.connectycube.videochat.RTCMediaStreamManager; import com.connectycube.videochat.RTCSession; import com.connectycube.videochat.RTCTypes; import com.connectycube.videochat.callbacks.RTCClientAudioTracksCallback; import com.connectycube.videochat.callbacks.RTCClientVideoTracksCallback; import com.connectycube.videochat.callbacks.RTCSessionConnectionCallbacks; import com.connectycube.videochat.callbacks.RTCSessionStateCallback; import com.connectycube.videochat.view.RTCSurfaceView; import com.connectycube.videochat.view.RTCVideoTrack; import com.example.secretvault.Adapter.OpponentsCallAdapter; import com.example.secretvault.Common.Common; import com.example.secretvault.R; import org.webrtc.CameraVideoCapturer; import org.webrtc.RendererCommon; import org.webrtc.SurfaceViewRenderer; import org.webrtc.voiceengine.WebRtcAudioUtils; import java.util.ArrayList; import static com.connectycube.videochat.BaseClient.TAG; import static com.example.secretvault.Common.Common.EXTRA_IS_INCOMING_CALL; import static com.example.secretvault.Common.Common.EXTRA_USERS_LIST; import static com.example.secretvault.Common.Common.currentSession; public class VideoConversationFragment extends BaseToolBarFragment implements RTCSessionStateCallback , RTCClientVideoTracksCallback, RTCSessionConnectionCallbacks{ SparseArray videoTracks = new SparseArray(); OpponentsCallAdapter opponentsAdapter; CallFragmentCallbackListener eventListener; ArrayList opponents; SparseArray opponentViewHolders; int currentUserId = 0; Boolean isRemoteShown = false; Boolean isIncomingCall = false; Boolean isCurrentCameraFront = true; CameraState cameraState = CameraState.DISABLED_FROM_USER; RecyclerView recycler_view_opponent; ImageButton button_hangup_call; ToggleButton toggle_switch_camera; ToggleButton toggle_camera; ToggleButton toggle_mute_mic; ToggleButton toggle_speaker; int fragmentLayout = R.layout.fragment_conversation_call; @Override public void onStartConnectToUser(RTCSession rtcSession, Integer integer) { } @Override public void onDisconnectedTimeoutFromUser(RTCSession rtcSession, Integer integer) { } @Override public void onConnectionFailedWithUser(RTCSession rtcSession, Integer integer) { } private enum CameraState{ NONE, DISABLED_FROM_USER, ENABLED_FROM_USER } public interface CallFragmentCallbackListener { public void onHangUpCall(); public void onSwitchAudio(); public void onStartScreenSharing(); public void onSwitchCamera(CameraVideoCapturer.CameraSwitchHandler cameraSwitchHandler); } @Override public void onAttach(Context context) { super.onAttach(context); try { eventListener = (CallFragmentCallbackListener)context; } catch (ClassCastException e) { } } @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); mainHandler = new Handler(); } @Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) { //View view = super.onCreateView(inflater, container, savedInstanceState); View view = inflater.inflate(fragmentLayout,container,false); recycler_view_opponent = view.findViewById(R.id.recycler_view_opponents); button_hangup_call = view.findViewById(R.id.button_hangup_call); toggle_switch_camera = view.findViewById(R.id.toggle_switch_camera); toggle_mute_mic = view.findViewById(R.id.toggle_mute_mic); toggle_speaker = view.findViewById(R.id.toggle_speaker); toggle_camera = view.findViewById(R.id.toggle_camera); initArguments(); restoreSession(); return view; } @Override public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { initFields(); } @Override public void onStart() { super.onStart(); if(currentSession.getState() != BaseSession.RTCSessionState.RTC_SESSION_CONNECTED){ if(isIncomingCall){ currentSession.acceptCall(null); }else{ currentSession.startCall(null); } } initVideoTrackListener(); initSessionListeners(); } private void restoreSession(){ RTCSession checkSessionState = currentSession; BaseSession.RTCSessionState state = currentSession.getState(); if(currentSession.getState() != BaseSession.RTCSessionState.RTC_SESSION_CONNECTED){ return; } SparseArray videoTracks = currentSession.getMediaStreamManager().getVideoTracks(); for(int i = 0; i newVideoTracks = new SparseArray(); for(int i=0;i)this.getArguments().getSerializable(EXTRA_USERS_LIST); currentUserId = ConnectycubeChatService.getInstance().getUser().getId(); isCurrentCameraFront = true; } private void initFields(){ button_hangup_call.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { hangUp(); } }); toggle_switch_camera.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) { switchCamera(); } }); toggle_switch_camera.setVisibility(View.VISIBLE); toggle_camera.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) { if(cameraState != CameraState.DISABLED_FROM_USER){ toggleCamera(isChecked); } } }); toggle_camera.setVisibility(View.VISIBLE); toggle_speaker.setOnClickListener(new View.OnClickListener() { @Override public void onClick(View view) { eventListener.onSwitchAudio(); } }); toggle_speaker.setVisibility(View.VISIBLE); toggle_mute_mic.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() { @Override public void onCheckedChanged(CompoundButton compoundButton, boolean isChecked) { toggleMic(isChecked); } }); toggle_mute_mic.setVisibility(View.VISIBLE); opponentViewHolders = new SparseArray(opponents.size()); initRecyclerView(); } private void initRecyclerView(){ recycler_view_opponent.setHasFixedSize(false); RecyclerView.LayoutManager layoutManager = new GridLayoutManager(getActivity(), 2); recycler_view_opponent.setLayoutManager(layoutManager); recycler_view_opponent.setItemAnimator(null); initAdapter(); recycler_view_opponent.getViewTreeObserver().addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() { @Override public void onGlobalLayout() { int height = recycler_view_opponent.getHeight(); if(height != 0){ if(isRemoteShown){ height = height/2; } updateAllCellHeight(height); recycler_view_opponent.getViewTreeObserver().removeOnGlobalLayoutListener(this); } } }); } private void initAdapter(){ int cellSizeWidth = 0; int cellSizeHeight = screenHeight(); ArrayList users = new ArrayList<>(); opponentsAdapter = new OpponentsCallAdapter(getActivity(), users, cellSizeWidth, cellSizeHeight); recycler_view_opponent.setAdapter(opponentsAdapter); } private void hangUp(){ eventListener.onHangUpCall(); } @Override public void onStateChanged(RTCSession rtcSession, BaseSession.RTCSessionState rtcSessionState) { Toast.makeText(getContext(),"VCF onStateChanged",Toast.LENGTH_LONG).show(); } @Override public void onConnectedToUser(RTCSession rtcSession, Integer userId) { setStatusForOpponent(userId, getString(R.string.text_status_connected)); Toast.makeText(getContext(),"VCF onConnectedToUser",Toast.LENGTH_LONG).show(); } @Override public void onDisconnectedFromUser(RTCSession rtcSession, Integer userId) { setStatusForOpponent(userId, getString(R.string.text_status_disconnected)); Toast.makeText(getContext(),"VCF onDisconnectedFromUser",Toast.LENGTH_LONG).show(); } @Override public void onConnectionClosedForUser(RTCSession rtcSession, Integer userId) { /* setStatusForOpponent(userId, getString(R.string.text_status_closed)); videoTracks.remove(userId); cleanAdapter(userId); Toast.makeText(getContext(),"VCF onConnectionClosedForUser",Toast.LENGTH_LONG).show();*/ } private void cleanAdapter(int userId){ OpponentsCallAdapter.ViewHolder itemHolder = getViewHolderForOpponent(userId); if (itemHolder != null) { opponentsAdapter.removeItem(itemHolder.getAdapterPosition()); opponentViewHolders.remove(userId); } } private void switchCamera(){ if (cameraState == CameraState.DISABLED_FROM_USER) { return; } eventListener.onSwitchCamera(new CameraVideoCapturer.CameraSwitchHandler() { @Override public void onCameraSwitchDone(boolean b) { isCurrentCameraFront = b; toggleCameraInternal(); } @Override public void onCameraSwitchError(String s) { } }); } private void toggleMic(Boolean isAudioEnabled){ currentSession.getMediaStreamManager().getLocalAudioTrack().setEnabled(isAudioEnabled); } private void toggleCameraInternal(){ RTCSurfaceView localView = getViewHolderForOpponent(currentUserId).itemView.findViewById(R.id.opponent_view); updateVideoView(localView, isCurrentCameraFront); toggleCamera(true); } private void toggleCamera(Boolean isNeedEnableCam){ currentSession.getMediaStreamManager().getLocalVideoTrack().setEnabled(isNeedEnableCam); if (!toggle_camera.isEnabled()) { toggle_camera.setEnabled(true); } } public void audioDeviceChanged(AppRTCAudioManager.AudioDevice newAudioDevice){ toggle_speaker.setChecked(newAudioDevice != AppRTCAudioManager.AudioDevice.SPEAKER_PHONE); } @Override public void onLocalVideoTrackReceive(RTCSession rtcSession, RTCVideoTrack rtcVideoTrack) { Log.d(TAG, "onLocalVideoTrackReceive"); cameraState = CameraState.NONE; setUserToAdapter(currentUserId); WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true); mainHandler.postDelayed(new Runnable() { @Override public void run() { setViewMultiCall(currentUserId,rtcVideoTrack,false); } },500L ); } @Override public void onRemoteVideoTrackReceive(RTCSession rtcSession, RTCVideoTrack rtcVideoTrack, Integer userId) { Toast.makeText(getContext(),"VCF onRemoteVideoTrackReceive",Toast.LENGTH_LONG).show(); updateCellSizeIfNeed(); setUserToAdapter(userId); WebRtcAudioUtils.setWebRtcBasedAcousticEchoCanceler(true); mainHandler.postDelayed(new Runnable() { @Override public void run() { setViewMultiCall(userId,rtcVideoTrack,true); } },500L); } private void updateCellSizeIfNeed(){ int height = recycler_view_opponent.getHeight() / 2; if (!isRemoteShown){ isRemoteShown = true; initCurrentUserCellHeight(height); opponentsAdapter.itemHeight = height; } } private void updateAllCellHeight(int height){ for (ConnectycubeUser user : opponentsAdapter.opponents) { OpponentsCallAdapter.ViewHolder holder = getViewHolderForOpponent(user.getId()); opponentsAdapter.initCellHeight(holder); } opponentsAdapter.itemHeight = height; } private void initCurrentUserCellHeight(int height){ OpponentsCallAdapter.ViewHolder holder = (OpponentsCallAdapter.ViewHolder)recycler_view_opponent.findViewHolderForAdapterPosition(0); if (holder instanceof OpponentsCallAdapter.ViewHolder) { opponentsAdapter.initCellHeight(holder, height); } } private void setUserToAdapter(int userId){ ConnectycubeUser user = getUserById(userId); opponentsAdapter.add(user); recycler_view_opponent.requestLayout(); } private ConnectycubeUser getUserById(int userId){ for (ConnectycubeUser user : opponents) { if (user.getId() == userId) { return user; } } return null; } private void setViewMultiCall(int userId, RTCVideoTrack videoTrack, Boolean remoteRenderer){ Toast.makeText(getContext(),userId + " VCF setViewMultiCall ",Toast.LENGTH_LONG).show(); OpponentsCallAdapter.ViewHolder itemHolder = getViewHolderForOpponent(userId); if (itemHolder != null) { RTCSurfaceView videoView = itemHolder.itemView.findViewById(R.id.opponent_view); // Log.d(TAG, "setViewMultiCall fillVideoView"); // Log.d(TAG, "setViewMultiCall videoView height= " + videoView.getHeight()); fillVideoView(userId, videoView, videoTrack, remoteRenderer); } } private OpponentsCallAdapter.ViewHolder getViewHolderForOpponent(int userId){ OpponentsCallAdapter.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 OpponentsCallAdapter.ViewHolder findHolder(int userId){ int childCount = recycler_view_opponent.getChildCount(); for (int i = 0;i opponents) { VideoConversationFragment object = new VideoConversationFragment(); Bundle args = new Bundle(); args.putSerializable(EXTRA_USERS_LIST, opponents); args.putBoolean(EXTRA_IS_INCOMING_CALL, isIncomingCall); object.setArguments(args); return object; } }