diff --git a/app/build.gradle b/app/build.gradle index facb8d416d92e3c366df573a29ac9acb5fe1c683..7439189f8504b1600e2f475171f8afbd9fd5a88b 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -632,6 +632,7 @@ dependencies { //RecyclerView header implementation 'com.timehop.stickyheadersrecyclerview:library:0.4.3@aar' + implementation 'com.android.support:recyclerview-v7:23.28.0.0' //navigation implementation 'ru.terrakok.cicerone:cicerone:3.0.0' @@ -676,8 +677,8 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // Conference, Calls mobile SDK - implementation 'com.nynja.sdk:NynjaSdk:1.20.7@aar' - //implementation(name: 'NynjaSdk-1.20.6', ext: 'aar') + implementation 'com.nynja.sdk:NynjaSdk:1.21@aar' + //implementation(name: 'NynjaSdk-1.21', ext: 'aar') //ExoPlayer implementation 'com.google.android.exoplayer:exoplayer-core:2.9.6' diff --git a/app/src/main/java/com/nynja/mobile/communicator/data/auth/AppSignatureHelper.java b/app/src/main/java/com/nynja/mobile/communicator/data/auth/AppSignatureHelper.java index ee914486f0e18bfbed59affeeb0bfb14d0064378..412c56cc4defed54c3932051c973866c84124c24 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/data/auth/AppSignatureHelper.java +++ b/app/src/main/java/com/nynja/mobile/communicator/data/auth/AppSignatureHelper.java @@ -66,10 +66,10 @@ public class AppSignatureHelper extends ContextWrapper { String base64Hash = Base64.encodeToString(hashSignature, Base64.NO_PADDING | Base64.NO_WRAP); base64Hash = base64Hash.substring(0, NUM_BASE64_CHAR); - Timber.v("sms_sample_test pkg: %s -- hash: %s", packageName, base64Hash); + Timber.v("Nynja pkg: %s -- hash: %s", packageName, base64Hash); return base64Hash; } catch (NoSuchAlgorithmException e) { - Timber.v("sms_sample_test hash:NoSuchAlgorithm"); + Timber.v("Nynja hash:NoSuchAlgorithm"); } return null; } diff --git a/app/src/main/java/com/nynja/mobile/communicator/data/calls/ActiveCallBase.java b/app/src/main/java/com/nynja/mobile/communicator/data/conference/ActiveCallBase.java similarity index 81% rename from app/src/main/java/com/nynja/mobile/communicator/data/calls/ActiveCallBase.java rename to app/src/main/java/com/nynja/mobile/communicator/data/conference/ActiveCallBase.java index ed56871a44cbe51b6234e276b27c96b85fc608e5..fce484df70291f732c2b534815501c5316c692a3 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/data/calls/ActiveCallBase.java +++ b/app/src/main/java/com/nynja/mobile/communicator/data/conference/ActiveCallBase.java @@ -1,4 +1,4 @@ -package com.nynja.mobile.communicator.data.calls; +package com.nynja.mobile.communicator.data.conference; import com.nynja.mobile.communicator.utils.StringUtils; @@ -27,15 +27,12 @@ public abstract class ActiveCallBase { public boolean isSpeakerOn; public AudioRouteType mAudioRouteType; public boolean isCallInProgress; - public boolean isOwnStreamActive; public boolean isRinging; public boolean isSwitchToAudio; public boolean isVideoEnabled; public boolean isVideoFullScreen; public boolean isScreenShareEnabled; public long mCallStartTime; // in miliseconds - public boolean hasRemoteVideoTrack; - public boolean hasRemoteScreenShareTrack; public boolean mIsAdHock; public ActiveCallBase(String endPointId, CallType callType, boolean videoEnabled, boolean outgoingCall) { @@ -43,10 +40,8 @@ public abstract class ActiveCallBase { mCallType = callType; mEndPointId = (endPointId == null) ? "" : endPointId; mCallStartTime = -1; - isVideoEnabled = (callType == CallType.P2PCall); + isVideoEnabled = true;//(callType == CallType.P2PCall); isVideoFullScreen = false; - hasRemoteVideoTrack = false; - isOwnStreamActive = videoEnabled; isOutgoingCall = outgoingCall; isMuted = false; isSpeakerOn = false; @@ -54,7 +49,6 @@ public abstract class ActiveCallBase { isRinging = false; isCallInProgress = false; isSwitchToAudio = false; - hasRemoteScreenShareTrack = false; isScreenShareEnabled = true; mIsAdHock = (isOutgoingCall && (callType == CallType.ConferenceCall) && diff --git a/app/src/main/java/com/nynja/mobile/communicator/data/conference/ConferenceVideoModule.java b/app/src/main/java/com/nynja/mobile/communicator/data/conference/ConferenceVideoModule.java new file mode 100644 index 0000000000000000000000000000000000000000..bf856e2b93332c0dc9fa952904291d57ee56aef3 --- /dev/null +++ b/app/src/main/java/com/nynja/mobile/communicator/data/conference/ConferenceVideoModule.java @@ -0,0 +1,232 @@ +package com.nynja.mobile.communicator.data.conference; + +import com.nynja.mobile.communicator.NynjaApp; +import com.nynja.mobile.communicator.data.DataManager; +import com.nynja.mobile.communicator.data.models.nynjamodels.ContactModel; +import com.nynja.mobile.communicator.data.sdk.calls.ActiveConferenceCall; +import com.nynja.mobile.communicator.data.sdk.calls.ConferenceListItem; +import com.nynja.mobile.communicator.utils.StringUtils; +import com.nynja.sdk.NYNCallParticipant; + +import org.webrtc.SurfaceViewRenderer; + +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Set; + +import kotlin.jvm.Synchronized; + +public class ConferenceVideoModule { + + private static ConferenceVideoModule mInstance = null; + public static final int MAX_VIDEO_FEEDS_COLUIMN_SIZE = 2; + public static final int MAX_VIDEO_FEEDS_ROW_SIZE = 2; + public static final int MAX_VIDEO_FEEDS_COUNT_PER_PAGE = MAX_VIDEO_FEEDS_COLUIMN_SIZE*MAX_VIDEO_FEEDS_ROW_SIZE; + + private DataManager mDataManager; + // Call worker object + private ActiveConferenceCall mCurrentActiveCall; + + public int mActiveVideoParticipantsCount = 0; + public HashMap mActiveVideoParticipantsMap= new HashMap<>(); + + + private ConferenceVideoModule() { + init(); + } + + public static ConferenceVideoModule getInstance() { + if (mInstance == null) { + mInstance = new ConferenceVideoModule(); + } + return mInstance; + } + + private void init() { + // ToDo:... + mCurrentActiveCall = null; + mDataManager = NynjaApp.getComponent().dataManager(); + mActiveVideoParticipantsCount = 0; + } + + public void reset() { + // ToDo:... + mActiveVideoParticipantsMap.clear(); + mCurrentActiveCall = null; + } + + public void setCurrentActiveCall(ActiveConferenceCall currentActiveCall) { + if (mCurrentActiveCall != currentActiveCall) { + mCurrentActiveCall = currentActiveCall; + } + } + + public void setVideoRendererForTrack(SurfaceViewRenderer videoFeed, String trackId, boolean isLocal) { + if (mCurrentActiveCall != null && mCurrentActiveCall.mConference != null) { + if (isLocal) { + mCurrentActiveCall.mConference.setLocalVideoRenderer(videoFeed); + } else { + mCurrentActiveCall.mConference.setRemoteVideoRenderer(videoFeed, trackId); + } + } + } + + public void enableVideoForTrack(String participantId, boolean enable) { + if (mCurrentActiveCall != null && mCurrentActiveCall.mConference != null) { + mCurrentActiveCall.mConference.enableVideoForTrack(participantId, enable); + } + } + + public String getTrackIdByParticipantId(String participantId) { + String trackId = ""; + if (mCurrentActiveCall != null) { + trackId = mCurrentActiveCall.mData.getTrackIdForParticipant(participantId); + } + return trackId; + } + + @Synchronized + public ArrayList getConferenceVideoMembers(ActiveConferenceCall activeConferenceCall) { + if (activeConferenceCall == null) return new ArrayList<>(); + Set participatIds = mActiveVideoParticipantsMap.keySet(); + mActiveVideoParticipantsMap.clear(); + synchronized (activeConferenceCall.mData.mParticipantArray) { + if (activeConferenceCall.mData.mParticipantArray != null) { + for (int index = 0; index < activeConferenceCall.mData.mParticipantArray.size(); index++) { + NYNCallParticipant participant = activeConferenceCall.mData.mParticipantArray.get(index); + if (participant.hasVideo() || (participant.isMe() && activeConferenceCall.mData.isOwnStreamActive)) { + ConferenceListItem item = conferenceParticipant(participant, participant.isOwner(), activeConferenceCall.mData.isOwnStreamActive); + mActiveVideoParticipantsMap.put(item.participantId, item); + } + } + } + } + + for (String participantId : participatIds) { + if (!mActiveVideoParticipantsMap.containsKey(participantId)) { + enableVideoForTrack(participantId, false); + String trackId = getTrackIdByParticipantId(participantId); + setVideoRendererForTrack(null, trackId, StringUtils.isEmpty(trackId)); + } + } + ArrayList items = new ArrayList<>(mActiveVideoParticipantsMap.values()); + mActiveVideoParticipantsCount = items.size(); + return items; + } + + @Synchronized + public ArrayList getUpdatedActiveSpeakersParticipants(ArrayList participantIds) { + ArrayList items = new ArrayList<>(mActiveVideoParticipantsMap.values()); + for (ConferenceListItem item : items) { + item.isSpeaking = false; + for (String participandId : participantIds) { + if (participandId.contentEquals(item.participantId)) { + item.isSpeaking = true; + } + } + } + return items; + } + + public float getSpanSize(int position) { + if (getWidthDiv(position) == 1 && getHeightDiv(position) == 1) return 1; + return MAX_VIDEO_FEEDS_COLUIMN_SIZE; + } + + public int getWidthDividerLength(int position) { + if (mActiveVideoParticipantsCount/MAX_VIDEO_FEEDS_COLUIMN_SIZE <= MAX_VIDEO_FEEDS_ROW_SIZE) { + return mActiveVideoParticipantsCount / MAX_VIDEO_FEEDS_COLUIMN_SIZE + 1; //MAX_VIDEO_FEEDS_ROW_SIZE + 1; + } + int lastPageBeginer = ((mActiveVideoParticipantsCount / MAX_VIDEO_FEEDS_COUNT_PER_PAGE) * MAX_VIDEO_FEEDS_COUNT_PER_PAGE); + if (mActiveVideoParticipantsCount == lastPageBeginer) { + lastPageBeginer = mActiveVideoParticipantsCount - MAX_VIDEO_FEEDS_COUNT_PER_PAGE; + } + + if (position < lastPageBeginer) { + return MAX_VIDEO_FEEDS_COUNT_PER_PAGE / MAX_VIDEO_FEEDS_COLUIMN_SIZE - 1; + } + + int row = (mActiveVideoParticipantsCount - lastPageBeginer)/MAX_VIDEO_FEEDS_COLUIMN_SIZE; + if (row <= MAX_VIDEO_FEEDS_ROW_SIZE) { + int rest = (mActiveVideoParticipantsCount - lastPageBeginer)%MAX_VIDEO_FEEDS_COLUIMN_SIZE; + if (rest == 0) { + if (row < MAX_VIDEO_FEEDS_ROW_SIZE) return row; + if (position/lastPageBeginer == 1) return row + 1; + return row - 1; + } + return row + 1; + } + + return row-1; + } + + private int widthDivider(int count) { + if (count <= MAX_VIDEO_FEEDS_COUNT_PER_PAGE) { + if ((count % MAX_VIDEO_FEEDS_COLUIMN_SIZE) == 0) { + return count / MAX_VIDEO_FEEDS_COLUIMN_SIZE; + } + } + return count / MAX_VIDEO_FEEDS_COLUIMN_SIZE + 1; + } + + private int lastPageWidthDivider(int position) { + switch (position % MAX_VIDEO_FEEDS_COUNT_PER_PAGE) { + case 0: { + if (position == mActiveVideoParticipantsCount - 1) return 1; + if (position == mActiveVideoParticipantsCount - 2) return 1; + return MAX_VIDEO_FEEDS_ROW_SIZE; + } + case 1: { + if (position == mActiveVideoParticipantsCount - 1) return 1; + if (position == mActiveVideoParticipantsCount - 2) return MAX_VIDEO_FEEDS_ROW_SIZE; + return MAX_VIDEO_FEEDS_ROW_SIZE; + } + case 2: { + return MAX_VIDEO_FEEDS_ROW_SIZE; + } + // next 3 items span 2 columns each + case 3: { + return MAX_VIDEO_FEEDS_ROW_SIZE; + } + } + + return MAX_VIDEO_FEEDS_ROW_SIZE; + } + + private int lastPageWidthDivider2(int lastPageCount) { + if (lastPageCount < MAX_VIDEO_FEEDS_COLUIMN_SIZE) return 1; + return MAX_VIDEO_FEEDS_ROW_SIZE; + } + + public int getWidthDiv(int position) { + if (mActiveVideoParticipantsCount <= MAX_VIDEO_FEEDS_COLUIMN_SIZE) return 1; + if (mActiveVideoParticipantsCount <= MAX_VIDEO_FEEDS_COUNT_PER_PAGE) { + return widthDivider(mActiveVideoParticipantsCount); + } + int lastPageBeginer = ((mActiveVideoParticipantsCount / MAX_VIDEO_FEEDS_COUNT_PER_PAGE) + * MAX_VIDEO_FEEDS_COUNT_PER_PAGE); + if (position < lastPageBeginer) { + return MAX_VIDEO_FEEDS_ROW_SIZE; + } + return lastPageWidthDivider2(mActiveVideoParticipantsCount - lastPageBeginer); + } + + public int getHeightDiv(int position) { + if (mActiveVideoParticipantsCount < MAX_VIDEO_FEEDS_COLUIMN_SIZE) return 1; + return MAX_VIDEO_FEEDS_COLUIMN_SIZE; + } + + protected ConferenceListItem conferenceParticipant(NYNCallParticipant participant, + boolean isModerator, + boolean isOwnStreamActive) { + ContactModel user = mDataManager.getContactsByPhoneId(participant.getAddress()); + ConferenceListItem conferenceListItem = ConferenceListItem.fromSdkParticipant(participant, (user != null), isOwnStreamActive); + if (user != null) { + conferenceListItem.avatar = user.avatar; + } + if (isModerator) conferenceListItem.type = ConferenceListItem.ConferenceItemType.Moderator; + + return conferenceListItem; + } + +} diff --git a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/ConferenceSDKPresenter.java b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/ConferenceSDKPresenter.java index 2072ed2aa5ce2d036fb8cc28b0e472c027bc864a..9c1ae7305cc79a10464f28039f47b09147fe0dd1 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/ConferenceSDKPresenter.java +++ b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/ConferenceSDKPresenter.java @@ -1,6 +1,6 @@ package com.nynja.mobile.communicator.data.sdk; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; import com.nynja.mobile.communicator.data.sdk.calls.ActiveConferenceCall; import com.nynja.mobile.communicator.data.sdk.calls.ConferenceSDKListener; import com.nynja.mobile.communicator.data.sdk.calls.RejoinConferenceCallData; @@ -9,6 +9,8 @@ import com.nynja.mobile.communicator.mvp.view.ErrorMvpView; import com.nynja.sdk.NYNCall; import com.nynja.sdk.NYNCallHistoryCode; +import java.util.ArrayList; + /** * Created by Ergyun Syuleyman on 4/25/18. */ @@ -53,6 +55,8 @@ public abstract class ConferenceSDKPresenter extends Bas @Override public void onScreenShareState(boolean active) {} + @Override public void onStartCapturerFailed(int msgResId, boolean isCamera) {} + @Override public void onRemoteScreenShareTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) {} @Override public void onRemoteScreenShareTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) {} @@ -73,7 +77,7 @@ public abstract class ConferenceSDKPresenter extends Bas @Override public void tryCallCameraStateChange(boolean pause) {} - @Override public void onActiveSpeakersUpdate(String activeSpeakers) {} + @Override public void onActiveSpeakersUpdate(String activeSpeakers, ArrayList participantIds) {} @Override public void onMuteStateChangedByHost(boolean isMuted) {} diff --git a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ActiveConferenceCall.java b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ActiveConferenceCall.java index d9777cef7070c7f84e012b80c60e74bdab2571fc..d613e9b7b8eed76bf948cd2c044940e4505a2c56 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ActiveConferenceCall.java +++ b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ActiveConferenceCall.java @@ -1,16 +1,14 @@ package com.nynja.mobile.communicator.data.sdk.calls; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; import com.nynja.mobile.communicator.data.models.nynjamodels.ContactModel; import com.nynja.mobile.communicator.utils.Consts; import com.nynja.mobile.communicator.utils.StringUtils; import com.nynja.sdk.NYNCall; import com.nynja.sdk.NYNCallParticipant; -import com.nynja.sdk.NYNCallParticipantArray; import com.nynja.sdk.NYNCallRoomType; import java.util.ArrayList; -import java.util.HashMap; /** * Created by Ergyun Syuleyman on 4/19/18. @@ -20,39 +18,36 @@ public class ActiveConferenceCall extends ActiveCallBase { private ArrayList mMembers; public NYNCall mConference; - public NYNCallParticipantArray mParticipantArray; public String mMyDisplayName; - public String mMyPhoneId; public boolean mIsSpeakerForceStopped; public boolean mPartial; - public int mParticipantsCountLimit; public boolean mInitialStartCapturer; - public String mRemoteSSTrackId; - public String mRemoteVideoTrackId; public boolean mWaiting; public boolean mAutoCreateAndStart; - public HashMap mActiveTracks; + public ConferenceCallData mData; - public static final int CONFERENCE_PARTICIPANTS_COUNT_LIMIT = 1000; public static final int ANDROID_10_PUSH_CALL_NTFN_ID = 1234567890; public ActiveConferenceCall(NYNCall conference, String endPointId, CallType callType, boolean isVideoEnabled, boolean outgoingCall) { super(endPointId, callType, isVideoEnabled, outgoingCall); + mData = new ConferenceCallData((callType == CallType.P2PCall), isVideoEnabled); mConference = conference; mMembers = new ArrayList(); mMyDisplayName = ""; - mMyPhoneId = ""; mIsSpeakerForceStopped = false; mPartial = false; - mParticipantsCountLimit = CONFERENCE_PARTICIPANTS_COUNT_LIMIT; // default limit - if cannot get from sdk mInitialStartCapturer = isVideoEnabled; - mRemoteSSTrackId = ""; - mRemoteVideoTrackId = ""; mWaiting = false; mAutoCreateAndStart = false; - mActiveTracks = new HashMap<>(); + } + + void setInternalCall(NYNCall conference) { + mConference = conference; + if (conference != null) { + mData.mParticipantArray = conference.getParticipants(); + } } public void setMembers(ArrayList members) { @@ -103,7 +98,7 @@ public class ActiveConferenceCall extends ActiveCallBase { public boolean isCameraRunning() { return (mConference != null - && isOwnStreamActive + && mData.isOwnStreamActive && isCallInProgress() && mConference.isCameraRunning()); } @@ -127,10 +122,10 @@ public class ActiveConferenceCall extends ActiveCallBase { public boolean isParticipateInConference(String memberId) { if (memberId == null) return false; - if (mParticipantArray == null) return false; + if (mData.mParticipantArray == null) return false; - for (int index = 0; index < mParticipantArray.size(); index++) { - NYNCallParticipant participant = mParticipantArray.get(index); + for (int index = 0; index < mData.mParticipantArray.size(); index++) { + NYNCallParticipant participant = mData.mParticipantArray.get(index); if (participant.getMemberId().contentEquals(memberId)) { return true; } @@ -145,7 +140,8 @@ public class ActiveConferenceCall extends ActiveCallBase { } public boolean isConference() { - return (mConference != null && mConference.isConference()); + return (mConference != null && + (mConference.isConference() || mCallType == CallType.ConferenceCall)); } public String getConferenceRoomId() { @@ -161,24 +157,7 @@ public class ActiveConferenceCall extends ActiveCallBase { return null; } - public String getConferenceModeratorName() { - if (mParticipantArray != null) { - for (int index = 0; index < mParticipantArray.size(); index++) { - NYNCallParticipant participant = mParticipantArray.get(index); - if (participant.isOwner()) { - return participant.getName(); - } - } - } - return ""; - - } - public String getConferenceSSOwner() { - String participanId = ""; - if (mActiveTracks.containsKey(mRemoteSSTrackId)) { - participanId = mActiveTracks.get(mRemoteSSTrackId); - } boolean isP2P = (mConference != null && !mConference.isConference()); if (isP2P) { if (mConference.isOutgoing()) { @@ -187,17 +166,8 @@ public class ActiveConferenceCall extends ActiveCallBase { return mConference.caller(); } } else { - if (mParticipantArray != null ) { - for (int index = 0; index < mParticipantArray.size(); index++) { - NYNCallParticipant participant = mParticipantArray.get(index); - if (participanId.contentEquals(participant.getParticipantId())) { - return participant.getName(); - } - } - } + return mData.getConferenceSSOwner(); } - - return getConferenceModeratorName(); } @@ -225,9 +195,9 @@ public class ActiveConferenceCall extends ActiveCallBase { ", isSpeakerOn=" + isSpeakerOn + ", mAudioRouteType=" + mAudioRouteType.toString() + ", mConference=" + mConference + - ", isOwnStreamActive=" + isOwnStreamActive + - ", hasRemoteVideoTrack=" + hasRemoteVideoTrack + - ", hasRemoteScreenShareTrack=" + hasRemoteScreenShareTrack + + ", isOwnStreamActive=" + mData.isOwnStreamActive + + ", hasRemoteVideoTrack=" + mData.hasRemoteVideoTrack + + ", hasRemoteScreenShareTrack=" + mData.hasRemoteScreenShareTrack + ", isVideoEnabled=" + isVideoEnabled + ", isCallInProgress=" + isCallInProgress + ", isRinging=" + isRinging + diff --git a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceCallData.java b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceCallData.java new file mode 100644 index 0000000000000000000000000000000000000000..8f4042828852457c7412b222bb685a3a82509465 --- /dev/null +++ b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceCallData.java @@ -0,0 +1,148 @@ +package com.nynja.mobile.communicator.data.sdk.calls; + +import com.nynja.mobile.communicator.utils.StringUtils; +import com.nynja.sdk.NYNCallParticipant; +import com.nynja.sdk.NYNCallParticipantArray; + +import java.util.HashMap; + +/** + * Created by Ergyun Syuleyman on 04/03/2020. + */ + +public class ConferenceCallData { + + public static final int CONFERENCE_PARTICIPANTS_COUNT_LIMIT = 1000; + + public NYNCallParticipantArray mParticipantArray; + public int mParticipantsCountLimit; + public String mRemoteSSTrackId; + public String mRemoteVideoTrackId; + public HashMap mActiveTracks; + public HashMap mActiveParticipanTracks; + public HashMap mActiveParticipanSSTracks; + public boolean hasRemoteVideoTrack; + public boolean hasRemoteScreenShareTrack; + public boolean mIsConference; + public boolean isOwnStreamActive; + + public ConferenceCallData() { + init(); + mIsConference = false; + } + + public ConferenceCallData(boolean isConference, boolean videoEnabled) { + init(); + mIsConference = isConference; + isOwnStreamActive = videoEnabled; + } + + private void init() { + mParticipantsCountLimit = CONFERENCE_PARTICIPANTS_COUNT_LIMIT; // default limit - if cannot get from sdk + mRemoteSSTrackId = ""; + mRemoteVideoTrackId = ""; + mActiveTracks = new HashMap<>(); + mActiveParticipanTracks = new HashMap<>(); + mActiveParticipanSSTracks = new HashMap<>(); + hasRemoteVideoTrack = false; + hasRemoteScreenShareTrack = false; + } + + public void onRemoteVideoAdded(String trackId, String participantId) { + hasRemoteVideoTrack = true; + mActiveTracks.put(trackId, participantId); + mActiveParticipanTracks.put(participantId, trackId); + mRemoteVideoTrackId = trackId; + } + + public void onRemoteVideoRemoved(String trackId, String participantId) { + mActiveTracks.remove(trackId); + mActiveParticipanTracks.remove(participantId); + hasRemoteVideoTrack = !isRemoteVideoTracksEmpty(); + mRemoteVideoTrackId = ""; + } + + public void onRemoteSSAdded(String trackId, String participantId) { + hasRemoteScreenShareTrack = true; + mActiveTracks.put(trackId, participantId); + mActiveParticipanSSTracks.put(participantId, trackId); + mRemoteSSTrackId = trackId; + } + + public void onRemoteSSRemoved(String trackId, String participantId) { + hasRemoteScreenShareTrack = false; + mActiveTracks.remove(trackId); + mActiveParticipanSSTracks.remove(participantId); + mRemoteSSTrackId = ""; + } + + public String getParticipantIdForTrack(String trackId) { + if (StringUtils.isEmpty(trackId)) return ""; + if (mActiveTracks.containsKey(trackId)) { + return mActiveTracks.get(trackId); + } + + return ""; + } + + public String getTrackIdForParticipant(String participantId) { + return getTrackIdForParticipant(participantId, true); + } + + public String getScreenTrackIdForParticipant(String participantId) { + return getTrackIdForParticipant(participantId, false); + } + + protected String getTrackIdForParticipant(String participantId, boolean isVideo) { + if (StringUtils.isEmpty(participantId)) return ""; + if (isVideo) { + if (mActiveParticipanTracks.containsKey(participantId)) { + return mActiveParticipanTracks.get(participantId); + } + } else { + if (mActiveParticipanSSTracks.containsKey(participantId)) { + return mActiveParticipanSSTracks.get(participantId); + } + } + + return ""; + } + + public String getConferenceSSOwner() { + if (mParticipantArray != null ) { + String participanId = getParticipantIdForTrack(mRemoteSSTrackId); + if (StringUtils.isEmpty(participanId)) return ""; + for (int index = 0; index < mParticipantArray.size(); index++) { + NYNCallParticipant participant = mParticipantArray.get(index); + if (participanId.contentEquals(participant.getParticipantId())) { + return participant.getName(); + } + } + } + + return getConferenceModeratorName(); + } + + public String getConferenceModeratorName() { + if (mParticipantArray != null) { + for (int index = 0; index < mParticipantArray.size(); index++) { + NYNCallParticipant participant = mParticipantArray.get(index); + if (participant.isOwner()) { + return participant.getName(); + } + } + } + + return ""; + } + + public boolean isRemoteVideoTracksEmpty() { + return !(mActiveParticipanTracks.size() > 0 && + mActiveTracks.size() > 0); + } + + public boolean hasRemoteVideoTrack() { + return (hasRemoteVideoTrack && + !isRemoteVideoTracksEmpty()); + } +} diff --git a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceListItem.java b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceListItem.java index 980d2a4e947d9fe30b8c02627fd0917d3aa778e8..e44ee8610cdb0882da1e30bde6e954d364740698 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceListItem.java +++ b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceListItem.java @@ -1,9 +1,8 @@ package com.nynja.mobile.communicator.data.sdk.calls; -import android.content.Context; - import com.nynja.mobile.communicator.R; import com.nynja.mobile.communicator.data.models.nynjamodels.ContactModel; +import com.nynja.mobile.communicator.utils.StringUtils; import com.nynja.sdk.NYNCallParticipant; import java.util.ArrayList; @@ -14,7 +13,7 @@ import io.reactivex.annotations.NonNull; * Created by Ergyun Syuleyman on 5/27/18. */ -public class ConferenceListItem { +public class ConferenceListItem extends Object { // Conference items in Grid view type public enum ConferenceItemType { Plus, @@ -39,6 +38,9 @@ public class ConferenceListItem { public boolean hasScreen; public boolean isVideoPaused; public boolean isScreenPaused; + public int isTrackActive; + public boolean isVideoFullScreen; + public boolean isSpeaking; protected ConferenceListItem() { phoneId = ""; @@ -53,6 +55,9 @@ public class ConferenceListItem { hasScreen = false; isVideoPaused = false; isScreenPaused = false; + isTrackActive = 0; + isVideoFullScreen = false; + isSpeaking = false; } protected ConferenceListItem(ConferenceItemType type, String phoneId, String memberId, @@ -73,9 +78,14 @@ public class ConferenceListItem { this.hasScreen = false; this.isVideoPaused = false; this.isScreenPaused = false; + this.isTrackActive = 0; + this.isVideoFullScreen = false; + this.isSpeaking = false; } - protected ConferenceListItem(ConferenceItemType type, NYNCallParticipant participant, boolean isFriend) { + protected ConferenceListItem(ConferenceItemType type, NYNCallParticipant participant, + boolean isFriend, + boolean isOwnStreamActive) { this.type = type; this.phoneId = participant.getAddress(); this.memberId = participant.getMemberId(); @@ -87,10 +97,32 @@ public class ConferenceListItem { this.isMuted = participant.isMuted(); this.isFriend = isFriend; this.memberActions = new ArrayList<>(); - this.hasVideo = participant.hasVideo(); + this.hasVideo = (participant.hasVideo() || (isMe && isOwnStreamActive)); this.hasScreen = participant.hasScreen(); this.isVideoPaused = participant.isVideoPaused(); this.isScreenPaused = participant.isScreenPaused(); + this.isSpeaking = participant.isSpeaking(); + this.isTrackActive = 0; + this.isVideoFullScreen = false; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ConferenceListItem item = (ConferenceListItem) o; + return (StringUtils.isEqual(phoneId, item.phoneId) + && StringUtils.isEqual(memberId, item.memberId) + && StringUtils.isEqual(participantId, item.participantId)); + } + + + @Override + public int hashCode() { + int result = phoneId != null ? phoneId.hashCode() : 0; + result = 31 * result + (memberId != null ? memberId.hashCode() : 0); + result = 31 * result + (participantId != null ? participantId.hashCode() : 0); + return result; } public void update(ConferenceListItem participant) { @@ -103,6 +135,12 @@ public class ConferenceListItem { this.hasScreen = participant.hasScreen; this.isVideoPaused = participant.isVideoPaused; this.isScreenPaused = participant.isScreenPaused; + this.isTrackActive = 0; + this.isMe = participant.isMe; + this.isFriend = participant.isFriend; + this.isMuted = participant.isMuted; + this.isSpeaking = participant.isSpeaking; + this.isVideoFullScreen = false; } private void setActions(ArrayList itemActions) { @@ -246,10 +284,11 @@ public class ConferenceListItem { return item; } - public static ConferenceListItem fromSdkParticipant(NYNCallParticipant participant, boolean isFriend) { + public static ConferenceListItem fromSdkParticipant(NYNCallParticipant participant, + boolean isFriend, + boolean isOwnStreamActive) { ConferenceListItem item = new ConferenceListItem( - ConferenceItemType.Participant, - participant, isFriend); + ConferenceItemType.Participant, participant, isFriend, isOwnStreamActive); return item; } diff --git a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceSDKListener.java b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceSDKListener.java index cb29ab18c46e86bb1e126d0ad9c775044ff4ee56..4710d6de072de19ab699bc03f4a91bb56dd74d0b 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceSDKListener.java +++ b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceSDKListener.java @@ -1,9 +1,11 @@ package com.nynja.mobile.communicator.data.sdk.calls; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; import com.nynja.sdk.NYNCall; import com.nynja.sdk.NYNCallHistoryCode; +import java.util.ArrayList; + /** * Created by Ergyun Syuleyman on 4/19/18. */ @@ -38,6 +40,8 @@ public interface ConferenceSDKListener { void onScreenShareState(boolean active); + void onStartCapturerFailed(int msgResId, boolean isCamera); + void onRemoteScreenShareTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) ; void onRemoteScreenShareTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) ; @@ -58,7 +62,7 @@ public interface ConferenceSDKListener { void tryCallCameraStateChange(boolean pause); - void onActiveSpeakersUpdate(String activeSpeakers); + void onActiveSpeakersUpdate(String activeSpeakers, ArrayList participantIds); void onMuteStateChangedByHost(boolean isMuted); diff --git a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceSDKModule.java b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceSDKModule.java index 18ccff2c405198a6cef5a44ee89363eccc7822f5..b00746dd8364d2bf498f808c26a93411eb64171a 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceSDKModule.java +++ b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/calls/ConferenceSDKModule.java @@ -1,7 +1,6 @@ package com.nynja.mobile.communicator.data.sdk.calls; import android.Manifest; -import android.app.Notification; import android.app.Service; import android.content.BroadcastReceiver; import android.content.Context; @@ -26,9 +25,9 @@ import com.nynja.mobile.communicator.NynjaApp; import com.nynja.mobile.communicator.R; import com.nynja.mobile.communicator.data.ProfileSyncManager; import com.nynja.mobile.communicator.data.audio.NynjaSoundManager; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ConferenceVideoModule; import com.nynja.mobile.communicator.data.db.DbHelper; -import com.nynja.mobile.communicator.data.db.PreferenceHelper; import com.nynja.mobile.communicator.data.models.SettingNotifications; import com.nynja.mobile.communicator.data.models.StateDevice; import com.nynja.mobile.communicator.data.models.mqtt.Conference; @@ -37,7 +36,6 @@ import com.nynja.mobile.communicator.data.models.nynjamodels.MemberModel; import com.nynja.mobile.communicator.data.sdk.BaseSDKModule; import com.nynja.mobile.communicator.ui.activities.MainActivity; import com.nynja.mobile.communicator.utils.Consts; -import com.nynja.mobile.communicator.utils.DialogFactory; import com.nynja.mobile.communicator.utils.MobileSDKConstants; import com.nynja.mobile.communicator.utils.NotificationHelper; import com.nynja.mobile.communicator.utils.StringUtils; @@ -376,8 +374,10 @@ public class ConferenceSDKModule extends BaseSDKModule { stopCallAllActiions(); clearConference(); if (fireEvent) { - for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { - conferenceSDKListener.conferenceEnded(); + synchronized (mConferenceSDKListener) { + for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { + conferenceSDKListener.conferenceEnded(); + } } } if (delayed) { @@ -395,8 +395,10 @@ public class ConferenceSDKModule extends BaseSDKModule { mConferenceDetails = null; isSpeakerPhoneForced = false; if (fireEvent) { - for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { - conferenceSDKListener.conferenceEnded(); + synchronized (mConferenceSDKListener) { + for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { + conferenceSDKListener.conferenceEnded(); + } } } if (delayed) { @@ -657,6 +659,7 @@ public class ConferenceSDKModule extends BaseSDKModule { //addCallHistoryListener(); //////////////////////////////////////////////////////////////////////////////////////////////////////// mConferenceAudioManager = AppRTCAudioManager.create(getContext()); + //ConferenceVideoModule.getInstance(); try { mCallsLooper = Looper.myLooper(); @@ -755,7 +758,7 @@ public class ConferenceSDKModule extends BaseSDKModule { && StringUtils.isNotEmpty(chatRoomId)) { mActiveConference.mEndPointId = chatRoomId; } - if (mHandler != null) mHandler.post( () -> { + runOnUIThread( () -> { onConferenceParticipantsUpdated(callId); }); } @@ -802,12 +805,7 @@ public class ConferenceSDKModule extends BaseSDKModule { Timber.d("onRemoteVideoTrackAdded(): callId=" + call.callId() + "; trackId=" + trackId + "; participantId=" + participantId + "; isConference=" + (isConference ? "true" : "false")); - if (isConference) { - mActiveConference.hasRemoteVideoTrack = true; - onRemoteScreenShareAdded(call.callId(), trackId, participantId); - } else { - onRemoteVideoAdded(call.callId(), trackId, participantId, isConference); - } + onRemoteVideoAdded(call.callId(), trackId, participantId, isConference); } } @@ -818,12 +816,7 @@ public class ConferenceSDKModule extends BaseSDKModule { Timber.d("onRemoteVideoTrackRemoved(): callId=" + call.callId() + "; trackId=" + trackId + "; participantId=" + participantId + "; isConference=" + (isConference ? "true" : "false")); - if (isConference) { - onRemoteScreenShareRemoved(call.callId(), trackId, participantId); - mActiveConference.hasRemoteVideoTrack = false; - } else { - onRemoteVideoRemoved(call.callId(), trackId, participantId, isConference); - } + onRemoteVideoRemoved(call.callId(), trackId, participantId, isConference); } } @@ -872,10 +865,10 @@ public class ConferenceSDKModule extends BaseSDKModule { } @Override - public void startScreenShareFailed(NYNCall call) { + public void startScreenShareFailed(NYNCall call, int code) { if (call != null) { Timber.d("startScreenShareFailed(): callId=" + call.callId()); - onOwnScreenShareStopped(call); + onStartCapturerFailed(call, code, false); } } @@ -886,6 +879,14 @@ public class ConferenceSDKModule extends BaseSDKModule { onOwnScreenShareStopped(call); } } + + @Override + public void startCameraFailed(NYNCall call, int code) { + if (call != null) { + Timber.d("startCameraFailed(): callId=" + call.callId()); + onStartCapturerFailed(call, code, true); + } + } }; } @@ -899,8 +900,8 @@ public class ConferenceSDKModule extends BaseSDKModule { mConferenceDetails.mCallId = conferenceId; NYNCall conference = mCallManager.getCallById(conferenceId); if (conference != null) { - mConferenceDetails.mActiveConference.mConference = conference; - mConferenceDetails.mActiveConference.mParticipantsCountLimit = conference.getParticipantsCountLimit(); + mConferenceDetails.mActiveConference.setInternalCall(conference); + mConferenceDetails.mActiveConference.mData.mParticipantsCountLimit = conference.getParticipantsCountLimit(); onGetConferenceRoomLimitsFinished(null, mConferenceDetails.mActiveConference.mEndPointId, conference.getParticipantsCountLimit()); addConferenceCallListener(mConferenceDetails.mActiveConference.mConference); @@ -1024,10 +1025,10 @@ public class ConferenceSDKModule extends BaseSDKModule { conferenceId != null && conferenceId.contentEquals(mConferenceDetails.mCallId) && mActiveConference.isWaiting()) { - mActiveConference.mConference = mCallManager.getCallById(mConferenceDetails.mCallId); + mActiveConference.setInternalCall(mCallManager.getCallById(mConferenceDetails.mCallId)); if (mActiveConference.mConference != null) { // todo: for limits use the new logic - local hashMap - mActiveConference.mParticipantsCountLimit = mActiveConference.mConference.getParticipantsCountLimit(); + mActiveConference.mData.mParticipantsCountLimit = mActiveConference.mConference.getParticipantsCountLimit(); addConferenceCallListener(mActiveConference.mConference); onCallReady(conferenceId, mActiveConference.isVideoEnabled, true); } @@ -1072,11 +1073,13 @@ public class ConferenceSDKModule extends BaseSDKModule { if (!hasCreatedActiveCall()) return; Timber.d("Request conference member with \'ConferenceId\'=\'" + conferenceId + "\' " + (result ? "succeed" : "failed!!!")); - mActiveConference.mParticipantArray = mActiveConference.mConference.getParticipants(); + synchronized (mActiveConference.mData) { + mActiveConference.mData.mParticipantArray = mActiveConference.mConference.getParticipants(); + } // update chatRoomId - if (mActiveConference.mParticipantArray != null && - mActiveConference.mParticipantArray.size() > 0 && + if (mActiveConference.mData.mParticipantArray != null && + mActiveConference.mData.mParticipantArray.size() > 0 && StringUtils.isEmpty(mActiveConference.mEndPointId)) { mActiveConference.mEndPointId = mActiveConference.mConference.getChatRoomId(); } @@ -1131,6 +1134,7 @@ public class ConferenceSDKModule extends BaseSDKModule { if (call == null) return; if (!mActiveConference.mConference.callId().contentEquals(call.callId())) return; String activeSpeakers = ""; + ArrayList participantIds = new ArrayList<>(); NYNCallParticipantArray participantArray = call.getParticipants(); for (int index = 0; index < participantArray.size(); index++) { NYNCallParticipant participant = participantArray.get(index); @@ -1141,9 +1145,10 @@ public class ConferenceSDKModule extends BaseSDKModule { } if (!activeSpeakers.isEmpty()) activeSpeakers += ", "; activeSpeakers += participant.getName(); + participantIds.add(participant.getParticipantId()); } for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) - conferenceSDKListener.onActiveSpeakersUpdate(activeSpeakers); + conferenceSDKListener.onActiveSpeakersUpdate(activeSpeakers, participantIds); } private synchronized void onMuteStateChangedByHost(NYNCall call, boolean isMuted) { @@ -1429,7 +1434,7 @@ public class ConferenceSDKModule extends BaseSDKModule { @Override public void createCallFinished(String requestId, String callId, boolean success) { if (!hasCreatedActiveCall()) return; - mActiveConference.mConference = mCallManager.getCallById(callId); + mActiveConference.setInternalCall(mCallManager.getCallById(callId)); Timber.d("Request create call with \'CallId\'=\'" + callId + "\' " + (success ? "succeed" : "failed!!!")); if (mActiveConference.isOutgoingCall) { @@ -1568,7 +1573,9 @@ public class ConferenceSDKModule extends BaseSDKModule { initConferenceCall(false); new Handler(Looper.getMainLooper()).postDelayed(() -> { mActiveConference.isCallInProgress = true; - mActiveConference.mParticipantArray = mActiveConference.mConference.getParticipants(); + synchronized (mActiveConference.mData) { + mActiveConference.mData.mParticipantArray = mActiveConference.mConference.getParticipants(); + } startConferenceActivityImpl(NynjaNavigator.MAIN_NEW_CALL, true); }, Consts.DELAY_100); } @@ -1602,61 +1609,96 @@ public class ConferenceSDKModule extends BaseSDKModule { private void onRemoteScreenShareAdded(String callId, String trackId, String participantId) { if (!hasCreatedActiveCall()) return; - mActiveConference.mActiveTracks.put(trackId, participantId); + mActiveConference.mData.onRemoteSSAdded(trackId, participantId); if (mActiveConference.mConference == null) return; if (mActiveConference.mConference.callId().contentEquals(callId)) { - mActiveConference.mRemoteSSTrackId = trackId; - mActiveConference.hasRemoteScreenShareTrack = true; for (ConferenceSDKListener conferenceListener : mConferenceSDKListener) { conferenceListener.onRemoteScreenShareTrackAdded(mActiveConference, trackId); } } +// mActiveConference.mData.mActiveTracks.put(trackId, participantId); +// if (mActiveConference.mConference == null) return; +// if (mActiveConference.mConference.callId().contentEquals(callId)) { +// mActiveConference.mData.mRemoteSSTrackId = trackId; +// mActiveConference.mData.hasRemoteScreenShareTrack = true; +// for (ConferenceSDKListener conferenceListener : mConferenceSDKListener) { +// conferenceListener.onRemoteScreenShareTrackAdded(mActiveConference, trackId); +// } +// } } private void onRemoteScreenShareRemoved(String callId, String trackId, String participantId) { if (!hasCreatedActiveCall()) return; - mActiveConference.mActiveTracks.remove(trackId); + mActiveConference.mData.onRemoteSSRemoved(trackId, participantId); if (mActiveConference.mConference == null) return; if (mActiveConference.mConference.callId().contentEquals(callId)) { - mActiveConference.hasRemoteScreenShareTrack = false; for (ConferenceSDKListener conferenceListener : mConferenceSDKListener) { conferenceListener.onRemoteScreenShareTrackRemoved(mActiveConference, trackId); } - mActiveConference.mRemoteSSTrackId = ""; } +// mActiveConference.mData.mActiveTracks.remove(trackId); +// if (mActiveConference.mConference == null) return; +// if (mActiveConference.mConference.callId().contentEquals(callId)) { +// mActiveConference.mData.hasRemoteScreenShareTrack = false; +// for (ConferenceSDKListener conferenceListener : mConferenceSDKListener) { +// conferenceListener.onRemoteScreenShareTrackRemoved(mActiveConference, trackId); +// } +// mActiveConference.mData.mRemoteSSTrackId = ""; +// } } private void onRemoteVideoAdded(String callId, String trackId, String participantId, boolean isConference) { if (!hasCreatedActiveCall()) return; - mActiveConference.mActiveTracks.put(trackId, participantId); + mActiveConference.mData.onRemoteVideoAdded(trackId, participantId); if (mActiveConference.mConference == null) return; if (mActiveConference.mConference.callId().contentEquals(callId)) { - mActiveConference.mRemoteVideoTrackId = trackId; - mActiveConference.hasRemoteVideoTrack = true; for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { conferenceSDKListener.onRemoteVideoTrackAdded(mActiveConference, trackId); } } +// if (isConference) { +// mActiveConference.mData.hasRemoteVideoTrack = true; +// onRemoteScreenShareAdded(callId, trackId, participantId); +// } else { +// mActiveConference.mData.mActiveTracks.put(trackId, participantId); +// if (mActiveConference.mConference == null) return; +// if (mActiveConference.mConference.callId().contentEquals(callId)) { +// mActiveConference.mData.mRemoteVideoTrackId = trackId; +// mActiveConference.mData.hasRemoteVideoTrack = true; +// } +// } } private void onRemoteVideoRemoved(String callId, String trackId, String participantId, boolean isConference) { if (!hasCreatedActiveCall()) return; - mActiveConference.mActiveTracks.remove(trackId); + mActiveConference.mData.onRemoteVideoRemoved(trackId, participantId); if (mActiveConference.mConference == null) return; if (mActiveConference.mConference.callId().contentEquals(callId)) { - mActiveConference.hasRemoteVideoTrack = false; for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { conferenceSDKListener.onRemoteVideoTrackRemoved(mActiveConference, trackId); } - mActiveConference.mRemoteVideoTrackId = ""; } +// if (isConference) { +// onRemoteScreenShareRemoved(callId, trackId, participantId); +// mActiveConference.mData.hasRemoteVideoTrack = false; +// } else { +// mActiveConference.mData.mActiveTracks.remove(trackId); +// if (mActiveConference.mConference == null) return; +// if (mActiveConference.mConference.callId().contentEquals(callId)) { +// mActiveConference.mData.hasRemoteVideoTrack = false; +// for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { +// conferenceSDKListener.onRemoteVideoTrackRemoved(mActiveConference, trackId); +// } +// mActiveConference.mData.mRemoteVideoTrackId = ""; +// } +// } } private void onLocalVideoStarted(String callId) { if (!hasCreatedActiveCall()) return; if (mActiveConference.mConference == null) return; if (mActiveConference.mConference.callId().contentEquals(callId)) { - mActiveConference.isOwnStreamActive = true; + mActiveConference.mData.isOwnStreamActive = true; for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { conferenceSDKListener.onLocalVideoCapturerStarted(mActiveConference); } @@ -1667,7 +1709,12 @@ public class ConferenceSDKModule extends BaseSDKModule { if (!hasCreatedActiveCall()) return; if (mActiveConference.mConference == null) return; if (mActiveConference.mConference.callId().contentEquals(callId)) { - mActiveConference.isOwnStreamActive = false; + mActiveConference.mData.isOwnStreamActive = false; + if (mActiveConference.isConference()) { + new Handler(Looper.getMainLooper()).post(() -> { + mActiveConference.mConference.setLocalVideoRenderer(null); + }); + } for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { conferenceSDKListener.onLocalVideoCapturerStopped(mActiveConference); } @@ -1708,6 +1755,28 @@ public class ConferenceSDKModule extends BaseSDKModule { } } + private void onStartCapturerFailed(NYNCall call, int code, boolean isCamera) { + if (call != null && + hasCreatedActiveCall() && mActiveConference.mConference != null + && mActiveConference.mConference.callId().contentEquals(call.callId())) { + for (ConferenceSDKListener conferenceListener : mConferenceSDKListener) { + if (code == -47) { + if (isCamera) { + conferenceListener.onStartCapturerFailed(R.string.call_start_camera_failed_busy, isCamera); + } else { + conferenceListener.onStartCapturerFailed(R.string.call_start_ss_failed_busy, isCamera); + } + } else { + if (isCamera) { + conferenceListener.onStartCapturerFailed(R.string.call_start_camera_failed_invalid_state, isCamera); + } else { + conferenceListener.onStartCapturerFailed(R.string.call_start_ss_failed_invalid_state, isCamera); + } + } + } + } + } + private void onChangeCallState(NYNCall call, NYNCallState newState) { for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { conferenceSDKListener.onConferenceStateChanged(mActiveConference); @@ -2083,13 +2152,10 @@ public class ConferenceSDKModule extends BaseSDKModule { mConferenceDetails = new ConferenceDetails(getDeviceId()); mActiveConference = createActiveConference(null, contact.phoneId, true, isVideoEnabled, ActiveCallBase.CallType.P2PCall); - mActiveConference.isOwnStreamActive = isVideoEnabled; + mActiveConference.mData.isOwnStreamActive = isVideoEnabled; mActiveConference.addMember(contact); setConferenceCallName(mActiveConference, contact.getFullName()); AppsTrackerUtils.getCleverTapInstance().trackEvent(getContext(), AppsTrackerConsts.ATEventMakeP2PCall); - if (StringUtils.isNotEmpty(phoneId)) { - mActiveConference.mMyPhoneId = phoneId; - } startConferenceActivityImpl(NynjaNavigator.ACTIVE_CALL); } } @@ -2563,10 +2629,10 @@ public class ConferenceSDKModule extends BaseSDKModule { private void updateConferenceNameIfNeeds() { if (mConferenceDetails != null && mConferenceDetails.mMembersCounter < 0) { if (mActiveConference == null) return; - if (mActiveConference.mParticipantArray == null) return; - if (mActiveConference.mParticipantArray.size() > 0 && TEMP_ALLOW_UPDATE_CONF_CALLS_NAME) { + if (mActiveConference.mData.mParticipantArray == null) return; + if (mActiveConference.mData.mParticipantArray.size() > 0 && TEMP_ALLOW_UPDATE_CONF_CALLS_NAME) { tryUpdateConferenceCallName(mActiveConference, - generateGroupConferenceName(mActiveConference.mParticipantArray.size() - 1)); + generateGroupConferenceName(mActiveConference.mData.mParticipantArray.size() - 1)); } } @@ -2585,11 +2651,11 @@ public class ConferenceSDKModule extends BaseSDKModule { if (mActiveConference.mConference != null) { mActiveConference.mConference.setListener(null); } - mActiveConference.mConference = mCallManager.getCallById(mConferenceDetails.mCallId); + mActiveConference.setInternalCall(mCallManager.getCallById(mConferenceDetails.mCallId)); } if (mActiveConference.mConference != null) { // todo: for limits use the new logic - local hashMap - mActiveConference.mParticipantsCountLimit = mActiveConference.mConference.getParticipantsCountLimit(); + mActiveConference.mData.mParticipantsCountLimit = mActiveConference.mConference.getParticipantsCountLimit(); addConferenceCallListener(mActiveConference.mConference); } } @@ -2844,7 +2910,7 @@ public class ConferenceSDKModule extends BaseSDKModule { mConferenceDetails = new ConferenceDetails(getDeviceId(), iConference.callId()); mActiveConference = createActiveConference(iConference); if (!hasCreatedActiveCall()) return false; - mActiveConference.isOwnStreamActive = false; + mActiveConference.mData.isOwnStreamActive = false; initConferenceCall(startActivity); if (startActivity) { if (Consts.SHOW_INCOMING_CALLS_AS_POPUP_NOTIFICATIONS) { @@ -2913,8 +2979,8 @@ public class ConferenceSDKModule extends BaseSDKModule { if (mConferenceDetails.mCallId != null && mConferenceDetails.mCallId.contentEquals(conferenceId)) { // @TODO: request update server side conference name ..... - for (int index = 0; index < mActiveConference.mParticipantArray.size(); index++) { - NYNCallParticipant participant = mActiveConference.mParticipantArray.get(index); + for (int index = 0; index < mActiveConference.mData.mParticipantArray.size(); index++) { + NYNCallParticipant participant = mActiveConference.mData.mParticipantArray.get(index); if (!participant.getMemberId().contentEquals(memberId)) { continue; } @@ -2924,10 +2990,12 @@ public class ConferenceSDKModule extends BaseSDKModule { break; } } - if (mActiveConference.mParticipantArray.size() > 1 && TEMP_ALLOW_UPDATE_CONF_CALLS_NAME) { - mActiveConference.mParticipantArray.remove(index); + if (mActiveConference.mData.mParticipantArray.size() > 1 && TEMP_ALLOW_UPDATE_CONF_CALLS_NAME) { + synchronized (mActiveConference.mData) { + mActiveConference.mData.mParticipantArray.remove(index); + } tryUpdateConferenceCallName(mActiveConference, - generateGroupConferenceName(mActiveConference.mParticipantArray.size() - 1));// exclude me + generateGroupConferenceName(mActiveConference.mData.mParticipantArray.size() - 1));// exclude me } break; } @@ -3045,7 +3113,7 @@ public class ConferenceSDKModule extends BaseSDKModule { return; } if (!mActiveConference.isOutgoingCall) { - mActiveConference.isVideoEnabled = videoEnabled; + //mActiveConference.isVideoEnabled = videoEnabled; } mActiveConference.isCallInProgress = true; mActiveConference.isRinging = false; @@ -3186,6 +3254,13 @@ public class ConferenceSDKModule extends BaseSDKModule { if (!activeConferenceCall.mConference.isCameraRunning()) return; activeConferenceCall.mConference.stopCamera(); + if (mActiveConference.isConference()) { + new Handler(Looper.getMainLooper()).post(() -> { + if (mActiveConference != null && mActiveConference.mConference != null) { + mActiveConference.mConference.setLocalVideoRenderer(null); + } + }); + } } public boolean hasRemoteScreenShare() { @@ -3193,7 +3268,7 @@ public class ConferenceSDKModule extends BaseSDKModule { if (mActiveConference.mConference == null) return false; return (mActiveConference.mConference.hasRemoteScreenShare() - || !mActiveConference.mActiveTracks.isEmpty()); + || !mActiveConference.mData.mActiveTracks.isEmpty()); } public boolean isScreenSharing() { @@ -3295,13 +3370,13 @@ public class ConferenceSDKModule extends BaseSDKModule { } public int getActiveConferenceParticipantsLimit() { - if (!hasCreatedActiveCall()) return ActiveConferenceCall.CONFERENCE_PARTICIPANTS_COUNT_LIMIT; + if (!hasCreatedActiveCall()) return ConferenceCallData.CONFERENCE_PARTICIPANTS_COUNT_LIMIT; if (mActiveConference.mConference != null && mActiveConference.mCallType == ActiveCallBase.CallType.P2PCall && mConferenceDetails != null && mConferenceDetails.mActiveConference != null) { - return mConferenceDetails.mActiveConference.mParticipantsCountLimit; + return mConferenceDetails.mActiveConference.mData.mParticipantsCountLimit; } - return mActiveConference.mParticipantsCountLimit; + return mActiveConference.mData.mParticipantsCountLimit; } private void playConnecting() { @@ -3425,7 +3500,7 @@ public class ConferenceSDKModule extends BaseSDKModule { replaceCallEx(subject, deleteGroupIfNoMessages, deleteAfterCall, isVideoConference, mActiveConference.mConference.callId(), - contacts); + contacts, mActiveConference.mData.isOwnStreamActive); } else { createAndStartConferenceEx(subject, chatRoomId, deleteGroupIfNoMessages, deleteAfterCall, isVideoConference, contacts); @@ -3460,7 +3535,7 @@ public class ConferenceSDKModule extends BaseSDKModule { if (limits >= 0) return limits; // set the default value - limits = ActiveConferenceCall.CONFERENCE_PARTICIPANTS_COUNT_LIMIT; + limits = ConferenceCallData.CONFERENCE_PARTICIPANTS_COUNT_LIMIT; mConferenceRoomLimits.put(defRoomId, limits); return limits; @@ -3511,7 +3586,8 @@ public class ConferenceSDKModule extends BaseSDKModule { boolean deleteAfterCall, boolean isVideoConference, String replaceRef, - ArrayList contacts) { + ArrayList contacts, + boolean isVideo) { if (StringUtils.isEmpty(replaceRef)) return; if (!(hasCreatedActiveCall() && isP2P())) return; @@ -3524,7 +3600,8 @@ public class ConferenceSDKModule extends BaseSDKModule { subject, !deleteGroupIfNoMessages, deleteAfterCall ? DELETE_CONFERENCE_CALL_AFTER_END : DELETE_CONFERENCE_CALL_AFTER_1_DAY, - members ); + members, + isVideoConference || isVideo); AppsTrackerUtils.getCleverTapInstance().trackEvent(getContext(), AppsTrackerConsts.ATEventMakeGroupCall); }); } @@ -3549,7 +3626,8 @@ public class ConferenceSDKModule extends BaseSDKModule { !deleteGroupIfNoMessages, deleteAfterCall ? DELETE_CONFERENCE_CALL_AFTER_END : DELETE_CONFERENCE_CALL_AFTER_1_DAY, "", - members ); + members, + isVideoConference); AppsTrackerUtils.getCleverTapInstance().trackEvent(getContext(), AppsTrackerConsts.ATEventMakeGroupCall); }); diff --git a/app/src/main/java/com/nynja/mobile/communicator/interfaces/OnConferenceVideoFeedClickListener.java b/app/src/main/java/com/nynja/mobile/communicator/interfaces/OnConferenceVideoFeedClickListener.java new file mode 100644 index 0000000000000000000000000000000000000000..a707d0639e3182770cc62cf4b71f1abf7a98d1aa --- /dev/null +++ b/app/src/main/java/com/nynja/mobile/communicator/interfaces/OnConferenceVideoFeedClickListener.java @@ -0,0 +1,9 @@ +package com.nynja.mobile.communicator.interfaces; + +/** + * Created by Ergyun Syuleyman on 4/25/18. + */ + +public interface OnConferenceVideoFeedClickListener extends OnConferenceItemClickListener { + void onSwitchCameraClick(T item); +} diff --git a/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/BasePresenter.java b/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/BasePresenter.java index b7a5129ff4333d8330b1021b05a77156e4026e64..fd01699566b77cf3c4cd164b78315bbf9406a1a3 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/BasePresenter.java +++ b/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/BasePresenter.java @@ -9,7 +9,7 @@ import com.nynja.mobile.communicator.data.DataManager; import com.nynja.mobile.communicator.data.FragmentTransferObject; import com.nynja.mobile.communicator.data.GooglePlacesService; import com.nynja.mobile.communicator.data.NynjaKeyboardManager; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; import com.nynja.mobile.communicator.data.models.events.ServerData; import com.nynja.mobile.communicator.data.models.events.ServerMessage; import com.nynja.mobile.communicator.data.models.events.local.AccountAuthFailedEvent; diff --git a/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/CallActivityPresenter.java b/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/CallActivityPresenter.java index 416edcc81415ccfb4eca973945b0b7cb58c13d9a..53640f1f51a6c6a1773602892176bea7896c7476 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/CallActivityPresenter.java +++ b/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/CallActivityPresenter.java @@ -1,14 +1,12 @@ package com.nynja.mobile.communicator.mvp.presenters; import com.arellomobile.mvp.InjectViewState; -import com.nynja.mobile.communicator.NynjaApp; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; import com.nynja.mobile.communicator.data.models.events.local.Event; import com.nynja.mobile.communicator.data.models.events.local.SimpleEvent; import com.nynja.mobile.communicator.data.sdk.ConferenceSDKPresenter; import com.nynja.mobile.communicator.data.sdk.calls.ActiveConferenceCall; import com.nynja.mobile.communicator.mvp.view.CallActivityView; -import com.nynja.mobile.communicator.utils.navigation.navigators.NynjaNavigator; /** * Created by Ergyun Syuleyman on 7/19/19. @@ -36,6 +34,37 @@ public class CallActivityPresenter extends ConferenceSDKPresenter { return; } if (mRoom.isGroupOrCalllChat()) { - getViewState().showAlertNotSupportedFeature(); -// FragmentTransferObject fto = new FragmentTransferObject(null, mRoom, OpenVideoConferenceCreate); -// mRouter.navigateTo(HomeNavigator.CHOOSE_USER, fto); + if (Consts.CLIENT_ALLOWED_VIDEO_CONFERENCE) { + navigateToStartGroupCall(true, mRoom); +// FragmentTransferObject fto = new FragmentTransferObject(null, mRoom, OpenVideoConferenceCreate); +// mRouter.navigateTo(HomeNavigator.CHOOSE_USER, fto); + } else { + getViewState().showMessage(R.string.call_start_video_conference_alert); + } } else { mDataManager.startCall(new ContactModel(mRoom.getOnlyMembers().get(0)), true); } diff --git a/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/ConferenceCallPresenter.java b/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/ConferenceCallPresenter.java index 09ed005c481169a421b6d02fc6434a77eadd2ac7..f4bb992891edc05a899a794d3e695f626c802ae3 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/ConferenceCallPresenter.java +++ b/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/ConferenceCallPresenter.java @@ -5,7 +5,7 @@ import android.support.annotation.Nullable; import com.arellomobile.mvp.InjectViewState; import com.nynja.mobile.communicator.R; import com.nynja.mobile.communicator.data.FragmentTransferObject; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; import com.nynja.mobile.communicator.data.models.events.local.Event; import com.nynja.mobile.communicator.data.models.events.local.SimpleEvent; import com.nynja.mobile.communicator.data.models.nynjamodels.ContactModel; @@ -69,6 +69,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter } public void switchCamera() { + if (getAttachedViews().size() == 0) return; ActiveConferenceCall activeConferenceCall = mDataManager.getConferenceSDK(). getActiveConference(); if (activeConferenceCall != null && activeConferenceCall.mConference != null) { @@ -91,6 +92,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter } public void showProfile(ConferenceListItem item) { + if (getAttachedViews().size() == 0) return; getViewState().closenConference(); if (!item.isMe) { ContactModel contact = mDataManager.getContactByChatId(item.phoneId); @@ -184,6 +186,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter } public void goToHome() { + if (getAttachedViews().size() == 0) return; getViewState().closenConference(); openHome(); } @@ -191,6 +194,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter public void openChatWithParticipant(String participantPhoneId) { ContactModel contactByChatId = mDataManager.getContactByChatId(participantPhoneId); if (contactByChatId != null) { + if (getAttachedViews().size() == 0) return; getViewState().closenConference(); RoomModel room = new RoomModel(contactByChatId); FragmentTransferObject fto = new FragmentTransferObject(room, null); @@ -199,6 +203,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter } public void navigateToChatRoom(RoomModel room) { + if (getAttachedViews().size() == 0) return; if (mDataManager.isMainActivityIsActive()) { getViewState().closenConference(); FragmentTransferObject fto = null; @@ -213,6 +218,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter } public void loadUser(ActiveConferenceCall activeConferenceCall) { + if (getAttachedViews().size() == 0) return; ContactModel user = mDataManager.getContactByChatId(activeConferenceCall.mEndPointId); getViewState().setUser(user); if (user == null) { @@ -225,6 +231,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter } public void onLeaveCall() { + if (getAttachedViews().size() == 0) return; if (isConferenceActive()) { if (isP2PCall()) { getViewState().showLeaveCallConfirmation(); @@ -244,6 +251,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter @Override public void timeAway(boolean videoEnabled, String time) { + if (getAttachedViews().size() == 0) return; getViewState().conferenceTimeUpdated(videoEnabled, time); } @@ -254,17 +262,19 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter @Override public void conferenceEnded() { - if (getViewState() == null) return; + if (getAttachedViews().size() == 0) return; getViewState().onConferenceEnded(); } @Override public void onConferenceRinging() { + if (getAttachedViews().size() == 0) return; getViewState().onConferenceRinging(); } @Override public void onConferenceConnected(ActiveConferenceCall activeConferenceCall) { + if (getAttachedViews().size() == 0) return; if (!activeConferenceCall.mConference.isConference()) { String personId = activeConferenceCall.isOutgoingCall ? activeConferenceCall.mEndPointId : activeConferenceCall.mConference.caller(); @@ -276,32 +286,34 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter @Override public void onConferenceFailed(String reason) { - if (getViewState() == null) return; + if (getAttachedViews().size() == 0) return; getViewState().onConferenceFailed(reason); } @Override public void onConferenceJoinFailed(String reason) { - if (getViewState() == null) return; + if (getAttachedViews().size() == 0) return; getViewState().onConferenceJoinFailed(reason); } @Override public void onCallFailed(String reason) { - if (getViewState() == null) return; + if (getAttachedViews().size() == 0) return; getViewState().onCallFailed(reason); } @Override public void onConferenceStateChanged(ActiveConferenceCall activeConferenceCall) { + if (getAttachedViews().size() == 0) return; getViewState().onConferenceStateChanged(activeConferenceCall); } protected ConferenceListItem conferenceParticipant(NYNCallParticipant participant, - boolean isModerator) { + boolean isModerator, + boolean isOwnStreamActive) { ContactModel user = mDataManager.getContactsByPhoneId(participant.getAddress()); - ConferenceListItem conferenceListItem = ConferenceListItem.fromSdkParticipant(participant, (user != null)); + ConferenceListItem conferenceListItem = ConferenceListItem.fromSdkParticipant(participant, (user != null), isOwnStreamActive); if (user != null) { conferenceListItem.avatar = user.avatar; } @@ -312,12 +324,13 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter @Override public void onConferenceMembersUpdate(ActiveConferenceCall activeConferenceCall) { + if (getAttachedViews().size() == 0) return; if (activeConferenceCall == null) return; ArrayList members = new ArrayList<>(); - if (activeConferenceCall.mParticipantArray != null) { - for (int index = 0; index < activeConferenceCall.mParticipantArray.size(); index++) { - NYNCallParticipant participant = activeConferenceCall.mParticipantArray.get(index); - members.add(conferenceParticipant(participant, participant.isOwner())); + if (activeConferenceCall.mData.mParticipantArray != null) { + for (int index = 0; index < activeConferenceCall.mData.mParticipantArray.size(); index++) { + NYNCallParticipant participant = activeConferenceCall.mData.mParticipantArray.get(index); + members.add(conferenceParticipant(participant, participant.isOwner(), activeConferenceCall.mData.isOwnStreamActive)); } } else { for (int index = 0; index < activeConferenceCall.getMembers().size(); index++) { @@ -334,66 +347,84 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter @Override public void onCreateVideoCallReady(ActiveConferenceCall activeConferenceCall) { + if (getAttachedViews().size() == 0) return; getViewState().onCreateVideoCallReady(activeConferenceCall); } @Override public void onScreenShareState(boolean active) { + if (getAttachedViews().size() == 0) return; getViewState().onScreenShareStateChanged(active && isScreenSharing()); } + @Override public void onStartCapturerFailed(int msgResId, boolean isCamera) { + if (getAttachedViews().size() == 0) return; + getViewState().onStartCapturerFailed(msgResId, isCamera); + } + @Override public void onRemoteVideoTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) { + if (getAttachedViews().size() == 0) return; + if (activeConferenceCall == null) return; + if (activeConferenceCall.isConference()) return; getViewState().onRemoteVideoTrackAdded(activeConferenceCall, trackId); } @Override public void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) { + if (getAttachedViews().size() == 0) return; + if (activeConferenceCall == null) return; + if (activeConferenceCall.isConference()) return; getViewState().onRemoteVideoTrackRemoved(activeConferenceCall, trackId); } @Override public void onLocalVideoCapturerStarted(ActiveConferenceCall activeConferenceCall) { + if (getAttachedViews().size() == 0) return; getViewState().onLocalVideoCapturerStarted(activeConferenceCall); } @Override public void onLocalVideoCapturerStopped(ActiveConferenceCall activeConferenceCall) { + if (getAttachedViews().size() == 0) return; getViewState().onLocalVideoCapturerStopped(activeConferenceCall); } @Override public void onMicrophoneStateChanged(boolean isMuted) { + if (getAttachedViews().size() == 0) return; getViewState().onMicrophoneStateChanged(isMuted); } @Override public void onSpeakerStateChanged(boolean isSpeakerOn) { + if (getAttachedViews().size() == 0) return; getViewState().onSpeakerStateChanged(isSpeakerOn); } @Override public void onAudioRouteChange(ActiveCallBase.AudioRouteType audioRouteType) { + if (getAttachedViews().size() == 0) return; getViewState().onAudioRouteChange(audioRouteType); } @Override public void tryCallCameraStateChange(boolean pause) { - if (getViewState() == null) return; + if (getAttachedViews().size() == 0) return; getViewState().tryCallCameraStateChange(pause); } @Override - public void onActiveSpeakersUpdate(String activeSpeakers) { - if (getViewState() == null) return; + public void onActiveSpeakersUpdate(String activeSpeakers, ArrayList participantIds) { + if (getAttachedViews().size() == 0) return; getViewState().onActiveSpeakersUpdate(activeSpeakers); } @Override public void onMuteStateChangedByHost(boolean isMuted) { - if (getViewState() == null) return; + if (getAttachedViews().size() == 0) return; int stringResId = isMuted ? R.string.call_mute_by_host : R.string.call_unmute_by_host; getViewState().showInfoBanner(stringResId); getViewState().updateMicrophonState(isMuted); @@ -412,6 +443,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter } public void onClickScreenShare(boolean isShareOn) { + if (getAttachedViews().size() == 0) return; ActiveConferenceCall call = mDataManager.getConferenceSDK().getActiveConference(); if (call != null && !call.isModerator() && !mDataManager.getConferenceSDK().isP2P()) { if (!isScreenSharing() && hasRemoteScreenShare()) { @@ -439,10 +471,10 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter @Nullable private ArrayList getConferenceParticipants() { if (mDataManager.getConferenceSDK(). - getActiveConference().mParticipantArray != null) { + getActiveConference().mData.mParticipantArray != null) { ArrayList currentContacts = new ArrayList<>(); long count = mDataManager.getConferenceSDK(). - getActiveConference().mParticipantArray.size(); + getActiveConference().mData.mParticipantArray.size(); String roomId = mDataManager.getConferenceSDK().getConferenceRoomId(); RoomModel room = mDataManager.getRoomById(roomId); List membersList = new ArrayList<>(); @@ -452,7 +484,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter for (int index = 0; index < count; index++) { NYNCallParticipant participant = mDataManager.getConferenceSDK(). - getActiveConference().mParticipantArray.get(index); + getActiveConference().mData.mParticipantArray.get(index); if (!participant.isMe()) { String memberAddress = participant.getAddress(); if (memberAddress == null) continue; @@ -515,7 +547,9 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter } if (type != null) { - getViewState().closenConference(); + if (getAttachedViews().size() > 0) { + getViewState().closenConference(); + } FragmentTransferObject fto = new FragmentTransferObject(currentContacts, room, type); mRouter.backOrNavigateTo(HomeNavigator.CHOOSE_USER, fto); } @@ -562,6 +596,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter } public void showInviteDialog() { + if (getAttachedViews().size() == 0) return; if (mDataManager.getConferenceSDK().isConferenceModerator()) { int addParticipantStringResId = isCallCreatedFromHome() ? R.string.contacts_contacts : R.string.group_participants; getViewState().showInviteOptionsModerator(addParticipantStringResId); @@ -575,6 +610,7 @@ public class ConferenceCallPresenter extends ConferenceSDKPresenter } public void copyLinkToClipboard() { + if (getAttachedViews().size() == 0) return; getViewState().showCopyLinkMessage(); } diff --git a/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/ConferenceVideoPresenter.java b/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/ConferenceVideoPresenter.java new file mode 100644 index 0000000000000000000000000000000000000000..93ff6234b0e3ebb4d30a1f03899aace87af3a940 --- /dev/null +++ b/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/ConferenceVideoPresenter.java @@ -0,0 +1,206 @@ +package com.nynja.mobile.communicator.mvp.presenters; + +import com.arellomobile.mvp.InjectViewState; +import com.nynja.mobile.communicator.data.FragmentTransferObject; +import com.nynja.mobile.communicator.data.conference.ConferenceVideoModule; +import com.nynja.mobile.communicator.data.models.nynjamodels.ContactModel; +import com.nynja.mobile.communicator.data.models.nynjamodels.RoomModel; +import com.nynja.mobile.communicator.data.sdk.ConferenceSDKPresenter; +import com.nynja.mobile.communicator.data.sdk.calls.ActiveConferenceCall; +import com.nynja.mobile.communicator.data.sdk.calls.ConferenceListItem; +import com.nynja.mobile.communicator.data.sdk.calls.ConferenceSDKListener; +import com.nynja.mobile.communicator.mvp.view.ConferenceVideoView; +import com.nynja.mobile.communicator.utils.navigation.navigators.HomeNavigator; +import com.nynja.sdk.NYNCallParticipant; + +import java.util.ArrayList; + +@InjectViewState +public class ConferenceVideoPresenter extends ConferenceSDKPresenter + implements ConferenceSDKListener { + + @Override + protected void onFirstViewAttach() { + super.onFirstViewAttach(); + ActiveConferenceCall activeConferenceCall = mDataManager.getConferenceSDK(). + getActiveConference(); + if (activeConferenceCall != null) { + getViewState().initConferenceStates(activeConferenceCall); + } + } + + public void removeFromCall(ConferenceListItem item) { + if (item != null) { + mDataManager.getConferenceSDK().removeParticipant(item.memberId, item.participantId); + } + } + + public void muteParticipant(ConferenceListItem item) { + if (!item.isMe) { + mDataManager.getConferenceSDK().muteParticipant(item.participantId); + } else { + mDataManager.getConferenceSDK().mute(); + } + } + + public void unMuteParticipant(String participantId) { + mDataManager.getConferenceSDK().unMuteParticipant(participantId); + } + + public void stopParticipantSS(ConferenceListItem item) { + if (!item.isMe) { + mDataManager.getConferenceSDK().stopParticipantSS(item.participantId); + } else { + mDataManager.getConferenceSDK().stopScreenCapture(); + } + } + + public void stopParticipantVideo(ConferenceListItem item) { + if (!item.isMe) { + mDataManager.getConferenceSDK().stopParticipantVideo(item.participantId); + } else { + mDataManager.getConferenceSDK().stopCameraCapture(); + } + } + + public void mute() { + mDataManager.getConferenceSDK().mute(); + } + + public void endCall() { + mDataManager.getConferenceSDK().endCall(); + } + + public boolean isConferenceModerator() { + return mDataManager.getConferenceSDK().isConferenceModerator(); + } + + public void showProfile(ConferenceListItem item) { + if (getAttachedViews().size() == 0) return; + getViewState().closenConference(); + if (!item.isMe) { + ContactModel contact = mDataManager.getContactByChatId(item.phoneId); + if (contact == null) { + contact = new ContactModel(); + contact.phoneId = item.phoneId; + } + mRouter.backOrNavigateTo(HomeNavigator.USER_PROFILE, contact); + } else { + mRouter.backToRoot(); +// mRouter.backOrNavigateTo(HomeNavigator.HOME); //todo my profile ?? + } + } + + public void openChatWithParticipant(String participantPhoneId) { + ContactModel contactByChatId = mDataManager.getContactByChatId(participantPhoneId); + if (contactByChatId != null) { + if (getAttachedViews().size() == 0) return; + getViewState().closenConference(); + RoomModel room = new RoomModel(contactByChatId); + FragmentTransferObject fto = new FragmentTransferObject(room, null); + navigateToChat(fto, FragmentTransferObject.Type.P2pList); + } + } + + public void loadConferenceParticipants(ActiveConferenceCall activeConferenceCall) { + onConferenceMembersUpdate(activeConferenceCall); + } + + public void updateActiveSpeakersState(ArrayList participantIds) { + if (getAttachedViews().size() == 0) return; + ArrayList members = ConferenceVideoModule + .getInstance().getUpdatedActiveSpeakersParticipants(participantIds); + getViewState().onConferenceMembersUpdate(members); + } + + @Override + public void onConferenceMembersUpdate(ActiveConferenceCall activeConferenceCall) { + if (getAttachedViews().size() == 0) return; + ArrayList members = getConferenceVideoMembers(activeConferenceCall); + getViewState().onConferenceMembersUpdate(members); + } + + @Override + public void onScreenShareState(boolean active) { + if (getAttachedViews().size() == 0) return; + getViewState().onScreenShareStateChanged(active && isScreenSharing()); + } + + @Override + public void onRemoteVideoTrackAdded(ActiveConferenceCall activeConferenceCall, + String trackId) { + if (getAttachedViews().size() == 0) return; + getViewState().onRemoteVideoTrackAdded(activeConferenceCall, trackId); + } + + @Override + public void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, + String trackId) { + getViewState().onRemoteVideoTrackRemoved(activeConferenceCall, trackId); + } + + @Override + public void onLocalVideoCapturerStarted(ActiveConferenceCall activeConferenceCall) { + if (getAttachedViews().size() == 0) return; + getViewState().onLocalVideoCapturerStarted(activeConferenceCall); + } + + @Override + public void onLocalVideoCapturerStopped(ActiveConferenceCall activeConferenceCall) { + if (getAttachedViews().size() == 0) return; + getViewState().onLocalVideoCapturerStopped(activeConferenceCall); + } + + @Override + public void onMicrophoneStateChanged(boolean isMuted) { + if (getAttachedViews().size() == 0) return; + getViewState().onMicrophoneStateChanged(isMuted); + } + + @Override + public void onSpeakerStateChanged(boolean isSpeakerOn) { + if (getAttachedViews().size() == 0) return; + getViewState().onSpeakerStateChanged(isSpeakerOn); + } + + @Override + public void onActiveSpeakersUpdate(String activeSpeakers, ArrayList participantIds) { + if (getAttachedViews().size() == 0) return; + getViewState().onActiveSpeakersUpdate(activeSpeakers, participantIds); + } + + @Override + public void onMuteStateChangedByHost(boolean isMuted) { + if (getAttachedViews().size() == 0) return; + getViewState().updateMicrophonState(isMuted); + } + + + public void callBackPerson(ConferenceListItem item) { + mDataManager.getConferenceSDK().callBackMember(item.memberId, item.phoneId, item.name); + } + + public void pauseCallVideoCapturer() { + mDataManager.getConferenceSDK().setCallCameraPauseState(true); + } + + public void resumeCallVideoCapturer() { + mDataManager.getConferenceSDK().setCallCameraPauseState(false); + } + + public ArrayList getConferenceVideoMembers(ActiveConferenceCall activeConferenceCall) { + return ConferenceVideoModule.getInstance().getConferenceVideoMembers(activeConferenceCall); + } + + + public void switchCamera() { + if (getAttachedViews().size() == 0) return; + ActiveConferenceCall activeConferenceCall = mDataManager.getConferenceSDK(). + getActiveConference(); + if (activeConferenceCall != null && activeConferenceCall.mConference != null) { + activeConferenceCall.mConference.switchCamera(); + } + } + +} + diff --git a/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/MainActivityPresenter.kt b/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/MainActivityPresenter.kt index 720b14e1ec4b6b1764cb175a3b060d54c99fb316..923269e60c835f11fd894ff153ce673b40f38637 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/MainActivityPresenter.kt +++ b/app/src/main/java/com/nynja/mobile/communicator/mvp/presenters/MainActivityPresenter.kt @@ -364,8 +364,16 @@ class MainActivityPresenter : ConferenceSDKPresenter() { } HomeActions.VideoCallAction -> { - viewState.showAlertNotSupportedFeature() -// tryStartCall(true, true, true, null) +// viewState.showAlertNotSupportedFeature() +// val fto = FragmentTransferObject(null, +// FragmentTransferObject.Type.OpenGroupVideoConferenceCreate, +// null) +// mRouter.navigateTo(HomeNavigator.CHOOSE_USER, fto) + if (Consts.CLIENT_ALLOWED_VIDEO_CONFERENCE) { + tryStartCall(true, true, true, null) + } else { + viewState.showAlertMessage(R.string.call_start_video_conference_alert) + } viewState.closeWheel() } diff --git a/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallActivityView.java b/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallActivityView.java index 4769faf639d3d877b981b82a0838f11abbf86f4c..2ffd3c3131984c1da4d6b928efec1fa6e6293908 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallActivityView.java +++ b/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallActivityView.java @@ -18,6 +18,14 @@ public interface CallActivityView extends ErrorMvpView { void onRemoteScreenShareTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) ; + void onRemoteVideoTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId); + + void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId); + + void onLocalVideoCapturerStarted(ActiveConferenceCall activeConferenceCall); + + void onLocalVideoCapturerStopped(ActiveConferenceCall activeConferenceCall); + void showPurchaseDialog(boolean isModerator, final String message); void closeActivity(); diff --git a/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallTapToSpeakView.java b/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallTapToSpeakView.java index 8d39df5902d93c5355f6e21a9d8ec13d1571ec60..2f87ef02936c472c23948bb3063edeabafb6e755 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallTapToSpeakView.java +++ b/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallTapToSpeakView.java @@ -1,6 +1,6 @@ package com.nynja.mobile.communicator.mvp.view; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; /** * Created by Ergyun Syuleyman on 9/14/18. diff --git a/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallView.java b/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallView.java index d891294e743879f7f47fe07bf611cf2605ea2ee8..d718d095ffe79e3494a69ddd827a6afee6314c79 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallView.java +++ b/app/src/main/java/com/nynja/mobile/communicator/mvp/view/CallView.java @@ -2,7 +2,7 @@ package com.nynja.mobile.communicator.mvp.view; import android.support.annotation.StringRes; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; import com.nynja.mobile.communicator.data.sdk.calls.ActiveConferenceCall; import com.nynja.mobile.communicator.data.sdk.calls.ConferenceListItem; import com.nynja.mobile.communicator.data.models.nynjamodels.ContactModel; @@ -42,6 +42,8 @@ public interface CallView extends ErrorMvpView { void onCreateVideoCallReady(ActiveConferenceCall activeConferenceCall); + void onStartCapturerFailed(int msgResId, boolean isCamera); + void onRemoteVideoTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) ; void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) ; diff --git a/app/src/main/java/com/nynja/mobile/communicator/mvp/view/ConferenceVideoView.java b/app/src/main/java/com/nynja/mobile/communicator/mvp/view/ConferenceVideoView.java new file mode 100644 index 0000000000000000000000000000000000000000..50d0d40f89a6e81ba154fdc38b068836ccdf2538 --- /dev/null +++ b/app/src/main/java/com/nynja/mobile/communicator/mvp/view/ConferenceVideoView.java @@ -0,0 +1,35 @@ +package com.nynja.mobile.communicator.mvp.view; + +import com.nynja.mobile.communicator.data.sdk.calls.ActiveConferenceCall; +import com.nynja.mobile.communicator.data.sdk.calls.ConferenceListItem; + +import java.util.ArrayList; + +public interface ConferenceVideoView extends ErrorMvpView { + + void initConferenceStates(ActiveConferenceCall activeConferenceCall); + + void onConferenceMembersUpdate(ArrayList members); + + void onRemoteVideoTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) ; + + void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) ; + + void onLocalVideoCapturerStarted(ActiveConferenceCall activeConferenceCall) ; + + void onLocalVideoCapturerStopped(ActiveConferenceCall activeConferenceCall) ; + + void onScreenShareStateChanged(boolean isSharing); + + void onMicrophoneStateChanged(boolean isMuted); + + void onSpeakerStateChanged(boolean isSpeakerOn); + + void onActiveSpeakersUpdate(String activeSpeakers, ArrayList participantIds); + + void updateMicrophonState(boolean isMuted); + + void closenConference(); + +} + diff --git a/app/src/main/java/com/nynja/mobile/communicator/mvp/view/MainActivityView.java b/app/src/main/java/com/nynja/mobile/communicator/mvp/view/MainActivityView.java index e2b7a3e30956aa0e99f7ee7869e3d587f7e56fbf..4ecb0e41d9a0887eab0044780ce2142157d5960c 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/mvp/view/MainActivityView.java +++ b/app/src/main/java/com/nynja/mobile/communicator/mvp/view/MainActivityView.java @@ -74,4 +74,6 @@ public interface MainActivityView extends JoinGroupView { void showPurchaseDialog(boolean isModerator, final String message); void tryStartCall(boolean createNew, boolean isGroup, boolean isVideo, Parcelable prevModel); + + void showAlertMessage(int res); } diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/activities/MainActivity.java b/app/src/main/java/com/nynja/mobile/communicator/ui/activities/MainActivity.java index 5ecff4d479cb1ec6ba94c835ed023fd7d9785e98..cc4a659d4112f93443722db0a4b11e8a5bc7a3da 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/activities/MainActivity.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/activities/MainActivity.java @@ -629,17 +629,17 @@ public class MainActivity extends BaseActivity implements MainActivityView, chatAudioLayout.setVisibility(View.VISIBLE); mPresenter.requestUser(); //TODO: video feeds support in next features - //if (!activeConferenceCall.isOwnStreamActive || !activeConferenceCall.hasRemoteVideoTrack) { + //if (!activeConferenceCall.mData.isOwnStreamActive || !activeConferenceCall.hasRemoteVideoTrack) { // video.setVisibility(View.GONE); // chatAudioLayout.setVisibility(View.VISIBLE); // mPresenter.requestUser(); // if (activeConferenceCall.mConference != null) - // activeConferenceCall.mConference.setRemoteVideoRenderer(video); + // activeConferenceCall.mConference.setVideoRendererForTrack(video); //} else { // video.setVisibility(View.VISIBLE); // chatAudioLayout.setVisibility(View.GONE); // if (activeConferenceCall.mConference != null) { - // activeConferenceCall.mConference.setRemoteVideoRenderer(null); + // activeConferenceCall.mConference.setVideoRendererForTrack(null); // } //} } @@ -668,6 +668,14 @@ public class MainActivity extends BaseActivity implements MainActivityView, onCallPermission(false, isVideo, createNew, isGroup, prevModel); } + @Override + public void showAlertMessage(int res) { + DialogFactory.showAlertDialog(this, + getString(res), + getString(R.string.signin_text_ok), + (dialog, which) -> dialog.dismiss()); + } + @Override public void openActiveConference() { mPresenter.navigateToActiveCall(); diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/activities/calls/CallActivity.java b/app/src/main/java/com/nynja/mobile/communicator/ui/activities/calls/CallActivity.java index 4346ba422c430ff9f0cad7263f23b2a0b7a189ac..f8f34f2abd68951c176cda409e577528c8be2b77 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/activities/calls/CallActivity.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/activities/calls/CallActivity.java @@ -5,26 +5,22 @@ import android.app.Activity; import android.bluetooth.BluetoothAdapter; import android.bluetooth.BluetoothDevice; import android.bluetooth.BluetoothHeadset; -import android.content.ActivityNotFoundException; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.content.res.Configuration; import android.media.projection.MediaProjectionManager; -import android.net.Uri; import android.os.Build; import android.os.Bundle; -import android.provider.Settings; import android.support.v4.view.ViewPager; -import android.widget.LinearLayout; import com.arellomobile.mvp.presenter.InjectPresenter; import com.nynja.mobile.communicator.R; import com.nynja.mobile.communicator.data.sdk.calls.ActiveConferenceCall; import com.nynja.mobile.communicator.mvp.presenters.CallActivityPresenter; import com.nynja.mobile.communicator.mvp.view.CallActivityView; -import com.nynja.mobile.communicator.ui.adapters.ActiveCallPagerAdapter; +import com.nynja.mobile.communicator.ui.adapters.conference.ActiveCallPagerAdapter; import com.nynja.mobile.communicator.ui.base.BaseActivity; import com.nynja.mobile.communicator.ui.views.CirclePageIndicator; import com.nynja.mobile.communicator.ui.views.CustomViewPager; @@ -35,6 +31,7 @@ import org.webrtc.ContextUtils; import org.webrtc.VideoSourceUtility; import butterknife.BindView; +import kotlin.jvm.Synchronized; import ru.terrakok.cicerone.Navigator; import timber.log.Timber; @@ -42,10 +39,11 @@ public class CallActivity extends BaseActivity implements CallActivityView { private static final int CAPTURE_PERMISSION_REQUEST_CODE = 1; - public boolean isScreenShareActive; @BindView(R.id.active_call_viewpager) CustomViewPager mViewPager; @BindView(R.id.active_call_indicator) CirclePageIndicator mIndicator; +// @BindView(R.id.active_call_rv) RecyclerView mRecyclerView; + @InjectPresenter CallActivityPresenter mPresenter; public interface BluetoothConnectivityListener { @@ -56,6 +54,7 @@ public class CallActivity extends BaseActivity implements CallActivityView { private ActiveCallPagerAdapter mPagerAdapter; + private int mSelectedPage = -1; public static Intent getLaunchIntent(Context context) { Intent intent = new Intent(context, CallActivity.class); @@ -76,6 +75,12 @@ public class CallActivity extends BaseActivity implements CallActivityView { mPresenter.bluetoothConnected(); } + initUI(); + + registerBluetoothReceiver(); + } + + private void initUI() { mPagerAdapter = new ActiveCallPagerAdapter(this); mViewPager.setAdapter(mPagerAdapter); addPageViewListener(); @@ -83,7 +88,27 @@ public class CallActivity extends BaseActivity implements CallActivityView { mIndicator.setSaveEnabled(false); mIndicator.setViewPager(mViewPager, ActiveCallPagerAdapter.CallPages.CallPageActiveCallFragment.ordinal()); mIndicator.notifyDataSetChanged(); - registerBluetoothReceiver(); + } + + private void initUIRv() { +// final DefaultItemAnimator itemAnimator = new DefaultItemAnimator(); +// itemAnimator.setSupportsChangeAnimations(false); +// mRecyclerView.setItemAnimator(itemAnimator); +// mRecyclerView.setNestedScrollingEnabled(false); +// mRecyclerView.setLayoutManager(new GridLayoutManager(this, 4)); +// mRecyclerView.setHasFixedSize(true); +// //mRecyclerView.setAdapter(ActiveCallAdapter); +// mRecyclerView.addItemDecoration(new CirclePagerIndicatorDecoration(R.dimen.rv_indicator_radius, +// R.dimen.rv_indicator_padding, +// R.dimen.rv_indicator_height, +// R.color.wheel_unselected_corner_bg, +// R.color.colorAccent)); +// new PagerSnapHelper().attachToRecyclerView(mRecyclerView); + } + + public boolean isScreenShareActive() { + if (mPagerAdapter != null) return mPagerAdapter.isScreenShareActive; + return false; } @Override @@ -93,39 +118,48 @@ public class CallActivity extends BaseActivity implements CallActivityView { mPagerAdapter.onConfigurationChanged(newConfig, mViewPager.getCurrentItem()); } - protected void addPageViewListener() { - if (mViewPager == null) return; - mViewPager.addOnPageChangeListener(new ViewPager.OnPageChangeListener() { - @Override - public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { - Timber.d("CallActivity::onPageScrolled(): position=%d, positionOffset=%f, " + - "positionOffsetPixels=%d", position, positionOffset, positionOffsetPixels); - } + protected ViewPager.OnPageChangeListener mOnPageChangeListener = new ViewPager.OnPageChangeListener() { + @Override + public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) { + Timber.d("CallActivity::onPageScrolled(): position=%d, positionOffset=%f, " + + "positionOffsetPixels=%d", position, positionOffset, positionOffsetPixels); + } - @Override - public void onPageSelected(int position) { - Timber.d("CallActivity::onPageSelected(): page=%d", position); + @Override + public void onPageSelected(int position) { + Timber.d("CallActivity::onPageSelected(): page=%d", position); // if (position == ActiveCallPagerAdapter.CallPages.CallPageActiveCallFragment.ordinal()) { // mIndicator.setVisibility(View.GONE); // } else { // mIndicator.setVisibility(View.VISIBLE); // } - synchronized (mPagerAdapter) { - mPagerAdapter.setFragmentActiveByPosition(position); + synchronized (mPagerAdapter) { +// mPagerAdapter.setFragmentActiveByPosition(position); + if (mSelectedPage >= 0) { + mPagerAdapter.setFragmentActiveByPosition(mSelectedPage, false); } + mPagerAdapter.setFragmentActiveByPosition(position, true); + mSelectedPage = position; } + } - @Override - public void onPageScrollStateChanged(int state) { - Timber.d("CallActivity::onPageScrollStateChanged(): state=%d", state); + @Override + public void onPageScrollStateChanged(int state) { + Timber.d("CallActivity::onPageScrollStateChanged(): state=%d", state); - } - }); + } + }; + + protected void addPageViewListener() { + if (mViewPager == null) return; + mViewPager.addOnPageChangeListener(mOnPageChangeListener); } - public void showShareScreen(boolean show) { - isScreenShareActive = show; + protected void showShareScreen(boolean show) { + int currentItem = mIndicator.getCurrentItem(); + ActiveCallPagerAdapter.CallPages page = mPagerAdapter.getPageByPosition(currentItem); synchronized (mPagerAdapter) { + mViewPager.removeOnPageChangeListener(mOnPageChangeListener); mViewPager.setAdapter(null); if (show) { mPagerAdapter.showShareScreen(); @@ -133,11 +167,12 @@ public class CallActivity extends BaseActivity implements CallActivityView { mPagerAdapter.hideShareScreen(); } + mViewPager.addOnPageChangeListener(mOnPageChangeListener); mViewPager.setAdapter(mPagerAdapter); } // addPageViewListener(); // set to position 1 - mIndicator.setViewPager(mViewPager, ActiveCallPagerAdapter.CallPages.ShareScreenFragment.ordinal()); + mIndicator.setViewPager(mViewPager, mPagerAdapter.getFragmentPositionByType(page)); mIndicator.notifyDataSetChanged(); } @@ -149,9 +184,11 @@ public class CallActivity extends BaseActivity implements CallActivityView { @Override protected void onDestroy() { unregisterReceiver(mBluetoothReceiver); - releaseWakeLockActivity(); - mViewPager.removeAllViews(); mPagerAdapter.reset(); + mViewPager.removeAllViews(); + ContextUtils.releaseRootEglBase(); + ContextUtils.releaseSSEglBase(); + releaseWakeLockActivity(); super.onDestroy(); } @@ -181,6 +218,10 @@ public class CallActivity extends BaseActivity implements CallActivityView { return mIndicator; } + public void goToPage(ActiveCallPagerAdapter.CallPages page) { + mIndicator.setCurrentItem(mPagerAdapter.getFragmentPositionByType(page)); + } + @TargetApi(21) private void prepareScreenCapture() { MediaProjectionManager mediaProjectionManager = @@ -210,8 +251,10 @@ public class CallActivity extends BaseActivity implements CallActivityView { @Override public void initConferenceStates(ActiveConferenceCall activeConferenceCall) { - if (activeConferenceCall != null && activeConferenceCall.hasRemoteScreenShareTrack) { - showShareScreen(true); + if (activeConferenceCall == null) return; + if (mPagerAdapter != null) { + mPagerAdapter.initFlags(activeConferenceCall.mData.hasRemoteScreenShareTrack, + (activeConferenceCall.mData.hasRemoteVideoTrack() || activeConferenceCall.mData.isOwnStreamActive)); } } @@ -302,24 +345,104 @@ public class CallActivity extends BaseActivity implements CallActivityView { @Override public void onRemoteScreenShareTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) { - final boolean hasRemoteScreenShareTrack = (activeConferenceCall != null && activeConferenceCall.hasRemoteScreenShareTrack); - runOnUiThread(() -> { + final boolean hasRemoteScreenShareTrack = (activeConferenceCall != null && activeConferenceCall.mData.hasRemoteScreenShareTrack); + mHandler.postDelayed(() -> { if (hasRemoteScreenShareTrack) { showShareScreen(true); } - }); + }, Consts.DELAY_100); } @Override public void onRemoteScreenShareTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) { - runOnUiThread(() -> { + mHandler.postDelayed(() -> { showShareScreen(false); - }); + }, Consts.DELAY_100); + } + + @Override + public void onRemoteVideoTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) { + mHandler.postDelayed(() -> { + tryUpdateConferenceVideoScreen(activeConferenceCall, true, false); + }, Consts.DELAY_100); + } + + @Override + public void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) { + mHandler.postDelayed(() -> { + tryUpdateConferenceVideoScreen(activeConferenceCall, false, false); + }, Consts.DELAY_100); } + @Override + public void onLocalVideoCapturerStarted(ActiveConferenceCall activeConferenceCall) { + mHandler.postDelayed(() -> { + tryUpdateConferenceVideoScreen(activeConferenceCall, true, true); + }, Consts.DELAY_100); + } + + @Override + public void onLocalVideoCapturerStopped(ActiveConferenceCall activeConferenceCall) { + mHandler.postDelayed(() -> { + tryUpdateConferenceVideoScreen(activeConferenceCall, false, true); + }, Consts.DELAY_200); + } @Override public Navigator getActivityNavigator() { return null; } + @Synchronized + private void tryUpdateConferenceVideoScreen(ActiveConferenceCall activeConferenceCall, + boolean show, boolean isMyVideo) { + if (activeConferenceCall == null) { + return; + } + if (!activeConferenceCall.isConference()) { + return; + } + int currentItem = mIndicator.getCurrentItem(); + ActiveCallPagerAdapter.CallPages page = mPagerAdapter.getPageByPosition(currentItem); + boolean goToMyFeed = (show && isMyVideo && page != ActiveCallPagerAdapter.CallPages.ConferenceVideoFragment); + if (show && mPagerAdapter.isConferenceVideoActive) { + if (goToMyFeed) { + mPagerAdapter.tryToGoTooMyVideoFeed(); + goToPage(ActiveCallPagerAdapter.CallPages.ConferenceVideoFragment); + } + return; + } + + boolean updated = false; + synchronized (mPagerAdapter) { + if (show && (activeConferenceCall.mData.hasRemoteVideoTrack() || activeConferenceCall.mData.isOwnStreamActive)) { + mViewPager.removeOnPageChangeListener(mOnPageChangeListener); + mViewPager.setAdapter(null); + mPagerAdapter.showConferenceVideoScreen(); + mViewPager.addOnPageChangeListener(mOnPageChangeListener); + mViewPager.setAdapter(mPagerAdapter); + updated = true; + } else if (!activeConferenceCall.mData.hasRemoteVideoTrack() && !activeConferenceCall.mData.isOwnStreamActive){ + mViewPager.removeOnPageChangeListener(mOnPageChangeListener); + mViewPager.setAdapter(null); + mPagerAdapter.hideConferenceVideoScreen(); + mViewPager.addOnPageChangeListener(mOnPageChangeListener); + mViewPager.setAdapter(mPagerAdapter); + updated = true; + } + } + // set to position 1 + if (updated) { + + if (goToMyFeed) { + page = ActiveCallPagerAdapter.CallPages.ConferenceVideoFragment; + } + mIndicator.setViewPager(mViewPager, mPagerAdapter.getFragmentPositionByType(page)); + mIndicator.notifyDataSetChanged(); + + mHandler.postDelayed(() -> { + mPagerAdapter.tryToGoTooMyVideoFeed(); + }, Consts.DELAY_200); + } + } + } diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ChatLanguageAdapter.kt b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ChatLanguageAdapter.kt index fa8edda924bf9e0eb9f9a402bd51526a050b3b57..7425f187a6d97c071053afecb6b0bdbc46cd249a 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ChatLanguageAdapter.kt +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ChatLanguageAdapter.kt @@ -38,7 +38,7 @@ class ChatLanguageAdapter(list: List, val onItemClickListener: On } override fun onBindHeaderViewHolder(holder: CountryPickerLetterViewHolder, position: Int) { - holder.bind(getCellChar(position).toString()) + holder.bind(getCellChar(position).toString(), position) } private fun getCellChar(position: Int): Char { diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ChooseUserVerticalAdapter.java b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ChooseUserVerticalAdapter.java index 1fdc9cc31d4aeac978c78540b63b104a4d583dfe..1a7a4f64ed57e84b2b4000411cf3fdc71d49e33c 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ChooseUserVerticalAdapter.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ChooseUserVerticalAdapter.java @@ -71,7 +71,7 @@ public class ChooseUserVerticalAdapter extends BaseSelectableModelAdapter contacts) { diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ContactsAdapter.java b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ContactsAdapter.java index 355bf7891b01e34cce2a02ff6b94661af9e8fc70..1620eccd6eba59c3bf177a0db4d50ca1a4f9958e 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ContactsAdapter.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ContactsAdapter.java @@ -46,7 +46,7 @@ public class ContactsAdapter extends BaseAdapter implem } @Override public void onBindHeaderViewHolder(ContactHeaderVh holder, int position) { - holder.bind(String.valueOf(getCellChar(position))); + holder.bind(String.valueOf(getCellChar(position)), position); } private char getCellChar(int position) { diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/CountryPickerAdapter.java b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/CountryPickerAdapter.java index 147cd34646c272cc954625b3a0b52449e041b4c2..660b7389c46e59d4a9198388bb2cb6dd7a2ed79a 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/CountryPickerAdapter.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/CountryPickerAdapter.java @@ -50,7 +50,7 @@ public class CountryPickerAdapter extends BaseAdapter mAdapterPages = new HashMap<>(); private List callPages = new ArrayList<>(); + public boolean isScreenShareActive = false; + public boolean isConferenceVideoActive = false; private CallActivity callActivity; + public enum CallPages { + CallPageTapToSpeak, + ShareScreenFragment, + CallPageActiveCallFragment, + ConferenceVideoFragment + + } + + public ActiveCallPagerAdapter(CallActivity callActivity) { + super(callActivity); + this.callActivity = callActivity; + preparePages(); + } + + public void initFlags(boolean isScreenShareActive, boolean isConferenceVideoActive) { + this.isScreenShareActive = isScreenShareActive; + this.isConferenceVideoActive = isConferenceVideoActive; + preparePages(); + } + + @Synchronized public void reset() { for (CallPages callPage : CallPages.values()) { final String itemId = makeFragmentId(getItemId(callPage.ordinal())); @@ -39,40 +64,65 @@ public class ActiveCallPagerAdapter extends NynjaViewPagerAdapter { } this.callActivity = null; callPages.clear(); + notifyDataSetChanged(); } - public void hideShareScreen() { - final String itemId = makeFragmentId(getItemId(CallPages.ShareScreenFragment.ordinal())); + @Synchronized + private void preparePages() { + + for (CallPages callPage : callPages) { + if (callPage == CallPages.ShareScreenFragment && !isScreenShareActive) { + final String itemId = makeFragmentId(getItemId(callPage.ordinal())); + BaseFragment fragment = mAdapterPages.get(itemId); + if (fragment != null) { + destroyView(fragment); + } + mAdapterPages.remove(itemId); + } +// if (callPage == CallPages.ConferenceVideoFragment && !isConferenceVideoActive) { +// final String itemId = makeFragmentId(getItemId(callPage.ordinal())); +// BaseFragment fragment = mAdapterPages.get(itemId); +// if (fragment != null) { +// destroyView(fragment); +// } +// mAdapterPages.remove(itemId); +// } + } callPages.clear(); for (CallPages callPage : CallPages.values()) { - if (callPage == CallPages.ShareScreenFragment) { + if (callPage == CallPages.ShareScreenFragment && !isScreenShareActive) { + continue; + } + if (callPage == CallPages.ConferenceVideoFragment && !isConferenceVideoActive) { continue; } callPages.add(callPage); } - mAdapterPages.remove(itemId); notifyDataSetChanged(); } + public void hideShareScreen() { + isScreenShareActive = false; + preparePages(); + } + public void showShareScreen() { - callPages.clear(); - callPages.addAll(Arrays.asList(CallPages.values())); - notifyDataSetChanged(); + isScreenShareActive = true; + preparePages(); } - public enum CallPages { - CallPageTapToSpeak, - ShareScreenFragment, - CallPageActiveCallFragment + public void hideConferenceVideoScreen() { + isConferenceVideoActive = false; + preparePages(); } - public ActiveCallPagerAdapter(CallActivity callActivity) { - super(callActivity); - this.callActivity = callActivity; - hideShareScreen(); + public void showConferenceVideoScreen() { + isConferenceVideoActive = true; + preparePages(); } + @Synchronized public BaseFragment getItem(int position) { final String itemId = makeFragmentId(getItemId(position)); BaseFragment fragment = mAdapterPages.get(itemId); @@ -99,6 +149,12 @@ public class ActiveCallPagerAdapter extends NynjaViewPagerAdapter { ((ScreenShareFragment) fragment).setCallActivity(callActivity); ((ScreenShareFragment)fragment).setIsActive(false); } + } else if (callPages.get(position) == CallPages.ConferenceVideoFragment) { + fragment = new ConferenceVideoFragment(); + if (callActivity != null) { + ((ConferenceVideoFragment) fragment).setCallActivity(callActivity); + ((ConferenceVideoFragment)fragment).setIsActive(false); + } } // if (position == CallPages.CallPageTapToSpeak.ordinal()) { @@ -162,21 +218,30 @@ public class ActiveCallPagerAdapter extends NynjaViewPagerAdapter { return mAdapterPages.get(itemId); } - protected void setFragmentActiveByPosition(int position, boolean active) { + public void setFragmentActiveByPosition(int position, boolean active) { BaseFragment fragment = getItemByPosition(position); if (fragment == null) return; if (position == CallPages.CallPageTapToSpeak.ordinal() && fragment instanceof CallTapToSpeakFragment) { ((CallTapToSpeakFragment)fragment).setActiveState(active); - } else if (position == CallPages.ShareScreenFragment.ordinal() - && fragment instanceof ScreenShareFragment) { - ((ScreenShareFragment)fragment).setActiveState(active); - } else if (position == CallPages.CallPageActiveCallFragment.ordinal() - && fragment instanceof ConferenceCallFragment) { - ((ConferenceCallFragment)fragment).setActiveState(active); - } else if (position == CallPages.ShareScreenFragment.ordinal() - && fragment instanceof ConferenceCallFragment) { - ((ConferenceCallFragment)fragment).setActiveState(active); + } else if (position == CallPages.ShareScreenFragment.ordinal()) { + if (fragment instanceof ScreenShareFragment) { + ((ScreenShareFragment)fragment).setActiveState(active); + } else if (fragment instanceof ConferenceCallFragment) { + ((ConferenceCallFragment) fragment).setActiveState(active); + } else if (fragment instanceof ConferenceVideoFragment) { + ((ConferenceVideoFragment)fragment).setActiveState(active); + } + } else if (position == CallPages.CallPageActiveCallFragment.ordinal()) { + if (fragment instanceof ConferenceCallFragment) { + ((ConferenceCallFragment)fragment).setActiveState(active); + } else if (fragment instanceof ConferenceVideoFragment) { + ((ConferenceVideoFragment)fragment).setActiveState(active); + } + } else if (position == CallPages.ConferenceVideoFragment.ordinal()) { + if (fragment instanceof ConferenceVideoFragment) { + ((ConferenceVideoFragment)fragment).setActiveState(active); + } } } @@ -186,6 +251,27 @@ public class ActiveCallPagerAdapter extends NynjaViewPagerAdapter { } } + @Synchronized + public int getFragmentPositionByType(CallPages page) { + int index = 0; + for (CallPages p : callPages) { + if (p == page) { + return index; + } + ++index; + } + return page.ordinal(); + } + + @Synchronized + public CallPages getPageByPosition( int position) { + CallPages page = CallPages.CallPageActiveCallFragment; + if (position < callPages.size()) { + page = callPages.get(position); + } + return page; + } + public void onConfigurationChanged(Configuration newConfig, int position) { BaseFragment fragment = getItemByPosition(position); if (fragment == null) return; @@ -193,11 +279,46 @@ public class ActiveCallPagerAdapter extends NynjaViewPagerAdapter { && fragment instanceof CallTapToSpeakFragment) || (position == CallPages.ShareScreenFragment.ordinal() && fragment instanceof ScreenShareFragment) + || (position == CallPages.ShareScreenFragment.ordinal() + && fragment instanceof ConferenceVideoFragment) + || (position == CallPages.ShareScreenFragment.ordinal() + && fragment instanceof ConferenceCallFragment) || (position == CallPages.CallPageActiveCallFragment.ordinal() && fragment instanceof ConferenceCallFragment) - || (position == CallPages.ShareScreenFragment.ordinal() - && fragment instanceof ConferenceCallFragment)) { + || (position == CallPages.CallPageActiveCallFragment.ordinal() + && fragment instanceof ConferenceVideoFragment) + || (position == CallPages.ConferenceVideoFragment.ordinal() + && fragment instanceof ConferenceVideoFragment) + ) { fragment.onConfigurationChanged(newConfig); } } + + public void tryToGoTooMyVideoFeed() { + int position = 0; + for (; position < callPages.size(); position++ ) { + if (position == CallPages.ShareScreenFragment.ordinal()) { + final String itemId = makeFragmentId(getItemId(position)); + BaseFragment fragment = mAdapterPages.get(itemId); + if (fragment instanceof ConferenceVideoFragment) { + ((ConferenceVideoFragment)fragment).tryToGoTooMyVideoFeed();; + return; + } + } else if (position == CallPages.CallPageActiveCallFragment.ordinal()) { + final String itemId = makeFragmentId(getItemId(position)); + BaseFragment fragment = mAdapterPages.get(itemId); + if (fragment instanceof ConferenceVideoFragment) { + ((ConferenceVideoFragment)fragment).tryToGoTooMyVideoFeed();; + return; + } + } else if (position == CallPages.ConferenceVideoFragment.ordinal()) { + final String itemId = makeFragmentId(getItemId(position)); + BaseFragment fragment = mAdapterPages.get(itemId); + if (fragment instanceof ConferenceVideoFragment) { + ((ConferenceVideoFragment)fragment).tryToGoTooMyVideoFeed();; + return; + } + } + } + } } \ No newline at end of file diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ConferenceParticipantsAdapter.java b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/conference/ConferenceParticipantsAdapter.java similarity index 94% rename from app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ConferenceParticipantsAdapter.java rename to app/src/main/java/com/nynja/mobile/communicator/ui/adapters/conference/ConferenceParticipantsAdapter.java index 3f978282a5160313fe70242d748ffdb3deb5231d..f1e40fff287e6ab5169c06e7cbc485b8e02f502f 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/ConferenceParticipantsAdapter.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/conference/ConferenceParticipantsAdapter.java @@ -1,4 +1,4 @@ -package com.nynja.mobile.communicator.ui.adapters; +package com.nynja.mobile.communicator.ui.adapters.conference; import android.content.Context; import android.support.annotation.NonNull; @@ -10,7 +10,7 @@ import android.widget.ArrayAdapter; import com.nynja.mobile.communicator.R; import com.nynja.mobile.communicator.data.sdk.calls.ConferenceListItem; import com.nynja.mobile.communicator.interfaces.OnConferenceItemClickListener; -import com.nynja.mobile.communicator.ui.adapters.viewholders.ConferenceContactVh; +import com.nynja.mobile.communicator.ui.adapters.viewholders.conference.ConferenceContactVh; import java.util.ArrayList; diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/conference/ConferenceVideoAdapter.java b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/conference/ConferenceVideoAdapter.java new file mode 100644 index 0000000000000000000000000000000000000000..0956d049d755aed605485fe7afe2ad6796d11640 --- /dev/null +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/conference/ConferenceVideoAdapter.java @@ -0,0 +1,149 @@ +package com.nynja.mobile.communicator.ui.adapters.conference; + +import android.content.Context; +import android.graphics.Insets; +import android.support.annotation.NonNull; +import android.util.DisplayMetrics; +import android.util.Size; +import android.view.Display; +import android.view.ViewGroup; +import android.view.WindowManager; + +import com.nynja.mobile.communicator.NynjaApp; +import com.nynja.mobile.communicator.data.conference.ConferenceVideoModule; +import com.nynja.mobile.communicator.data.sdk.calls.ConferenceListItem; +import com.nynja.mobile.communicator.interfaces.OnConferenceVideoFeedClickListener; +import com.nynja.mobile.communicator.ui.activities.calls.CallActivity; +import com.nynja.mobile.communicator.ui.adapters.viewholders.conference.ConferenceVideoItemVh; +import com.nynja.mobile.communicator.ui.base.BaseAdapter; +import com.nynja.mobile.communicator.ui.base.BaseVH; + +import java.util.HashSet; +import java.util.List; + +import static com.nynja.mobile.communicator.data.conference.ConferenceVideoModule.MAX_VIDEO_FEEDS_COLUIMN_SIZE; +import static com.nynja.mobile.communicator.data.conference.ConferenceVideoModule.MAX_VIDEO_FEEDS_ROW_SIZE; + +public class ConferenceVideoAdapter> extends BaseAdapter { + + private OnConferenceVideoFeedClickListener mOnConferenceVideoFeedClickListener; + private boolean mIsModerator = false; + private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); + private int mScreenWidth = 0; + private int mScreenHeight = 0; + HashSet mHoldersSet; + + public ConferenceVideoAdapter(List list, + OnConferenceVideoFeedClickListener onConferenceItemClickListener) { + super(list); + this.mOnConferenceVideoFeedClickListener = onConferenceItemClickListener; + mHoldersSet = new HashSet<>(); + setHasStableIds(true); + } + + public void setOnItemClickListener(OnConferenceVideoFeedClickListener onConferenceItemClickListener) { + this.mOnConferenceVideoFeedClickListener = onConferenceItemClickListener; + } + + @Override + public long getItemId(int position) { + if (position < getItemCount()) { + return getItem(position).hashCode(); + } + return -1L; + } + + public void setIsModerator(boolean isModerator) { + this.mIsModerator = isModerator; + } + + public boolean isModerator() { + return this.mIsModerator; + } + + public void releaseVideoCallRrenderers() { + for (ConferenceVideoItemVh vh : mHoldersSet) { + vh.onViewRecycled(); + vh.releaseVideoCallRrenderers(); + } + mHoldersSet.clear(); + + clear(); + } + + public int getoMyVideoFeedPostion() { + int pos = 0; + for (ConferenceListItem item : getItems()) { + if (item.isMe) return pos; + ++pos; + } + return -1; + } + + @NonNull + @Override + public T onCreateViewHolder(@NonNull ViewGroup viewGroup, int i) { + ConferenceVideoItemVh viewHolder = new ConferenceVideoItemVh(viewGroup, mIsModerator); + viewHolder.setOnClickListener(mOnConferenceVideoFeedClickListener); + if (viewGroup.getContext() instanceof CallActivity) { + WindowManager wm = (WindowManager) NynjaApp.getComponent().context().getSystemService(Context.WINDOW_SERVICE); + Display display = wm.getDefaultDisplay(); + display.getMetrics(mDisplayMetrics); + mScreenWidth = mDisplayMetrics.widthPixels; + mScreenHeight = (int) (mDisplayMetrics.heightPixels - mDisplayMetrics.densityDpi/mDisplayMetrics.density); + } + mHoldersSet.add(viewHolder); + return (T) viewHolder; + } + + @Override + public void onBindViewHolder(T holder, int position) { + super.onBindViewHolder(holder, position); + if (mScreenWidth > 0) { + float divWidth = ConferenceVideoModule.getInstance().getWidthDiv(position); + float divHeight = ConferenceVideoModule.getInstance().getHeightDiv(position); + //////////////////////////////////////////////////// + //if (position == 2) ((ConferenceVideoItemVh) holder).getItem().isVideoFullScreen = true; + //if (((ConferenceVideoItemVh) holder).getItem().isVideoFullScreen) { + // divH = 1f; divV = 1f; + //} + //////////////////////////////////////////////////// + int itemWidth = (int) (mScreenWidth / divWidth); + int itemHeight = (int) (mScreenHeight / divHeight); + ViewGroup.LayoutParams layoutParams = holder.itemView.getLayoutParams(); + if (divHeight == MAX_VIDEO_FEEDS_COLUIMN_SIZE) { + layoutParams.height = ViewGroup.LayoutParams.MATCH_PARENT; + } else { + layoutParams.height = itemHeight; + } + if (divWidth == 1) { + layoutParams.width = ViewGroup.LayoutParams.MATCH_PARENT; + } else { + layoutParams.width = itemWidth - ConferenceVideoModule.getInstance().getWidthDividerLength(position); + } + holder.itemView.setLayoutParams(layoutParams); + } + } + + @Override + public int getItemViewType(int position) { + return super.getItemViewType(position); + } + + @Override public void onViewAttachedToWindow(@NonNull T holder) { + super.onViewAttachedToWindow(holder); + ((ConferenceVideoItemVh) holder).onViewAttachedToWindow(); + } + + @Override public void onViewDetachedFromWindow(@NonNull T holder) { + super.onViewDetachedFromWindow(holder); + ((ConferenceVideoItemVh) holder).onViewDetachedFromWindow(); + } + + @Override + public void onViewRecycled(@NonNull T holder) { + super.onViewRecycled(holder); + ((ConferenceVideoItemVh)holder).onViewRecycled(); + } +} + diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/viewholders/ConferenceContactVh.java b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/viewholders/conference/ConferenceContactVh.java similarity index 98% rename from app/src/main/java/com/nynja/mobile/communicator/ui/adapters/viewholders/ConferenceContactVh.java rename to app/src/main/java/com/nynja/mobile/communicator/ui/adapters/viewholders/conference/ConferenceContactVh.java index 7ea4d9ee01f67a3e7fc6435c513facfefd24456b..e97f58d1dbe92b33f2f06856aef59b5fcb97d491 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/viewholders/ConferenceContactVh.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/viewholders/conference/ConferenceContactVh.java @@ -1,4 +1,4 @@ -package com.nynja.mobile.communicator.ui.adapters.viewholders; +package com.nynja.mobile.communicator.ui.adapters.viewholders.conference; import android.content.Context; import android.view.LayoutInflater; diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/viewholders/conference/ConferenceVideoItemVh.java b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/viewholders/conference/ConferenceVideoItemVh.java new file mode 100644 index 0000000000000000000000000000000000000000..8c4e0165619eed1659081743eda384ac3ee6596f --- /dev/null +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/adapters/viewholders/conference/ConferenceVideoItemVh.java @@ -0,0 +1,262 @@ +package com.nynja.mobile.communicator.ui.adapters.viewholders.conference; + +import android.graphics.Color; +import android.view.View; +import android.view.ViewGroup; +import android.widget.FrameLayout; +import android.widget.ImageView; +import android.widget.TextView; + +import com.nynja.mobile.communicator.R; +import com.nynja.mobile.communicator.data.conference.ConferenceVideoModule; +import com.nynja.mobile.communicator.data.sdk.calls.ConferenceListItem; +import com.nynja.mobile.communicator.interfaces.OnConferenceVideoFeedClickListener; +import com.nynja.mobile.communicator.ui.base.BaseVH; +import com.nynja.mobile.communicator.ui.views.CheckableImageView; +import com.nynja.mobile.communicator.utils.StringUtils; + +import org.webrtc.RendererCommon; +import org.webrtc.SurfaceViewRenderer; + +import butterknife.BindView; +import butterknife.ButterKnife; +import kotlin.jvm.Synchronized; +import timber.log.Timber; + +import static org.webrtc.ContextUtils.createRootEglBase; + +public class ConferenceVideoItemVh extends BaseVH { + + @BindView(R.id.conf_video_muted) ImageView mMutedIndicator; + @BindView(R.id.conf_video_name) TextView mName; + @BindView(R.id.conf_video_ss) CheckableImageView mSS; + @BindView(R.id.conf_video_active) CheckableImageView mActiveVideo; + @BindView(R.id.conf_video_feed) SurfaceViewRenderer mVideoFeed; + @BindView(R.id.conf_video_holder_layout) FrameLayout mFeedFrameLayout; + //@BindView(R.id.conf_participant_item) RelativeLayout mFeedFrameLayout; + @BindView(R.id.video_active_switch_camera) ImageView mSwithCamera; + + protected ConferenceListItem mItem; + private OnConferenceVideoFeedClickListener mListener; + private boolean mIsModerator; + private boolean mIsRenderersInitialized = false; + + public ConferenceVideoItemVh(ViewGroup parent, boolean isModerator) { + super(parent, R.layout.li_conference_video); + mIsModerator = isModerator; + } + + public void setOnClickListener(OnConferenceVideoFeedClickListener listener) { + this.mListener = listener; + } + + + @Override public void bind() { + //mUnbinder = ButterKnife.bind(this, itemView); + } + + @Override public void bind(ConferenceListItem item, int position) { + mPosition = position; + if (mUnbinder != null) { + //mUnbinder.unbind(); + } else { + mUnbinder = ButterKnife.bind(this, itemView); + } + setData(item); + } + + public SurfaceViewRenderer getVideoFeed() { + return mVideoFeed; + } + + public ConferenceListItem getItem() { + return mItem; + } + + @Override public void setData(ConferenceListItem item) { + this.mItem = item; + mSS.setAutoToggle(false); + mActiveVideo.setAutoToggle(false); + mName.setText(item.name); + itemView.setOnClickListener(null); + itemView.setOnLongClickListener(null); + if (mListener != null) { + item.initActions(mIsModerator); + itemView.setOnClickListener(v -> mListener.onItemClick(item, mPosition)); + itemView.setOnLongClickListener(v -> mListener.onItemLongClick(item, mPosition)); + } + itemView.setTag(this); + if (item.type == ConferenceListItem.ConferenceItemType.Plus) { +// mPhoto.setImageResource(R.drawable.v_add_member_voice_call); + } else { + // item.type == ConferenceListItem.ConferenceItemType.Participant + if (item.memberId == null || item.memberId.isEmpty()) { +// mPhoto.setImageResource(R.drawable.participant_placeholder_grey); + } else { + // in Conference + setupInConference(item); + onViewAttachedToWindow(); + } + } + } + + public void onViewAttachedToWindow() { + initVideoCallRenderer(); + setVideoRendererForTrack(); + enableVideoForTrack(true); + } + + public void onViewDetachedFromWindow() { + enableVideoForTrack(false); + //removeVideoRendererForTrack(); + } + + public void onViewRecycled() { + enableVideoForTrack(false); + } + + @Synchronized + public void releaseVideoCallRrenderers() { + removeVideoRendererForTrack(); + if (!mIsRenderersInitialized) return; + if (mVideoFeed != null) { + mVideoFeed.release(); + } + mIsRenderersInitialized = false; + } + + ///////////////////////////////////////////////////////////////////////////////////// + // Internal Helpers + private void setupInConference(ConferenceListItem item) { + initVideoCallRenderer(); + if (item.participantId == null || !item.isActive) { + // in Conference - but not joined yet + mMutedIndicator.setVisibility(View.GONE); +// mPhoto.setImageResource(R.drawable.participant_placeholder_grey); + } else { + // already joined in Conference + drawIsMutedIfNeeded(item); + drawIsScfreenSharingIfNeeded(item); +// drawIsActiveVideoIfNeeded(item); + drawIsFeedVideoIfNeeded(item.hasVideo); + if (mItem != null) { + if (mItem != null && mItem.isMe) { + mFeedFrameLayout.setBackgroundResource(R.drawable.video_feed_square_stroke_red); + mSwithCamera.setOnClickListener(null); + if (mListener != null) { + mSwithCamera.setOnClickListener(v -> mListener.onSwitchCameraClick(item)); + mSwithCamera.setVisibility(View.VISIBLE); + } + } else if (mItem != null && mItem.isSpeaking) { + mFeedFrameLayout.setBackgroundResource(R.drawable.video_feed_square_stroke_green); + } else { + mFeedFrameLayout.setBackgroundColor(Color.TRANSPARENT); + } + } else { + mFeedFrameLayout.setBackgroundColor(Color.TRANSPARENT); + } + } + } + + private void drawIsMutedIfNeeded(ConferenceListItem item) { + if(item.isMuted) { + mMutedIndicator.setVisibility(View.VISIBLE); + } else { + mMutedIndicator.setVisibility(View.GONE); +// ImageUtils.loadAvatarImage(item.avatar, mPhoto); + } + } + + private void drawIsActiveVideoIfNeeded(ConferenceListItem item) { + if(item.hasVideo) { + mActiveVideo.setChecked(true); + mActiveVideo.setVisibility(View.VISIBLE); + } else { + mActiveVideo.setVisibility(View.GONE); + } + } + + private void drawIsScfreenSharingIfNeeded(ConferenceListItem item) { + if(item.hasScreen) { + mSS.setChecked(true); + mSS.setVisibility(View.VISIBLE); + } else { + mSS.setVisibility(View.GONE); + } + } + + private void drawIsFeedVideoIfNeeded(boolean visible) { + if (visible) { + mVideoFeed.setVisibility(View.VISIBLE); + } else { + mVideoFeed.setVisibility(View.GONE); + } + refreshFeedVideo(); + } + + private void refreshFeedVideo() { + if (mVideoFeed != null) { + mVideoFeed.invalidate(); + mVideoFeed.refreshDrawableState(); + } + } + + private void showHideItemView(boolean show) { + if (show) { + itemView.setVisibility(View.VISIBLE); + } else { + itemView.setVisibility(View.GONE); + } + + } + + @Synchronized + private void initVideoCallRenderer() { + if (mIsRenderersInitialized) return; + try { + mVideoFeed.init(createRootEglBase().getEglBaseContext(), null); + mVideoFeed.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); + mVideoFeed.setEnableHardwareScaler(true); + if (mItem != null && mItem.isMe) { + mVideoFeed.setMirror(true); + } + mIsRenderersInitialized = true; + } catch (Exception ex) { + Timber.e(ex); + } + } + + @Synchronized + private void setVideoRendererForTrack() { + if (!mIsRenderersInitialized) return; + if (mItem == null) return; + if (mItem.isTrackActive == 1) return; + String trackId = ConferenceVideoModule.getInstance().getTrackIdByParticipantId(mItem.participantId); + if (StringUtils.isNotEmpty(trackId) || mItem.isMe) { + mItem.isTrackActive = 1; + ConferenceVideoModule.getInstance().setVideoRendererForTrack(mVideoFeed, trackId, mItem.isMe); + } + } + + @Synchronized + private void removeVideoRendererForTrack() { + if (!mIsRenderersInitialized) return; + if (mItem == null) return; + if (mItem.isTrackActive == 0) return; + String trackId = ConferenceVideoModule.getInstance().getTrackIdByParticipantId(mItem.participantId); + if (StringUtils.isNotEmpty(trackId) || mItem.isMe) { + ConferenceVideoModule.getInstance().setVideoRendererForTrack(null, trackId, mItem.isMe); + mItem.isTrackActive = 0; + } + } + + @Synchronized + private void enableVideoForTrack(boolean enable) { + if (!mIsRenderersInitialized) return; + if (mItem == null) return; + if (StringUtils.isNotEmpty(mItem.participantId)) { + ConferenceVideoModule.getInstance().enableVideoForTrack(mItem.participantId, enable); + refreshFeedVideo(); + } + } +} diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/base/BaseAdapter.java b/app/src/main/java/com/nynja/mobile/communicator/ui/base/BaseAdapter.java index 79e9e1fb861ec75cbfa8260792e577c84dd49071..fe33b3d069c80db66bad8104fff0acc37d53fa23 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/base/BaseAdapter.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/base/BaseAdapter.java @@ -4,6 +4,8 @@ import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; +import com.nynja.mobile.communicator.ui.adapters.viewholders.conference.ConferenceVideoItemVh; + import java.util.ArrayList; import java.util.List; @@ -33,7 +35,7 @@ public abstract class BaseAdapter> extends RecyclerVie } @Override public void onBindViewHolder(Vh holder, int position) { - holder.bind(getItem(position)); + holder.bind(getItem(position), position); } @Override public int getItemCount() { diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/base/BaseVH.java b/app/src/main/java/com/nynja/mobile/communicator/ui/base/BaseVH.java index 24db500d3b41078f460e388b26dbbda6f0cfa622..1023fe205e9b94deaa9aa9d604cd115dc7985828 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/base/BaseVH.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/base/BaseVH.java @@ -2,6 +2,7 @@ package com.nynja.mobile.communicator.ui.base; import android.content.Context; import android.support.annotation.LayoutRes; +import android.support.v4.view.ViewCompat; import android.support.v7.widget.RecyclerView; import android.view.LayoutInflater; import android.view.View; @@ -24,7 +25,8 @@ import butterknife.Unbinder; public abstract class BaseVH extends RecyclerView.ViewHolder { private Context mContext; - private Unbinder mUnbinder; + protected Unbinder mUnbinder; + protected int mPosition = -1; public BaseVH(ViewGroup parent, @LayoutRes int layoutId) { this(LayoutInflater.from(parent.getContext()).inflate(layoutId, parent, false)); @@ -33,10 +35,15 @@ public abstract class BaseVH extends RecyclerView.ViewHolder { public BaseVH(View itemView) { super(itemView); mContext = itemView.getContext(); + bind(); + } + + public void bind() { mUnbinder = ButterKnife.bind(this, itemView); } - public void bind(I item) { + public void bind(I item, int position) { + mPosition = position; if (mUnbinder != null) mUnbinder.unbind(); mUnbinder = ButterKnife.bind(this, itemView); setData(item); diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/chats/ChatFragment.java b/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/chats/ChatFragment.java index 6224cd2154fc1e89bdfeee9e2c69b4a1ae9c8cf3..cc4579d2c1408d6a609a6bfbef4478c599cd4c3a 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/chats/ChatFragment.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/chats/ChatFragment.java @@ -414,7 +414,7 @@ public class ChatFragment extends BaseChatFragment implements ChatMvpView, mPresenter.startAudioCall(); updateHeaderActionButtons(); }); - if (!mRoom.isGroupChat() && !mRoom.isChannel()) { + if (!mRoom.isChannel()) { mNynjaHeaderView.showVideoCallButton(); mNynjaHeaderView.subscribeToVideoButtonEvent(v -> { mPresenter.startVideoCall(); diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/CallTapToSpeakFragment.java b/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/CallTapToSpeakFragment.java index 9b81c77fa34882cb3bcfbe69067f99f1bb73a99d..d69f39b90e62c6d109d1c2d6afb63529340d6a79 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/CallTapToSpeakFragment.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/CallTapToSpeakFragment.java @@ -20,7 +20,7 @@ import android.widget.Toast; import com.arellomobile.mvp.presenter.InjectPresenter; import com.nynja.mobile.communicator.R; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; import com.nynja.mobile.communicator.data.sdk.calls.ActiveConferenceCall; import com.nynja.mobile.communicator.mvp.presenters.CallTapToSpeakPresenter; import com.nynja.mobile.communicator.mvp.view.CallTapToSpeakView; @@ -393,10 +393,10 @@ public class CallTapToSpeakFragment extends BaseFragment if (isBluetoothHeadsetConnected()) { onAudioRouteChange(audioRouteType); } else { - speaker.post(() -> { + if (speaker != null) { speaker.setImageResource(R.drawable.speaker_selector); speaker.setChecked(isSpeakerOn); - }); + }; } speaker.setAutoToggle(!isBluetoothHeadsetConnected()); diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ConferenceCallFragment.java b/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ConferenceCallFragment.java index 0ccf2d283d1ba23f903a136d5117280416939c02..7122cc65bf9678aa41b01439eecc3349129269a6 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ConferenceCallFragment.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ConferenceCallFragment.java @@ -7,6 +7,7 @@ import android.bluetooth.BluetoothDevice; import android.content.ClipData; import android.content.ClipboardManager; import android.content.Context; +import android.content.DialogInterface; import android.content.SharedPreferences; import android.os.Build; import android.os.Bundle; @@ -34,7 +35,7 @@ import android.widget.Toast; import com.arellomobile.mvp.presenter.InjectPresenter; import com.nynja.mobile.communicator.BuildConfig; import com.nynja.mobile.communicator.R; -import com.nynja.mobile.communicator.data.calls.ActiveCallBase; +import com.nynja.mobile.communicator.data.conference.ActiveCallBase; import com.nynja.mobile.communicator.data.models.nynjamodels.ContactModel; import com.nynja.mobile.communicator.data.models.nynjamodels.RoomModel; import com.nynja.mobile.communicator.data.sdk.calls.ActiveConferenceCall; @@ -43,8 +44,8 @@ import com.nynja.mobile.communicator.interfaces.OnConferenceItemClickListener; import com.nynja.mobile.communicator.mvp.presenters.ConferenceCallPresenter; import com.nynja.mobile.communicator.mvp.view.CallView; import com.nynja.mobile.communicator.ui.activities.calls.CallActivity; -import com.nynja.mobile.communicator.ui.adapters.ActiveCallPagerAdapter; -import com.nynja.mobile.communicator.ui.adapters.ConferenceParticipantsAdapter; +import com.nynja.mobile.communicator.ui.adapters.conference.ActiveCallPagerAdapter; +import com.nynja.mobile.communicator.ui.adapters.conference.ConferenceParticipantsAdapter; import com.nynja.mobile.communicator.ui.base.BaseFragment; import com.nynja.mobile.communicator.ui.views.CheckableImageView; import com.nynja.mobile.communicator.utils.ActionSheetDialog; @@ -53,7 +54,6 @@ import com.nynja.mobile.communicator.utils.DialogFactory; import com.nynja.mobile.communicator.utils.ImageUtils; import com.nynja.mobile.communicator.utils.PermissionHelper; import com.nynja.mobile.communicator.utils.StringUtils; -import com.nynja.mobile.communicator.utils.Utils; import org.webrtc.ContextUtils; import org.webrtc.EglBase; @@ -231,12 +231,12 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, @Override public void onResume() { super.onResume(); -// setRemoteVideoRenderer(mConferencePresenter.getActiveConference()); +// setVideoRendererForTrack(mConferencePresenter.getActiveConference()); // setLocalVideoRenderer(mConferencePresenter.getActiveConference()); // mConferencePresenter.resumeCallVideoCapturer(); isActive = true; if (callActivity != null) { - openShareScreenButton.setVisibility(callActivity.isScreenShareActive ? View.VISIBLE : View.GONE); + openShareScreenButton.setVisibility(callActivity.isScreenShareActive() ? View.VISIBLE : View.GONE); } if (mConferencePresenter.getActiveConference() != null) { @@ -265,7 +265,7 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, // @Override // public void onStart() { // super.onStart(); -// setRemoteVideoRenderer(mConferencePresenter.getActiveConference(), ""); +// setVideoRendererForTrack(mConferencePresenter.getActiveConference(), ""); // setLocalVideoRenderer(mConferencePresenter.getActiveConference()); // mConferencePresenter.resumeCallVideoCapturer(); // isActive = true; @@ -274,7 +274,8 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, @OnClick(R.id.open_share_screen) void openShareScreen() { if (callActivity != null) { - callActivity.getPageIndicator().setCurrentItem(ActiveCallPagerAdapter.CallPages.ShareScreenFragment.ordinal()); + callActivity.goToPage(ActiveCallPagerAdapter.CallPages.ShareScreenFragment); +// callActivity.getPageIndicator().setCurrentItem(ActiveCallPagerAdapter.CallPages.ShareScreenFragment.ordinal()); } } @@ -294,25 +295,17 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, @OnClick(R.id.voice_call_active_video_layout) //turn_video_on_off) void turnVideoOnOff() { - if (!videoOnOf.isEnabled()) return; - if (!videoOnOf.isChecked()) { - if (!mConferencePresenter.isCameraRunning()) { - mRxPermissions.requestEachCombined(Manifest.permission.CAMERA) - .subscribe(permission -> { - if (permission.granted) { - videoOnOf.setChecked(true); - mConferencePresenter.startCameraCapture(); - } else if (!permission.shouldShowRequestPermissionRationale) { - PermissionHelper.getInstance().showPermissionDialog(getActivity(), - mConferencePresenter.getPermissionDialogListener(), Manifest.permission.CAMERA); - } - }, Timber::e); + if (mConferencePresenter.getActiveConference().isConference()) { + if (Consts.CLIENT_ALLOWED_VIDEO_CONFERENCE) { + onClickCameraButton(); + } else { + DialogFactory.showAlertDialog(getContext(), + getString(R.string.call_start_video_conference_alert), + getString(R.string.signin_text_ok), + (dialog, which) -> dialog.dismiss()); } - } else if (videoOnOf.isChecked() && mConferencePresenter.isCameraRunning()) { - mConferencePresenter.stopCameraCapture(); - videoOnOf.setChecked(false); } else { - videoOnOf.setChecked(mConferencePresenter.isCameraRunning()); + onClickCameraButton(); } } @@ -367,6 +360,31 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, } } + private void onClickCameraButton() { + if (!videoOnOf.isEnabled()) return; + if (!videoOnOf.isChecked()) { + if (!mConferencePresenter.isCameraRunning()) { + mRxPermissions.requestEachCombined(Manifest.permission.CAMERA) + .subscribe(permission -> { + if (permission.granted) { + videoOnOf.setChecked(true); + mConferencePresenter.startCameraCapture(); + } else if (!permission.shouldShowRequestPermissionRationale) { + PermissionHelper.getInstance().showPermissionDialog(getActivity(), + mConferencePresenter.getPermissionDialogListener(), Manifest.permission.CAMERA); + } + }, Timber::e); + } else { + videoOnOf.setChecked(mConferencePresenter.isCameraRunning()); + } + } else if (videoOnOf.isChecked() && mConferencePresenter.isCameraRunning()) { + mConferencePresenter.stopCameraCapture(); + videoOnOf.setChecked(false); + } else { + videoOnOf.setChecked(mConferencePresenter.isCameraRunning()); + } + } + private void showAudioRoutActionSheet() { if (getContext() != null) { String bluetoothDeviceName = getString(R.string.bluetooth); @@ -541,7 +559,9 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, mConfParticipantsAdapter.setIsModerator(activeConferenceCall.isModerator()); if (activeConferenceCall.isWaiting()) { waitingConference(activeConferenceCall); - } else if (activeConferenceCall.isVideoEnabled || activeConferenceCall.isOwnStreamActive) { + } else if ((activeConferenceCall.isVideoEnabled + || activeConferenceCall.mData.isOwnStreamActive) + && !activeConferenceCall.isConference()){ videoConference(activeConferenceCall); //if (!activeConferenceCall.isSpeakerOn) } else { @@ -567,7 +587,7 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, @Override public void onConferenceEnded() { if (getActivity() == null || !isAdded()) return; - getActivity().runOnUiThread(() -> getActivity().finish()); + //getActivity().runOnUiThread(() -> getActivity().finish()); } @Override @@ -638,8 +658,8 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, activeUserNameSection.setVisibility(visible); audioIncomeLayout.setVisibility(visible); name.setVisibility(visible); - callActivity.getPageIndicator().setVisibility(visible); - if (activeConferenceCall.mCallType == ActiveCallBase.CallType.ConferenceCall) { + //callActivity.getPageIndicator().setVisibility(visible); + if (activeConferenceCall.isConference()) { activeUserPhotoLayout.setVisibility(View.GONE); conferenceParticipantsLayout.setVisibility(visible); } else { @@ -652,7 +672,7 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, protected void updateRendererUI(ActiveConferenceCall activeConferenceCall) { if (activeConferenceCall != null && activeConferenceCall.mConference != null) { - if (activeConferenceCall.hasRemoteVideoTrack + if (activeConferenceCall.mData.hasRemoteVideoTrack // && activeConferenceCall.mConference.hasRemoteVideo() && isActive) { videoRemote.setVisibility(View.VISIBLE); @@ -698,8 +718,8 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, name.setVisibility(visible); conferenceParticipantsLayout.setVisibility(View.GONE); activeUserNameSection.setVisibility(visible); - callActivity.getPageIndicator().setVisibility(visible); - if (activeConferenceCall != null && activeConferenceCall.mCallType == ActiveCallBase.CallType.ConferenceCall) { + //callActivity.getPageIndicator().setVisibility(visible); + if (activeConferenceCall != null && activeConferenceCall.isConference()) { // TODO: for later Conference calls video support. } else { mConferencePresenter.loadUser(activeConferenceCall); @@ -741,9 +761,16 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, } } - if (activeConferenceCall.mCallType == ActiveCallBase.CallType.ConferenceCall) { + if (activeConferenceCall.isConference()) { mConferencePresenter.loadConferenceParticipants(activeConferenceCall); addCallerLayout.setEnabled(activeConferenceCall.isModerator()); + if (activeConferenceCall.mInitialStartCapturer && + activeConferenceCall.mData.isOwnStreamActive) { + mHandler.postDelayed(() ->{ + activeConferenceCall.mConference.startCamera(); + }, Consts.DELAY_200); + activeConferenceCall.mInitialStartCapturer = false; + } } else { mConferencePresenter.loadUser(activeConferenceCall); // TODO: update state after P2P upgrade to Group call support implemented !!!! @@ -757,7 +784,8 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, if (callActivity != null && !activeConferenceCall.isCallInProgress()) { callActivity.getViewPager().setPagingEnabled(false); } - mHandler.post(() -> initialConferenceStates(activeConferenceCall)); + //mHandler.post(() -> initialConferenceStates(activeConferenceCall)); + initialConferenceStates(activeConferenceCall); //////////////////////////////////////////////////////////////////////////////////////////// // TEST TEST TEST if (BuildConfig.DEBUG) { @@ -783,6 +811,23 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, } + @Override public void onStartCapturerFailed(int msgResId, final boolean isCamera) { + getActivity().runOnUiThread(() -> DialogFactory.showAlert(getActivity(), + getString(msgResId), + getString(R.string.call_conference_alert_title), + new DialogInterface.OnDismissListener() { + @Override + public void onDismiss(DialogInterface dialogInterface) { + if (isCamera) { + videoOnOf.setChecked(mConferencePresenter.isCameraRunning()); + } else { + setScreenShareOn(false); + } + } + })); + + } + @Override public void onRemoteVideoTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) { if (getActivity() == null || !isAdded()) return; @@ -891,7 +936,7 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, if (active) { updateMicrophonState(activeConferenceCall.isMuted); if (activeConferenceCall.isVideoEnabled) { - if (activeConferenceCall.isOwnStreamActive) { + if (activeConferenceCall.mData.isOwnStreamActive) { setLocalVideoRenderer(mConferencePresenter.getActiveConference()); mConferencePresenter.resumeCallVideoCapturer(); } @@ -902,8 +947,8 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, muteMicHint.setVisibility(View.GONE); mHandler.removeCallbacks(mShowMuteMicHintExpired); if (activeConferenceCall.isVideoEnabled) { - if (activeConferenceCall.isOwnStreamActive) { - mConferencePresenter.pauseCallVideoCapturer(); + if (activeConferenceCall.mData.isOwnStreamActive) { + //mConferencePresenter.pauseCallVideoCapturer(); removeLocalVideoRenderer(mConferencePresenter.getActiveConference()); } removeRemoteVideoRenderer(mConferencePresenter.getActiveConference()); @@ -1128,7 +1173,8 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, private void requestPermissionIfNeeds(ActiveConferenceCall activeConferenceCall) { if (activeConferenceCall == null) return; - boolean enableVideo = (activeConferenceCall.isOwnStreamActive && activeConferenceCall.mInitialStartCapturer); + boolean enableVideo = (activeConferenceCall.mData.isOwnStreamActive + && activeConferenceCall.mInitialStartCapturer); ArrayList permissions = getPermissionsForCall(enableVideo); String[] perms = new String[0]; @@ -1204,7 +1250,7 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, private void updateAddContact(ActiveConferenceCall activeConferenceCall) { if (shouldCallActionsActive(activeConferenceCall)) { - if (activeConferenceCall.mCallType == ActiveCallBase.CallType.ConferenceCall) { + if (activeConferenceCall.isConference()) { addCallerLayout.setEnabled(activeConferenceCall.isModerator()); addContact.setEnabled(activeConferenceCall.isModerator()); } else { @@ -1249,7 +1295,7 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, private void initVideoCallRrenderers(ActiveConferenceCall activeConferenceCall) { if (activeConferenceCall == null) return; - if (activeConferenceCall.mCallType == ActiveCallBase.CallType.ConferenceCall) return; + if (activeConferenceCall.isConference()) return; if (!activeConferenceCall.isVideoEnabled) return; try { videoLocal.init(createRootEglBase().getEglBaseContext(), null); @@ -1279,34 +1325,37 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, private void setRemoteVideoRenderer(ActiveConferenceCall activeConferenceCall) { if (!mIsRenderersInitialized) return; if (activeConferenceCall == null) return; - //if (activeConferenceCall.mCallType == ActiveCallBase.CallType.ConferenceCall) return; + if (activeConferenceCall.isConference()) return; if (activeConferenceCall.mConference == null) return; + if (activeConferenceCall.mConference.isConference()) return; if (!activeConferenceCall.isVideoEnabled) return; - if (activeConferenceCall.hasRemoteVideoTrack) { - activeConferenceCall.mConference.setRemoteVideoRenderer(videoRemote, activeConferenceCall.mRemoteVideoTrackId); + if (activeConferenceCall.mData.hasRemoteVideoTrack) { + activeConferenceCall.mConference.setRemoteVideoRenderer(videoRemote, activeConferenceCall.mData.mRemoteVideoTrackId); } } private void removeRemoteVideoRenderer(ActiveConferenceCall activeConferenceCall, String trackId) { if (activeConferenceCall == null) return; - //if (activeConferenceCall.mCallType == ActiveCallBase.CallType.ConferenceCall) return; + if (activeConferenceCall.isConference()) return; if (activeConferenceCall.mConference == null) return; + if (activeConferenceCall.mConference.isConference()) return; if (!activeConferenceCall.mConference.isRunning()) return; if (!activeConferenceCall.isVideoEnabled) return; - if (!activeConferenceCall.hasRemoteVideoTrack && StringUtils.isNotEmpty(trackId)) { + if (!activeConferenceCall.mData.hasRemoteVideoTrack && StringUtils.isNotEmpty(trackId)) { activeConferenceCall.mConference.setRemoteVideoRenderer(null, trackId); } } private void removeRemoteVideoRenderer(ActiveConferenceCall activeConferenceCall) { if (activeConferenceCall == null) return; - removeRemoteVideoRenderer(activeConferenceCall, activeConferenceCall.mRemoteVideoTrackId); + if (activeConferenceCall.isConference()) return; + removeRemoteVideoRenderer(activeConferenceCall, activeConferenceCall.mData.mRemoteVideoTrackId); } private void setLocalVideoRenderer(ActiveConferenceCall activeConferenceCall) { if (!mIsRenderersInitialized) return; if (activeConferenceCall == null) return; - //if (activeConferenceCall.mCallType == ActiveCallBase.CallType.ConferenceCall) return; + if (activeConferenceCall.isConference()) return; if (activeConferenceCall.mConference == null) return; if (!activeConferenceCall.isVideoEnabled) return; @@ -1315,7 +1364,7 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, private void removeLocalVideoRenderer(ActiveConferenceCall activeConferenceCall) { if (activeConferenceCall == null) return; - //if (activeConferenceCall.mCallType == ActiveCallBase.CallType.ConferenceCall) return; + if (activeConferenceCall.isConference()) return; if (activeConferenceCall.mConference == null) return; if (!activeConferenceCall.mConference.isRunning()) return; if (!activeConferenceCall.isVideoEnabled) return; @@ -1378,8 +1427,10 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, } private void startConferenceCall() { - mConferencePresenter.startConference(); - status.setText(R.string.call_connecting); + if (getActivity() != null) { + mConferencePresenter.startConference(); + status.setText(R.string.call_connecting); + } } private void updateContactInfo() { @@ -1392,8 +1443,9 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, private void setConferenceState(ActiveConferenceCall activeConferenceCall) { if (activeConferenceCall.isWaiting()) { waitingConference(activeConferenceCall); - } else if (activeConferenceCall.isSwitchToAudio || - !activeConferenceCall.isVideoEnabled) { + } else if (activeConferenceCall.isSwitchToAudio + || !activeConferenceCall.isVideoEnabled + || activeConferenceCall.isConference()) { audioConference(activeConferenceCall); } else { videoConference(activeConferenceCall); @@ -1425,7 +1477,7 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, @Override public void updateMicrophonState(boolean isMuted) { if (audioMute != null && isActive) { - audioMute.post(() -> audioMute.setChecked(isMuted)); + audioMute.setChecked(isMuted); } } @@ -1553,7 +1605,7 @@ public class ConferenceCallFragment extends BaseFragment implements CallView, public void showFirstStopScreenSharingWarning(ActiveConferenceCall call){ if (call == null) return; String message = getString(R.string.call_ask_to_stop_ss_first, call.getConferenceSSOwner()); - if (call.isConference() && call.hasRemoteVideoTrack) { + if (call.isConference() && call.mData.hasRemoteVideoTrack) { message = getString(R.string.call_ask_to_stop_camera_first, call.getConferenceSSOwner()); } final String msg = message; diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ConferenceVideoFragment.java b/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ConferenceVideoFragment.java new file mode 100644 index 0000000000000000000000000000000000000000..eaad10fb5cf52701faa81bdb171b3b389f0a756d --- /dev/null +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ConferenceVideoFragment.java @@ -0,0 +1,568 @@ +package com.nynja.mobile.communicator.ui.fragments.conference; + +import android.os.Bundle; +import android.os.Handler; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.support.v7.widget.DefaultItemAnimator; +import android.support.v7.widget.DividerItemDecoration; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.LinearSnapHelper; +import android.support.v7.widget.PagerSnapHelper; +import android.support.v7.widget.RecyclerView; +import android.support.v7.widget.SnapHelper; +import android.view.LayoutInflater; +import android.view.View; +import android.view.ViewGroup; +import android.widget.Toast; + +import com.arellomobile.mvp.presenter.InjectPresenter; +import com.nynja.mobile.communicator.R; +import com.nynja.mobile.communicator.data.conference.ConferenceVideoModule; +import com.nynja.mobile.communicator.data.sdk.calls.ActiveConferenceCall; +import com.nynja.mobile.communicator.data.sdk.calls.ConferenceListItem; +import com.nynja.mobile.communicator.interfaces.OnConferenceItemClickListener; +import com.nynja.mobile.communicator.interfaces.OnConferenceVideoFeedClickListener; +import com.nynja.mobile.communicator.mvp.presenters.ConferenceVideoPresenter; +import com.nynja.mobile.communicator.mvp.view.ConferenceVideoView; +import com.nynja.mobile.communicator.ui.activities.calls.CallActivity; +import com.nynja.mobile.communicator.ui.adapters.conference.ConferenceVideoAdapter; +import com.nynja.mobile.communicator.ui.adapters.viewholders.conference.ConferenceVideoItemVh; +import com.nynja.mobile.communicator.ui.base.BaseFragment; +import com.nynja.mobile.communicator.utils.ActionSheetDialog; +import com.nynja.mobile.communicator.utils.DialogFactory; +import com.nynja.mobile.communicator.utils.StringUtils; + +import org.webrtc.ContextUtils; +import org.webrtc.EglBase; + +import java.util.ArrayList; + +import butterknife.BindView; +import timber.log.Timber; + +import static com.nynja.mobile.communicator.data.conference.ConferenceVideoModule.MAX_VIDEO_FEEDS_COLUIMN_SIZE; + +public class ConferenceVideoFragment extends BaseFragment implements ConferenceVideoView, + OnConferenceVideoFeedClickListener { + + private static final int FRONT_CAMERA = 1; + + // conference controls + @BindView(R.id.f_call_rv) RecyclerView mRecyclerView; + + @InjectPresenter + ConferenceVideoPresenter mConferencePresenter; + + private ConferenceVideoAdapter mConfParticipantsAdapter; + private boolean isActive; + private CallActivity callActivity; + private Handler mHandler; + GridLayoutManager mGridLayoutManager; + private ArrayList mMembersList = new ArrayList<>(); + private boolean mTryToGoTooMyVideoFeed = false; + + @Override + public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceState) { + super.onViewCreated(view, savedInstanceState); + mHandler = new Handler(); + createRootEglBase(); + ActiveConferenceCall activeConferenceCall = mConferencePresenter.getActiveConference(); + ConferenceVideoModule.getInstance().setCurrentActiveCall(activeConferenceCall); + mConfParticipantsAdapter = new ConferenceVideoAdapter(mMembersList, this); + initUIRv(); + } + + @Override + public void onDestroyView() { + mRecyclerView.clearOnChildAttachStateChangeListeners(); + mRecyclerView.clearOnScrollListeners(); + mRecyclerView.setAdapter(null); + mConfParticipantsAdapter.releaseVideoCallRrenderers(); + super.onDestroyView(); + } + + @Nullable + @Override + public View onCreateView(@NonNull LayoutInflater inflater, @Nullable ViewGroup container, @Nullable Bundle savedInstanceState) { + return inflater.inflate(R.layout.fragment_conference_video_call, container, false); + } + + @Override + public void onDestroy() { + mConfParticipantsAdapter = null; + super.onDestroy(); + ConferenceVideoModule.getInstance().setCurrentActiveCall(null); + } + + + @Override + public void onResume() { + super.onResume(); + isActive = true; + scrollToMyFeedPosition(); + } + + @Override + public void onPause() { + mConferencePresenter.pauseCallVideoCapturer(); + isActive = false; + super.onPause(); + } + + @Override + public void onSwitchCameraClick(ConferenceListItem item) { + mConferencePresenter.switchCamera(); + } + + @Override + public void onItemClick(ConferenceListItem item, int position) { + Timber.d("On clicked position: %d contact: %s", position, item); + if (item == null) return; + // '+' item + } + + @Override + public boolean onItemLongClick(ConferenceListItem item, int position) { + Timber.d("On long clicked position: %d contact: %s", position, item); + if (item == null) return false; + // '+' item + if (item.type == ConferenceListItem.ConferenceItemType.Plus) { + return false; + } + + if (item.memberActions.size() == 0) return false; + + + ActionSheetDialog actionSheet = new ActionSheetDialog(getActivity()); + actionSheet.isKeyPrevPressed = true; + actionSheet.setCancelButtonTitle(getString(R.string.cancel)); + ArrayList itemsArray = new ArrayList<>(); + for(int resId : item.memberActions) { + itemsArray.add(getString(resId)); + } + actionSheet.addItems(itemsArray.toArray(new String[itemsArray.size()])); + actionSheet.setItemClickListener( + itemPosition -> { + if (item.isMe) { + onClickMyIconItem(item, itemPosition); + } else { + if (item.isFriend) { + onClickOthersFriendIconItem(item, itemPosition); + } else { + onClickOthersIconItem(item, itemPosition); + } + } + }); + actionSheet.setCancelableOnTouchMenuOutside(true); +// actionSheet.showMenu(); + return true; + } + + private void initUIRv() { + mRecyclerView.setNestedScrollingEnabled(false); + mGridLayoutManager = new GridLayoutManager(getActivity(), + MAX_VIDEO_FEEDS_COLUIMN_SIZE, + LinearLayoutManager.HORIZONTAL, + false); +// mGridLayoutManager.setSpanCount(2); +// mGridLayoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); +// mGridLayoutManager.setReverseLayout(false); +// mGridLayoutManager.setSpanSizeLookup(new GridLayoutManager.SpanSizeLookup() { +// @Override +// public int getSpanSize(int position) { +// return (int)ConferenceVideoModule.getInstance().getSpanSize(position); +// } +// }); + mGridLayoutManager.setItemPrefetchEnabled(true); + mRecyclerView.setLayoutManager(mGridLayoutManager); + mRecyclerView.setHasFixedSize(true); + mRecyclerView.setAdapter(mConfParticipantsAdapter); + SnapHelper snapHelper = new LinearSnapHelper(); + snapHelper.attachToRecyclerView(mRecyclerView); + mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL)); + mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL)); +// mRecyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() { +// @Override +// public void onChildViewAttachedToWindow(View view) { +// ConferenceVideoItemVh holder = (ConferenceVideoItemVh)getChildViewHolderInternal(view); +// if (holder != null) { +// //holder.onViewAttachedToWindow(); +// } +// } +// +// @Override +// public void onChildViewDetachedFromWindow(View view) { +// ConferenceVideoItemVh holder = (ConferenceVideoItemVh)getChildViewHolderInternal(view); +// if (holder != null) { +// //holder.onViewDetachedFromWindow(); +// } +// } +// }); +// mRecyclerView.addOnScrollListener(new RecyclerView.OnScrollListener() { +// @Override +// public void onScrolled(RecyclerView recyclerView, int dx, int dy) { +// super.onScrolled(recyclerView, dx, dy); +// } +// }); + } + + private RecyclerView.ViewHolder getChildViewHolderInternal(View child) { + return child == null ? null : (RecyclerView.ViewHolder)(child.getTag()); + } + + protected void updateRendererUI(ActiveConferenceCall activeConferenceCall) { + if (mConfParticipantsAdapter != null) { + mConfParticipantsAdapter.notifyDataSetChanged(); + } + } + + protected void videoConference(ActiveConferenceCall activeConferenceCall) { + if (getActivity() == null || !isAdded()) return; + updateRendererUI(activeConferenceCall); + } + + protected void initialConferenceStates(ActiveConferenceCall activeConferenceCall) { + setRemoteVideoRenderer(activeConferenceCall); + if (isActive) { + mConferencePresenter.resumeCallVideoCapturer(); + } + } + + @Override + public void initConferenceStates(ActiveConferenceCall activeConferenceCall) { + if (activeConferenceCall.isConference()) { + mConferencePresenter.loadConferenceParticipants(activeConferenceCall); + } + + // on first create time it is Active. + isActive = true; + mConfParticipantsAdapter.setIsModerator(activeConferenceCall.isModerator()); + setConferenceState(activeConferenceCall); + mHandler.post(() -> initialConferenceStates(activeConferenceCall)); + } + + @Override + public void onConferenceMembersUpdate(ArrayList members) { + if (getActivity() == null || !isAdded()) return; + getActivity().runOnUiThread(() -> loadConferenceMembers(members)); + } + + @Override + public void onRemoteVideoTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) { + if (getActivity() == null || !isAdded()) return; + getActivity().runOnUiThread(() -> { + setRemoteVideoRenderer(activeConferenceCall); + setConferenceState(activeConferenceCall); + }); + } + + @Override + public void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) { + if (getActivity() == null || !isAdded()) return; + getActivity().runOnUiThread(() -> { + removeRemoteVideoRenderer(activeConferenceCall, trackId); + setConferenceState(activeConferenceCall); + }); + } + + @Override + public void onLocalVideoCapturerStarted(ActiveConferenceCall activeConferenceCall) { + if (getActivity() == null || !isAdded()) return; + mHandler.post(() -> { + setLocalVideoRenderer(activeConferenceCall); + setConferenceState(activeConferenceCall); + }); + } + + @Override + public void onLocalVideoCapturerStopped(ActiveConferenceCall activeConferenceCall) { + if (getActivity() == null || !isAdded()) return; + mHandler.post(() -> { + removeLocalVideoRenderer(activeConferenceCall); + setConferenceState(activeConferenceCall); + }); + } + + @Override + public void onScreenShareStateChanged(boolean isSharing) { + if (getActivity() == null || !isAdded()) return; + mHandler.post(() -> mConfParticipantsAdapter.notifyDataSetChanged()); + } + + @Override + public void onMicrophoneStateChanged(boolean isMuted) { + if (getActivity() == null || !isAdded()) return; + getActivity().runOnUiThread(() -> updateMicrophonState(isMuted)); + } + + @Override + public void onSpeakerStateChanged(boolean isSpeakerOn) { + if (getActivity() == null || !isAdded()) return; +// getActivity().runOnUiThread(() -> updateSpeakerState(isSpeakerOn)); + } + + @Override + public void onActiveSpeakersUpdate(String activeSpeakers, ArrayList participantIds) { + if (getActivity() == null || !isAdded()) return; + getActivity().runOnUiThread(() -> { + if (getActivity() == null || !isAdded() || !isActive) return; + mConferencePresenter.updateActiveSpeakersState(participantIds); + }); + } + + @Override + public void showInternetError() { + if (isAdded()) { + Toast.makeText(getActivity(), R.string.error_no_internet_connection, Toast.LENGTH_SHORT).show(); + } + } + + public boolean onBackPressed() { + return false; + } + + public void setCallActivity(CallActivity activity) { + callActivity = activity; + } + + public void setIsActive(boolean isActive) { + //this.isActive = isActive; + } + + public void tryToGoTooMyVideoFeed() { + mTryToGoTooMyVideoFeed = true; + if (isActive) { + scrollToMyFeedPosition(); + } + } + + private void scrollToMyFeedPosition() { + if (mTryToGoTooMyVideoFeed) { + mTryToGoTooMyVideoFeed = false; + int myPos = mConfParticipantsAdapter.getoMyVideoFeedPostion(); + if (myPos >= 0) mRecyclerView.scrollToPosition(myPos); + } + } + + public void setActiveState(boolean active) { + ActiveConferenceCall activeConferenceCall = mConferencePresenter.getActiveConference(); + isActive = active; + if (activeConferenceCall == null) return; + if (active) { + updateMicrophonState(activeConferenceCall.isMuted); + if (activeConferenceCall.isVideoEnabled) { + if (activeConferenceCall.mData.isOwnStreamActive) { + setLocalVideoRenderer(mConferencePresenter.getActiveConference()); + mConferencePresenter.resumeCallVideoCapturer(); + } + setRemoteVideoRenderer(mConferencePresenter.getActiveConference()); + setConferenceState(activeConferenceCall); + } + scrollToMyFeedPosition(); + } else { + if (activeConferenceCall.isVideoEnabled) { + if (activeConferenceCall.mData.isOwnStreamActive) { + mConferencePresenter.pauseCallVideoCapturer(); + removeLocalVideoRenderer(mConferencePresenter.getActiveConference()); + } + removeRemoteVideoRenderers(mConferencePresenter.getActiveConference()); + setConferenceState(activeConferenceCall); + } + } + } + + // Helpers + private void onClickMyIconItem(ConferenceListItem participant, Integer postion) { + if (postion >= participant.memberActions.size() || postion < 0) return; + switch (participant.memberActions.get(postion)) { +// case R.string.call_my_profile: +// mConferencePresenter.showProfile(participant); +// break; +// case R.string.call_leave_call): +// leaveCall(); +// break; + + default: + break; + } + } + + private void onClickOthersFriendIconItem(ConferenceListItem participant, Integer postion) { + if (postion >= participant.memberActions.size() || postion < 0) return; + switch (participant.memberActions.get(postion)) { + case R.string.call_view_profile: + mConferencePresenter.showProfile(participant); + break; + case R.string.send_a_message: + mConferencePresenter.openChatWithParticipant(participant.phoneId); + break; + + case R.string.call_remove_from_call: + showRemoveParticipantConfirmation(participant); + break; + + case R.string.call_back: + mConferencePresenter.callBackPerson(participant); + break; + + case R.string.call_unmute: + unMuteParticipant(participant); + break; + case R.string.call_mute: + muteParticipant(participant); + break; + + case R.string.call_item_menu_stop_ss: + stopParticipantSS(participant); + break; + + case R.string.call_item_menu_stop_video: + stopParticipantVideo(participant); + break; + + default: + break; + } + } + + private void onClickOthersIconItem(ConferenceListItem participant, Integer postion) { + if (postion >= participant.memberActions.size() || postion < 0) return; + switch (participant.memberActions.get(postion)) { + case R.string.call_view_profile: + mConferencePresenter.showProfile(participant); + break; + + case R.string.call_remove_from_call: + showRemoveParticipantConfirmation(participant); + break; + + case R.string.call_unmute: + unMuteParticipant(participant); + break; + case R.string.call_mute: + muteParticipant(participant); + break; + case R.string.call_item_menu_stop_ss: + stopParticipantSS(participant); + break; + case R.string.call_item_menu_stop_video: + stopParticipantVideo(participant); + break; + + default: + break; + } + } + + private void showRemoveParticipantConfirmation(ConferenceListItem participant) { + DialogFactory.createYesNoDialog(getActivity(), R.string.call_remove_participant_confirm, + (dialog, which) -> mConferencePresenter.removeFromCall(participant), + (dialog, which) -> dialog.dismiss()).show(); + } + + private void muteParticipant(ConferenceListItem item) { + mConferencePresenter.muteParticipant(item); + } + + private void unMuteParticipant(ConferenceListItem item) { + mConferencePresenter.unMuteParticipant(item.participantId); + } + + private void stopParticipantSS(ConferenceListItem item) { + mConferencePresenter.stopParticipantSS(item); + } + + private void stopParticipantVideo(ConferenceListItem item) { + mConferencePresenter.stopParticipantVideo(item); + } + + + private void showActionSheetDialog(ActionSheetDialog.MenuItemClickListener listener, String... items) { + ActionSheetDialog actionSheet = new ActionSheetDialog(getActivity()); + actionSheet.setCancelButtonTitle(getString(R.string.cancel)); + actionSheet.addItems(items); + actionSheet.setItemClickListener(listener); + actionSheet.setCancelableOnTouchMenuOutside(true); + actionSheet.showMenu(); + } + + private EglBase createRootEglBase() { + return ContextUtils.createRootEglBase(); + } + + private void loadConferenceMembers(ArrayList members) { + mConfParticipantsAdapter.setItems(members); + } + + private void setRemoteVideoRenderer(ActiveConferenceCall activeConferenceCall) { + if (activeConferenceCall == null) return; + if (!activeConferenceCall.isConference()) return; + if (activeConferenceCall.mConference == null) return; + if (!activeConferenceCall.mConference.isConference()) return; + if (!activeConferenceCall.isVideoEnabled) return; + + mConferencePresenter.loadConferenceParticipants(activeConferenceCall); + } + + private void removeRemoteVideoRenderer(ActiveConferenceCall activeConferenceCall, String trackId) { + if (activeConferenceCall == null) return; + if (activeConferenceCall.mConference == null) return; + if (!activeConferenceCall.mConference.isConference()) return; + if (!activeConferenceCall.mConference.isRunning()) return; + if (!activeConferenceCall.isVideoEnabled) return; + + if (StringUtils.isNotEmpty(trackId)) { + ConferenceVideoModule.getInstance().setVideoRendererForTrack(null, trackId, false); + } + + mConfParticipantsAdapter.notifyDataSetChanged(); + } + + private void removeRemoteVideoRenderers(ActiveConferenceCall activeConferenceCall) { + if (activeConferenceCall == null) return; + ArrayList members = mConferencePresenter.getConferenceVideoMembers(activeConferenceCall); + for (ConferenceListItem item : members) { + String trackId = ConferenceVideoModule.getInstance().getTrackIdByParticipantId(item.participantId); + if (StringUtils.isNotEmpty(trackId) || item.isMe) { + ConferenceVideoModule.getInstance().setVideoRendererForTrack(null, trackId, item.isMe); + } + } + } + + private void setLocalVideoRenderer(ActiveConferenceCall activeConferenceCall) { + if (activeConferenceCall == null) return; + if (activeConferenceCall.mConference == null) return; + if (!activeConferenceCall.isVideoEnabled) return; + + mConferencePresenter.loadConferenceParticipants(activeConferenceCall); + } + + private void removeLocalVideoRenderer(ActiveConferenceCall activeConferenceCall) { + if (activeConferenceCall == null) return; + if (activeConferenceCall.mConference == null) return; + if (!activeConferenceCall.mConference.isRunning()) return; + if (!activeConferenceCall.isVideoEnabled) return; + + mConferencePresenter.loadConferenceParticipants(activeConferenceCall); + } + + private void setConferenceState(ActiveConferenceCall activeConferenceCall) { + if (activeConferenceCall.isConference()) { + videoConference(activeConferenceCall); + } + } + + @Override + public void updateMicrophonState(boolean isMuted) { + } + + @Override + public void closenConference() { + if (getActivity() == null) return; + getActivity().finish(); + } + + +} diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ScreenShareFragment.kt b/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ScreenShareFragment.kt index 92f0d45b9fed0af6da353c127a663cd6924033df..8abd3f17792699a2d5f87b9d69e9616b02ef7153 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ScreenShareFragment.kt +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/fragments/conference/ScreenShareFragment.kt @@ -68,7 +68,13 @@ class ScreenShareFragment : BaseFragment(), ScreenShareView { mScreenShareRemote?.layoutParams = layoutParams mZoomableFrameLayout.reset() mFrameLayout?.addView(mContainer) - remoteScreenShareState(mPresenter.activeConference.hasRemoteScreenShareTrack) + remoteScreenShareState(mPresenter.activeConference.mData.hasRemoteScreenShareTrack) + } + + override fun onDestroyView() { + removeRemoteScreenShareRenderer(mPresenter.getActiveConference()) + mScreenShareRemote?.release() + super.onDestroyView() } override fun onConfigurationChanged(newConfig: Configuration) { @@ -87,7 +93,7 @@ class ScreenShareFragment : BaseFragment(), ScreenShareView { } override fun initConferenceStates(activeConferenceCall: ActiveConferenceCall) { - if (activeConferenceCall.hasRemoteScreenShareTrack) { + if (activeConferenceCall.mData.hasRemoteScreenShareTrack) { setRemoteScreenShareRenderer(activeConferenceCall) } else { removeRemoteScreenShareRenderer(activeConferenceCall) @@ -121,7 +127,7 @@ class ScreenShareFragment : BaseFragment(), ScreenShareView { } private fun removeRemoteScreenShareRenderer(activeConferenceCall: ActiveConferenceCall?) { - removeRemoteScreenShareRenderer(activeConferenceCall, activeConferenceCall?.mRemoteSSTrackId) + removeRemoteScreenShareRenderer(activeConferenceCall, activeConferenceCall?.mData?.mRemoteSSTrackId) } private fun remoteScreenShareState(visible: Boolean) { @@ -148,10 +154,10 @@ class ScreenShareFragment : BaseFragment(), ScreenShareView { if (activeConferenceCall == null) return //if (activeConferenceCall.mCallType == ActiveCallBase.CallType.ConferenceCall) return if (activeConferenceCall.mConference == null) return - if (!activeConferenceCall.hasRemoteScreenShareTrack) return + if (!activeConferenceCall.mData.hasRemoteScreenShareTrack) return - if (StringUtils.isNotEmpty(activeConferenceCall.mRemoteSSTrackId)) { - setRemoteScreenShareRenderer(activeConferenceCall, activeConferenceCall.mRemoteSSTrackId) + if (StringUtils.isNotEmpty(activeConferenceCall.mData.mRemoteSSTrackId)) { + setRemoteScreenShareRenderer(activeConferenceCall, activeConferenceCall.mData.mRemoteSSTrackId) } remoteScreenShareState(activeConferenceCall, true) @@ -176,7 +182,7 @@ class ScreenShareFragment : BaseFragment(), ScreenShareView { mScreenShareRemote?.setEnableHardwareScaler(true) mScreenShareRemote?.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT, RendererCommon.ScalingType.SCALE_ASPECT_FIT) // duplicates to Presenter::attachView(view: ScreenShareView?) -> setRemoteScreenShareRenderer()... - //if (activeConferenceCall.hasRemoteScreenShareTrack) { + //if (activeConferenceCall.mData.hasRemoteScreenShareTrack) { // setRemoteScreenShareRenderer(activeConferenceCall) //} diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/views/CirclePageIndicator.java b/app/src/main/java/com/nynja/mobile/communicator/ui/views/CirclePageIndicator.java index 1de72cc6a37de47c4579bcaf37648d3e972c7bf4..e3e48346feba2f0fe68c06e4252fd7f8f9402ff4 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/views/CirclePageIndicator.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/views/CirclePageIndicator.java @@ -374,6 +374,10 @@ public class CirclePageIndicator extends View implements ViewPager.OnPageChangeL invalidate(); } + public int getCurrentItem() { + return mCurrentPage; + } + public void notifyDataSetChanged() { invalidate(); } diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/views/CirclePagerIndicatorDecoration.java b/app/src/main/java/com/nynja/mobile/communicator/ui/views/CirclePagerIndicatorDecoration.java new file mode 100644 index 0000000000000000000000000000000000000000..898677c168a01a441789bfca58e738252a37ee38 --- /dev/null +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/views/CirclePagerIndicatorDecoration.java @@ -0,0 +1,118 @@ +package com.nynja.mobile.communicator.ui.views; + +import android.content.res.Resources; +import android.graphics.Canvas; +import android.graphics.Paint; +import android.graphics.Rect; +import android.support.annotation.ColorInt; +import android.support.v7.widget.GridLayoutManager; +import android.support.v7.widget.LinearLayoutManager; +import android.support.v7.widget.RecyclerView; +import android.view.View; + +import org.jetbrains.annotations.NotNull; + +/** + * Created by Ergyun Syuleyman on 04/07/20. + */ +public class CirclePagerIndicatorDecoration extends RecyclerView.ItemDecoration { + + private final int indicatorHeight; + private final int indicatorItemPadding; + private final int radius; + + private final Paint inactivePaint = new Paint(); + private final Paint activePaint = new Paint(); + + public CirclePagerIndicatorDecoration(int radius, int padding, int indicatorHeight, + int colorInactive, int colorActive) { + float strokeWidth = Resources.getSystem().getDisplayMetrics().density * 2; + this.radius = radius; + inactivePaint.setStrokeCap(Paint.Cap.ROUND); + inactivePaint.setStrokeWidth(strokeWidth); + inactivePaint.setStyle(Paint.Style.STROKE); + inactivePaint.setAntiAlias(true); + inactivePaint.setColor(colorInactive); + + activePaint.setStrokeCap(Paint.Cap.ROUND); + activePaint.setStrokeWidth(strokeWidth); + activePaint.setStyle(Paint.Style.FILL); + activePaint.setAntiAlias(true); + activePaint.setColor(colorActive); + + this.indicatorItemPadding = padding; + this.indicatorHeight = indicatorHeight; + } + + @Override + public void onDrawOver(@NotNull Canvas c, @NotNull RecyclerView parent, @NotNull RecyclerView.State state) { + super.onDrawOver(c, parent, state); + + final RecyclerView.Adapter adapter = parent.getAdapter(); + + if (adapter == null) { + return; + } + + int itemCount = adapter.getItemCount(); + + // center horizontally, calculate width and subtract half from center + float totalLength = this.radius * 2 * itemCount; + float paddingBetweenItems = Math.max(0, itemCount - 1) * indicatorItemPadding; + float indicatorTotalWidth = totalLength + paddingBetweenItems; + float indicatorStartX = (parent.getWidth() - indicatorTotalWidth) / 2f; + + // center vertically in the allotted space + float indicatorPosY = parent.getHeight() - indicatorHeight / 2f; + + drawInactiveDots(c, indicatorStartX, indicatorPosY, itemCount); + + final int activePosition; + + if (parent.getLayoutManager() instanceof GridLayoutManager) { + activePosition = ((GridLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition(); + } else if (parent.getLayoutManager() instanceof LinearLayoutManager) { + activePosition = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition(); + } else { + // not supported layout manager + return; + } + + if (activePosition == RecyclerView.NO_POSITION) { + return; + } + + // find offset of active page if the user is scrolling + final View activeChild = parent.getLayoutManager().findViewByPosition(activePosition); + if (activeChild == null) { + return; + } + + drawActiveDot(c, indicatorStartX, indicatorPosY, activePosition); + } + + private void drawInactiveDots(Canvas c, float indicatorStartX, float indicatorPosY, int itemCount) { + // width of item indicator including padding + final float itemWidth = this.radius * 2 + indicatorItemPadding; + + float start = indicatorStartX + radius; + for (int i = 0; i < itemCount; i++) { + c.drawCircle(start, indicatorPosY, radius, inactivePaint); + start += itemWidth; + } + } + + private void drawActiveDot(Canvas c, float indicatorStartX, float indicatorPosY, + int highlightPosition) { + // width of item indicator including padding + final float itemWidth = this.radius * 2 + indicatorItemPadding; + float highlightStart = indicatorStartX + radius + itemWidth * highlightPosition; + c.drawCircle(highlightStart, indicatorPosY, radius, activePaint); + } + + @Override + public void getItemOffsets(@NotNull Rect outRect, @NotNull View view, @NotNull RecyclerView parent, @NotNull RecyclerView.State state) { + super.getItemOffsets(outRect, view, parent, state); + outRect.bottom = indicatorHeight; + } +} diff --git a/app/src/main/java/com/nynja/mobile/communicator/ui/wheel/entity/Factory.java b/app/src/main/java/com/nynja/mobile/communicator/ui/wheel/entity/Factory.java index 6d865403a9789e32463ef978f935de7988505d70..047e26a48a2dd645c5f0dd78632dcb7e05df68cc 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/ui/wheel/entity/Factory.java +++ b/app/src/main/java/com/nynja/mobile/communicator/ui/wheel/entity/Factory.java @@ -541,13 +541,13 @@ public class Factory { .enableBgColor(isSubItems ? R.color.wheel_unselected_corner_bg : R.color.intercom_full_transparent_full_black) .build()); - // add(SimpleWheelItem.newBuilder() - // .wheelAction(HomeActions.VideoCallAction) - // .text(R.string.video_call) - // .icons(R.drawable.ic_new_video_call_vector, 0) - // .enableBgColor(isSubItems ? R.color.wheel_unselected_corner_bg : R.color.intercom_full_transparent_full_black) - // .build()); - // + add(SimpleWheelItem.newBuilder() + .wheelAction(HomeActions.VideoCallAction) + .text(R.string.video_call) + .icons(R.drawable.ic_new_video_call_vector, 0) + .enableBgColor(isSubItems ? R.color.wheel_unselected_corner_bg : R.color.intercom_full_transparent_full_black) + .build()); + add(SimpleWheelItem.newBuilder() .wheelAction(HomeActions.CallHistory) diff --git a/app/src/main/java/com/nynja/mobile/communicator/utils/Consts.java b/app/src/main/java/com/nynja/mobile/communicator/utils/Consts.java index 3a4cf23d080a556492a4b2ab6f6329a4c1950a5b..8dc5ca8301d53dedbd0225f4114024480a846709 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/utils/Consts.java +++ b/app/src/main/java/com/nynja/mobile/communicator/utils/Consts.java @@ -116,13 +116,19 @@ public interface Consts { // Incoming call showing as a popup Notification boolean SHOW_INCOMING_CALLS_AS_POPUP_NOTIFICATIONS = false; - // Samsung IAP + // Samsung + // IAP boolean CLIENT_SIDE_MODERATOR_END_CONFERENCE = true;//BuildConfig.FLAVOR.toLowerCase().contentEquals("samsung"); boolean ENABLE_SAMSUNG_IAP_TEST_MODE = false; long SHOW_TRIAL_PEROIOD_MSG_AT_START = 30; // in seconds long SHOW_TRIAL_PEROIOD_VALUE_5 = 5; // in minutes long SHOW_TRIAL_PEROIOD_VALUE_10 = 10; // in minutes int SHOW_TRIAL_PEROIOD_END_TIME = 40; // in minutes + // Video conference + //boolean CLIENT_ALLOWED_VIDEO_CONFERENCE = (BuildConfig.FLAVOR.toLowerCase().contentEquals("samsung") + // || BuildConfig.FLAVOR.toLowerCase().contentEquals("samsungdev") + // || BuildConfig.FLAVOR.toLowerCase().contentEquals("samsungpreprod")); + boolean CLIENT_ALLOWED_VIDEO_CONFERENCE = (BuildConfig.APPLICATION_ID.toLowerCase().contentEquals("com.nynja.mobile.communicator.superapp")); // Default Max length restriction of group chat (room) name - on creation int MAX_GROUP_NAME_SIZE_RESTRICTION = 32; diff --git a/app/src/main/res/drawable/ic_mute_mic_icon.xml b/app/src/main/res/drawable/ic_mute_mic_icon.xml new file mode 100644 index 0000000000000000000000000000000000000000..ee444f25f19fdfe415aefb90a537bff3e0742b96 --- /dev/null +++ b/app/src/main/res/drawable/ic_mute_mic_icon.xml @@ -0,0 +1,15 @@ + + + + + + + + + diff --git a/app/src/main/res/drawable/video_feed_square_stroke_green.xml b/app/src/main/res/drawable/video_feed_square_stroke_green.xml new file mode 100644 index 0000000000000000000000000000000000000000..ec8f78faa7463c0421e66b6b9ba3d70eff7ef34a --- /dev/null +++ b/app/src/main/res/drawable/video_feed_square_stroke_green.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/drawable/video_feed_square_stroke_red.xml b/app/src/main/res/drawable/video_feed_square_stroke_red.xml new file mode 100644 index 0000000000000000000000000000000000000000..04f48350efd902a5f151bcc7634f4893954e6fd2 --- /dev/null +++ b/app/src/main/res/drawable/video_feed_square_stroke_red.xml @@ -0,0 +1,9 @@ + + + + + + \ No newline at end of file diff --git a/app/src/main/res/layout/activity_rv_call.xml b/app/src/main/res/layout/activity_rv_call.xml new file mode 100644 index 0000000000000000000000000000000000000000..26a98b0d3c92a0d2aade003d5f69b5559a4d9d28 --- /dev/null +++ b/app/src/main/res/layout/activity_rv_call.xml @@ -0,0 +1,14 @@ + + + + + + + diff --git a/app/src/main/res/layout/fragment_conference_video_call.xml b/app/src/main/res/layout/fragment_conference_video_call.xml new file mode 100644 index 0000000000000000000000000000000000000000..260e83a9429f84f343cfb8849abae09268567196 --- /dev/null +++ b/app/src/main/res/layout/fragment_conference_video_call.xml @@ -0,0 +1,13 @@ + + + + + + + diff --git a/app/src/main/res/layout/li_conference_participant.xml b/app/src/main/res/layout/li_conference_participant.xml index b01f8f61d52355295886974c2d0a00089c5d10a4..4977502ffeb69b0a25130160cc83f1c8d1d8a659 100644 --- a/app/src/main/res/layout/li_conference_participant.xml +++ b/app/src/main/res/layout/li_conference_participant.xml @@ -33,9 +33,7 @@ android:visibility="gone" app:layout_constraintBottom_toBottomOf="parent" app:layout_constraintLeft_toLeftOf="parent" - app:layout_constraintRight_toRightOf="parent" - app:layout_constraintTop_toTopOf="parent" - app:srcCompat="@drawable/v_ic_mute_member" /> + app:srcCompat="@drawable/ic_mute_mic_icon" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values-en/strings.xml b/app/src/main/res/values-en/strings.xml index 744b3f055f92ea0ff4870657c97428343587640f..70c50b5768ca816e601df35578e92cdd36911c93 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -1330,4 +1330,11 @@ Create New NYNJA Account Use Another Account + + Please stop your screen share first + Cannot start camera because it has been already started by another participant + Please stop your camera first + Cannot start screen share because it has been already started by another participant + Start camera is not available for groups + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index f55d6f169d4cd273261e2f72d423feee547e8376..a4c40ba95768c269b34ecbd1bb60c06ad5dcc3bb 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1329,4 +1329,11 @@ Create New NYNJA Account Use Another Account + + Please stop your screen share first + Cannot start camera because it has been already started by another participant + Please stop your camera first + Cannot start screen share because it has been already started by another participant + Start camera is not available for groups + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 7b909eb3a9fce91a1678b3a379b14f7bb02e6729..0ff8c721e1167a0c73313f37733efa8ef841d7c6 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -1329,4 +1329,11 @@ Create New NYNJA Account Use Another Account + + Please stop your screen share first + Cannot start camera because it has been already started by another participant + Please stop your camera first + Cannot start screen share because it has been already started by another participant + Start camera is not available for groups + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 9df49b104b33e8d47db711e2f0c3e67bd6ee3359..5ccdce3a7a6e16dc45a822f44de0d66df1ab6ca6 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1,9 +1,9 @@ - + 欢迎来到 新手上路 - + 选择一个国家 好的 错误的国家代码 @@ -58,7 +58,7 @@ 名字必须在2-32个符号之间且中间不得超过一个空格。 姓氏必须在2-32个符号之间且中间不得超过一个空格。 用户名必须在2-32个符号之间且可以包含拉丁字母,数字和下划线。 - + 账户设置 删除账户 状态 @@ -87,7 +87,7 @@ 如果您删除了您的账户,您将永久无法再访问您的钱包,账户数据,信息和多媒体资料。此项操作是无法撤销的。\n\n删除您的账户即代表您确认收回您对NYNJA使用条款的同意。\n\n同时,请确保您已经保存好了12个单词种子,以便之后在其他平台重新生成您的密钥。 您确定想要删除您的账户吗?此项操作无法撤销。 您是群聊唯一的管理员。您在分配另一管理员之后可删除您的账户。 - + 电话号码 电子邮箱 Facebook @@ -109,14 +109,14 @@ Facebook链接 Twitter链接 对不起,现在无法添加联系人信息。 - + 电子邮箱 - + 语音信息 - + !警告! 您的设备似乎刷过机了。您的应用程式的安全性可能会受到损害。 - + 通用 登出 @@ -124,12 +124,12 @@ 删除用户 日历\配置 日历配置 - + 已选定日历集成 Google日历 Microsoft 365 Outlook日历 - + 自动创建群聊进行群呼叫 无信息时删除群聊 @@ -143,7 +143,7 @@ A 下午7:23 下午7:24 - + 扫描二维码 保存至图库 @@ -153,20 +153,20 @@ 加入群聊失败 您想要加入群聊:%1$s ? 相册 - + 从图库选择 从相机拍摄 从图库选择 取消 添加联系人 - + 无网络连接 用户未找到 可用空间不足 文件未保存至下载 此代码已发送。 - + 添加联系人 从我的通讯录添加NYNJA用户 通过短信邀请 @@ -178,11 +178,11 @@ 联系历史 添加Nynja用户 联系历史 - + 添加 忽视 - + 操作 聊天 @@ -198,7 +198,7 @@ 帮助和\支持 日历 教程 - + %1$s %2$s 用户名: %1$s @@ -231,7 +231,7 @@ 隐藏 标为已读 标为未读 - + 个人资料设置 个人资料信息 @@ -239,7 +239,7 @@ 空闲超时 用户名\u002A 电子邮箱 - + 新联系人 全部 @@ -261,7 +261,7 @@ 方向盘\n位置 更改\号码 更改号码 - + 历史 按二维\n码 @@ -271,7 +271,7 @@ 按用户名 按联系人 邀请好友 - + 联系人 视频通话 @@ -282,7 +282,7 @@ 位置 视频 加入会议 - + 最近 最近聊天 @@ -290,7 +290,7 @@ 家庭 工作 已预约发送 - + 我的二维\n码 编辑账户 @@ -302,20 +302,20 @@ 更改\n号码 我的二维码 群聊\n选项 - + 输入电子邮箱地址 输入电话号码 输入用户名 用户未找到 未找到结果 - + 接受 已添加 已请求 历史 - + 呼叫历史 呼叫历史 @@ -336,7 +336,7 @@ %1$sh %1$sm %1$ss - + 切换至音频 接受音频 @@ -414,9 +414,9 @@ 说话者 耳机 呼叫未找到 - - - + + + 活跃 空闲 @@ -455,7 +455,7 @@ 替换 Keep 此对话草稿已保存在服务器上。您想要删除您当前的草稿信息吗? - + 出错 移动通信器 永不回头 @@ -490,11 +490,11 @@ 翻译失败。请重试。 转录失败。请重试。 您有另外一个通话正在进行中。请完成您的其他通话并重试。 - + 打开 复制链接 - + 新的群聊 群聊名称*: @@ -520,14 +520,14 @@ 新用户在通话中没有位置 新用户在通话中只有%1$d位置 复制链接 - + 余额 显示全部 星标信息 进入聊天 进入群聊 进入星标信息 - + 未读信息 未读群聊信息 联系人请求 @@ -537,7 +537,7 @@ %1$d位成员,%2$d位处于活跃状态 %1$d位成员 您已经从此群聊中被移出 - + 编辑姓名 保留您的现有姓名。 @@ -547,7 +547,7 @@ 不要添加用户名。 您可以在NYNJA上选择一个用户名。在您选定之后,其他人将能够通过此用户名找到您,他们无需知道您的电话号码就可以与您取得联系。 保留您的现有用户名。 - + 群组规则 通知 @@ -569,7 +569,7 @@ 添加为成员 呼叫 您不是管理员。请申请本群聊的添加权限。 - + 点击两次退出。 对不起,此用户名是无效的 用户名必须至少2个字符 @@ -599,7 +599,7 @@ %2$d中的%1$d发送中 %1$s加入了群聊 %1$s加入了群聊 - + 翻译接收信息的语言 发送已翻译信息的语言 @@ -618,11 +618,11 @@ 自动 收到 关闭 - + 翻译接收信息 翻译发送信息 将语音信息转录成文本 - + 发送时的翻译语言 接收时自动翻译 发送时自动翻译 @@ -648,23 +648,23 @@ 相机 更多 频道列表 - + 粘贴 - + 今天 上午 下午 日期 时间 - + 小时 分钟 : 此处繁忙 + - + 星标 未加星标 @@ -693,15 +693,15 @@ 停止共享 添加至日历 邀请您加入NYNJA会议。加入信息以关注。 - + 删除双方信息 删除我的信息 删除全部信息 - + 回复 活跃 - + 语音信息 联系人 @@ -713,7 +713,7 @@ 已删除信息 语音通话 视频通话 - + 发送信息至: 文本信息: @@ -730,11 +730,11 @@ 选定的日期和/或时间是无效的。请一定要选择一个有效的日期和时间。 信息已经成功\n预约发送 您无法预约发送音频信息,因为您无法连接到服务器。 - + 已加星标信息 已添加:%1$s - + 用户名可以包含英文字母,数字和下划线 错误的用户名格式 返回主页 @@ -751,13 +751,13 @@ 您正在使用的是一个过时的应用版本。请确保从Play Store下载最新版本。如果您现在的版本是从Play Store下载的最新版并且您仍然会收到此信息,请稍作坚持,等待开发团队为您发布新版本。 Send Location发送位置 搜索或选择一个地方 - + 地图 精确到%1$d米 卫星 混合 无结果 - + 全部 文本 @@ -768,7 +768,7 @@ 音频 联系人 位置 - + 照片 视频 @@ -778,7 +778,7 @@ 无数据 不支持附件 已编辑 - + 已选定 未选定聊天 @@ -787,7 +787,7 @@ 请至少选个一个聊天 \u0020和%d其他 \u0020和%d其他 - + 已加入黑名单 邀请好友 @@ -795,12 +795,12 @@ 介绍一款名叫NYNJA的超棒的全新平台!这是一个集工作效率和沟通通信于一体的平台。在这里下载它:https://www.nynja.io/mobile并添加我为联系人,我的NYNJA用户名:%1$s 邀请至Nynja 联系卡 - + 已加入黑名单联系人 - + Google服务必需使用此功能 - + 星期一 星期二 @@ -809,7 +809,7 @@ 星期五 星期六 星期日 - + 星期一 星期二 星期三 @@ -817,7 +817,7 @@ 星期五 星期六 星期日 - + 共享 分享音频 @@ -826,11 +826,11 @@ 分享文件 分享链接 分享联系人 - + NYNJA for Android\n%s 版本 版本号 - + 当前会话 中止全部其他会话 @@ -847,15 +847,15 @@ https://nynja.work/how-to-videos https://nynja.work/privacy-policy https://nynja.work/terms-of-use - + 中止本会话? 请检查网络连接并重试。 对不起,我们在没有网络连接的情况下无法进行搜索。 已删除参与人 已删除联系人 - + 进入联系历史 - + 选择国家 当前号码: @@ -872,7 +872,7 @@ 您应当在%d秒内收到它。 分享NYNJA 数据和存储 - + 新消息提醒 呼叫通知 @@ -889,7 +889,7 @@ NYNJA Android NYNJA iOS 已发信息音 - + 方向盘位置 选择方向盘位置 @@ -897,14 +897,14 @@ 右手 (当前位置) 使用此方向盘位置? - + 主题 选择最适合您的主题 黑暗的归途 天国的阶梯 使用此主题? - + 自动多媒体下载 本地存储使用 当使用移动数据时 @@ -924,7 +924,7 @@ GIFs 转录 转录中… - + 时间表设置 显示发送信息 @@ -939,7 +939,7 @@ 本周 最后7天 最后30天 - + 频道名称* 频道链接* @@ -955,7 +955,7 @@ 您的频道列表是空的。您可以创建您自己的频道。 对不起,无订阅者可供选择 订阅者 - + 网格 扫描二维码 @@ -985,7 +985,7 @@ 相机正在被一款其他应用在使用。 麦克风正在通话中使用。在通话中静音即可开始录制视频。 麦克风正在被一款其他有应用在使用。 - + 未找到任何结果…\n尝试另一次搜索 此链接已经在使用 群聊照片 @@ -1002,12 +1002,12 @@ 剪切 复制 标签 - + 加入 播放... 您不再具有查看此频道内容的权限。 管理员已经将您屏蔽。 - + %1$d订阅者 %1$d订阅者 @@ -1016,7 +1016,7 @@ %1$d订阅者 %1$d订阅者 - + 创建钱包 钱包 @@ -1065,14 +1065,14 @@ 最多500个符号 用户未找到 生成种子 - + 新的信息 未知错误 频道信息 频道已存在 - + 即将推出 - + 市场 自由职业 @@ -1095,7 +1095,7 @@ 口译类型 搜索口译人员 总价格 - + 输入口译时间 (1-300) 时间范围从1到300分钟 请选择您想要进行口译的源语言 @@ -1128,7 +1128,7 @@ 口译人员分配中 NYNJA正在为您寻找完美的口译人员。继续使用本应用,我们将在口译人员准备好开始通话时通知您。 即将推出 - + 0.0 K/s @@ -1138,7 +1138,7 @@ 好友通知 添加好友请求通知 呼叫通知 - + %1$s想要在NYNJA添加您为好友! 通过短信邀请 @@ -1171,14 +1171,14 @@ 直接拨打电话号码 发送短信信息 设置 - + 现在 一分钟前 %d分钟前 今天 昨天 - + 内部错误。请稍后再试 验证码不正确 @@ -1194,13 +1194,13 @@ 电话号码是无效的 电话号码已在使用 请检查您所输入的数据 - + 分享内容访问失败。 不支持功能 服务台 服务台目前不可用。请稍后再试! - + 无已加入黑名单联系人 Home phone @@ -1329,4 +1329,11 @@ Create New NYNJA Account Use Another Account + + Please stop your screen share first + Cannot start camera because it has been already started by another participant + Please stop your camera first + Cannot start screen share because it has been already started by another participant + Start camera is not available for groups + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 9df49b104b33e8d47db711e2f0c3e67bd6ee3359..5ccdce3a7a6e16dc45a822f44de0d66df1ab6ca6 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1,9 +1,9 @@ - + 欢迎来到 新手上路 - + 选择一个国家 好的 错误的国家代码 @@ -58,7 +58,7 @@ 名字必须在2-32个符号之间且中间不得超过一个空格。 姓氏必须在2-32个符号之间且中间不得超过一个空格。 用户名必须在2-32个符号之间且可以包含拉丁字母,数字和下划线。 - + 账户设置 删除账户 状态 @@ -87,7 +87,7 @@ 如果您删除了您的账户,您将永久无法再访问您的钱包,账户数据,信息和多媒体资料。此项操作是无法撤销的。\n\n删除您的账户即代表您确认收回您对NYNJA使用条款的同意。\n\n同时,请确保您已经保存好了12个单词种子,以便之后在其他平台重新生成您的密钥。 您确定想要删除您的账户吗?此项操作无法撤销。 您是群聊唯一的管理员。您在分配另一管理员之后可删除您的账户。 - + 电话号码 电子邮箱 Facebook @@ -109,14 +109,14 @@ Facebook链接 Twitter链接 对不起,现在无法添加联系人信息。 - + 电子邮箱 - + 语音信息 - + !警告! 您的设备似乎刷过机了。您的应用程式的安全性可能会受到损害。 - + 通用 登出 @@ -124,12 +124,12 @@ 删除用户 日历\配置 日历配置 - + 已选定日历集成 Google日历 Microsoft 365 Outlook日历 - + 自动创建群聊进行群呼叫 无信息时删除群聊 @@ -143,7 +143,7 @@ A 下午7:23 下午7:24 - + 扫描二维码 保存至图库 @@ -153,20 +153,20 @@ 加入群聊失败 您想要加入群聊:%1$s ? 相册 - + 从图库选择 从相机拍摄 从图库选择 取消 添加联系人 - + 无网络连接 用户未找到 可用空间不足 文件未保存至下载 此代码已发送。 - + 添加联系人 从我的通讯录添加NYNJA用户 通过短信邀请 @@ -178,11 +178,11 @@ 联系历史 添加Nynja用户 联系历史 - + 添加 忽视 - + 操作 聊天 @@ -198,7 +198,7 @@ 帮助和\支持 日历 教程 - + %1$s %2$s 用户名: %1$s @@ -231,7 +231,7 @@ 隐藏 标为已读 标为未读 - + 个人资料设置 个人资料信息 @@ -239,7 +239,7 @@ 空闲超时 用户名\u002A 电子邮箱 - + 新联系人 全部 @@ -261,7 +261,7 @@ 方向盘\n位置 更改\号码 更改号码 - + 历史 按二维\n码 @@ -271,7 +271,7 @@ 按用户名 按联系人 邀请好友 - + 联系人 视频通话 @@ -282,7 +282,7 @@ 位置 视频 加入会议 - + 最近 最近聊天 @@ -290,7 +290,7 @@ 家庭 工作 已预约发送 - + 我的二维\n码 编辑账户 @@ -302,20 +302,20 @@ 更改\n号码 我的二维码 群聊\n选项 - + 输入电子邮箱地址 输入电话号码 输入用户名 用户未找到 未找到结果 - + 接受 已添加 已请求 历史 - + 呼叫历史 呼叫历史 @@ -336,7 +336,7 @@ %1$sh %1$sm %1$ss - + 切换至音频 接受音频 @@ -414,9 +414,9 @@ 说话者 耳机 呼叫未找到 - - - + + + 活跃 空闲 @@ -455,7 +455,7 @@ 替换 Keep 此对话草稿已保存在服务器上。您想要删除您当前的草稿信息吗? - + 出错 移动通信器 永不回头 @@ -490,11 +490,11 @@ 翻译失败。请重试。 转录失败。请重试。 您有另外一个通话正在进行中。请完成您的其他通话并重试。 - + 打开 复制链接 - + 新的群聊 群聊名称*: @@ -520,14 +520,14 @@ 新用户在通话中没有位置 新用户在通话中只有%1$d位置 复制链接 - + 余额 显示全部 星标信息 进入聊天 进入群聊 进入星标信息 - + 未读信息 未读群聊信息 联系人请求 @@ -537,7 +537,7 @@ %1$d位成员,%2$d位处于活跃状态 %1$d位成员 您已经从此群聊中被移出 - + 编辑姓名 保留您的现有姓名。 @@ -547,7 +547,7 @@ 不要添加用户名。 您可以在NYNJA上选择一个用户名。在您选定之后,其他人将能够通过此用户名找到您,他们无需知道您的电话号码就可以与您取得联系。 保留您的现有用户名。 - + 群组规则 通知 @@ -569,7 +569,7 @@ 添加为成员 呼叫 您不是管理员。请申请本群聊的添加权限。 - + 点击两次退出。 对不起,此用户名是无效的 用户名必须至少2个字符 @@ -599,7 +599,7 @@ %2$d中的%1$d发送中 %1$s加入了群聊 %1$s加入了群聊 - + 翻译接收信息的语言 发送已翻译信息的语言 @@ -618,11 +618,11 @@ 自动 收到 关闭 - + 翻译接收信息 翻译发送信息 将语音信息转录成文本 - + 发送时的翻译语言 接收时自动翻译 发送时自动翻译 @@ -648,23 +648,23 @@ 相机 更多 频道列表 - + 粘贴 - + 今天 上午 下午 日期 时间 - + 小时 分钟 : 此处繁忙 + - + 星标 未加星标 @@ -693,15 +693,15 @@ 停止共享 添加至日历 邀请您加入NYNJA会议。加入信息以关注。 - + 删除双方信息 删除我的信息 删除全部信息 - + 回复 活跃 - + 语音信息 联系人 @@ -713,7 +713,7 @@ 已删除信息 语音通话 视频通话 - + 发送信息至: 文本信息: @@ -730,11 +730,11 @@ 选定的日期和/或时间是无效的。请一定要选择一个有效的日期和时间。 信息已经成功\n预约发送 您无法预约发送音频信息,因为您无法连接到服务器。 - + 已加星标信息 已添加:%1$s - + 用户名可以包含英文字母,数字和下划线 错误的用户名格式 返回主页 @@ -751,13 +751,13 @@ 您正在使用的是一个过时的应用版本。请确保从Play Store下载最新版本。如果您现在的版本是从Play Store下载的最新版并且您仍然会收到此信息,请稍作坚持,等待开发团队为您发布新版本。 Send Location发送位置 搜索或选择一个地方 - + 地图 精确到%1$d米 卫星 混合 无结果 - + 全部 文本 @@ -768,7 +768,7 @@ 音频 联系人 位置 - + 照片 视频 @@ -778,7 +778,7 @@ 无数据 不支持附件 已编辑 - + 已选定 未选定聊天 @@ -787,7 +787,7 @@ 请至少选个一个聊天 \u0020和%d其他 \u0020和%d其他 - + 已加入黑名单 邀请好友 @@ -795,12 +795,12 @@ 介绍一款名叫NYNJA的超棒的全新平台!这是一个集工作效率和沟通通信于一体的平台。在这里下载它:https://www.nynja.io/mobile并添加我为联系人,我的NYNJA用户名:%1$s 邀请至Nynja 联系卡 - + 已加入黑名单联系人 - + Google服务必需使用此功能 - + 星期一 星期二 @@ -809,7 +809,7 @@ 星期五 星期六 星期日 - + 星期一 星期二 星期三 @@ -817,7 +817,7 @@ 星期五 星期六 星期日 - + 共享 分享音频 @@ -826,11 +826,11 @@ 分享文件 分享链接 分享联系人 - + NYNJA for Android\n%s 版本 版本号 - + 当前会话 中止全部其他会话 @@ -847,15 +847,15 @@ https://nynja.work/how-to-videos https://nynja.work/privacy-policy https://nynja.work/terms-of-use - + 中止本会话? 请检查网络连接并重试。 对不起,我们在没有网络连接的情况下无法进行搜索。 已删除参与人 已删除联系人 - + 进入联系历史 - + 选择国家 当前号码: @@ -872,7 +872,7 @@ 您应当在%d秒内收到它。 分享NYNJA 数据和存储 - + 新消息提醒 呼叫通知 @@ -889,7 +889,7 @@ NYNJA Android NYNJA iOS 已发信息音 - + 方向盘位置 选择方向盘位置 @@ -897,14 +897,14 @@ 右手 (当前位置) 使用此方向盘位置? - + 主题 选择最适合您的主题 黑暗的归途 天国的阶梯 使用此主题? - + 自动多媒体下载 本地存储使用 当使用移动数据时 @@ -924,7 +924,7 @@ GIFs 转录 转录中… - + 时间表设置 显示发送信息 @@ -939,7 +939,7 @@ 本周 最后7天 最后30天 - + 频道名称* 频道链接* @@ -955,7 +955,7 @@ 您的频道列表是空的。您可以创建您自己的频道。 对不起,无订阅者可供选择 订阅者 - + 网格 扫描二维码 @@ -985,7 +985,7 @@ 相机正在被一款其他应用在使用。 麦克风正在通话中使用。在通话中静音即可开始录制视频。 麦克风正在被一款其他有应用在使用。 - + 未找到任何结果…\n尝试另一次搜索 此链接已经在使用 群聊照片 @@ -1002,12 +1002,12 @@ 剪切 复制 标签 - + 加入 播放... 您不再具有查看此频道内容的权限。 管理员已经将您屏蔽。 - + %1$d订阅者 %1$d订阅者 @@ -1016,7 +1016,7 @@ %1$d订阅者 %1$d订阅者 - + 创建钱包 钱包 @@ -1065,14 +1065,14 @@ 最多500个符号 用户未找到 生成种子 - + 新的信息 未知错误 频道信息 频道已存在 - + 即将推出 - + 市场 自由职业 @@ -1095,7 +1095,7 @@ 口译类型 搜索口译人员 总价格 - + 输入口译时间 (1-300) 时间范围从1到300分钟 请选择您想要进行口译的源语言 @@ -1128,7 +1128,7 @@ 口译人员分配中 NYNJA正在为您寻找完美的口译人员。继续使用本应用,我们将在口译人员准备好开始通话时通知您。 即将推出 - + 0.0 K/s @@ -1138,7 +1138,7 @@ 好友通知 添加好友请求通知 呼叫通知 - + %1$s想要在NYNJA添加您为好友! 通过短信邀请 @@ -1171,14 +1171,14 @@ 直接拨打电话号码 发送短信信息 设置 - + 现在 一分钟前 %d分钟前 今天 昨天 - + 内部错误。请稍后再试 验证码不正确 @@ -1194,13 +1194,13 @@ 电话号码是无效的 电话号码已在使用 请检查您所输入的数据 - + 分享内容访问失败。 不支持功能 服务台 服务台目前不可用。请稍后再试! - + 无已加入黑名单联系人 Home phone @@ -1329,4 +1329,11 @@ Create New NYNJA Account Use Another Account + + Please stop your screen share first + Cannot start camera because it has been already started by another participant + Please stop your camera first + Cannot start screen share because it has been already started by another participant + Start camera is not available for groups + diff --git a/app/src/main/res/values/dimen.xml b/app/src/main/res/values/dimen.xml index ec7e8a434ecd6a90f47ab477185a0a9db46822f0..28d7133a14a04d43116a8f748218c2faaae3bd5c 100644 --- a/app/src/main/res/values/dimen.xml +++ b/app/src/main/res/values/dimen.xml @@ -260,4 +260,9 @@ 30dp + + 12dp + 5dp + 8dp + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4bf45727a75433310db7824074b7efbbc29db207..011d976b8a79c9cb91f1d439ae8eca248392bbcb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1330,4 +1330,12 @@ Click \"Create New NYNJA Account\" below to confirm. If you already have a NYNJA account, click \"Use Another Account\" Create New NYNJA Account Use Another Account + + + Please stop your screen share first + Cannot start camera because it has been already started by another participant + Please stop your camera first + Cannot start screen share because it has been already started by another participant + Start camera is not available for groups +