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 index 95e50ef93518fa0a65bd4b51ac41bfa8d3d0de80..21c32cc16055c1af1ebbd8fcd2ac57aa22ba7195 100644 --- 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 @@ -12,9 +12,14 @@ import org.webrtc.SurfaceViewRenderer; import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.Map; import java.util.Set; import kotlin.jvm.Synchronized; +import timber.log.Timber; public class ConferenceVideoModule { @@ -28,7 +33,7 @@ public class ConferenceVideoModule { private ActiveConferenceCall mCurrentActiveCall; public int mActiveVideoParticipantsCount = 0; - public HashMap mActiveVideoParticipantsMap= new HashMap<>(); + public LinkedHashSet mActiveVideoParticipants= new LinkedHashSet<>(); private ConferenceVideoModule() { @@ -51,7 +56,7 @@ public class ConferenceVideoModule { public void reset() { // ToDo:... - mActiveVideoParticipantsMap.clear(); + mActiveVideoParticipants.clear(); mCurrentActiveCall = null; } @@ -85,38 +90,110 @@ public class ConferenceVideoModule { return trackId; } - @Synchronized - public ArrayList getConferenceVideoMembers(ActiveConferenceCall activeConferenceCall) { + public synchronized ArrayList getConferenceVideoMembers(ActiveConferenceCall activeConferenceCall) { if (activeConferenceCall == null) return new ArrayList<>(); - Set participatIds = mActiveVideoParticipantsMap.keySet(); - mActiveVideoParticipantsMap.clear(); - synchronized (activeConferenceCall.mData.mParticipantArray) { + HashSet mTempActiveVideoParticipants= new HashSet<>(); + HashSet tempParticipants= new HashSet<>(); + synchronized (activeConferenceCall.mData) { 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); + ConferenceListItem item = conferenceParticipant(participant, participant.isOwner(), activeConferenceCall.mData.isOwnStreamActive); + tempParticipants.add(item); + if (//participant.hasVideo() || + activeConferenceCall.mData.mActiveParticipanTracks.containsValue(participant.getParticipantId())) { + ////////////////////////////////////////////////////////////////////////////////////////////////// + // WORKAROUND !!!!!!!!!! + item.hasVideo = true; + item.isMe = false; + ////////////////////////////////////////////////////////////////////////////////////////////////// + Timber.d("ConferenceVideoModule::getConferenceVideoMembers(): updated video feed item participant name=%s; ID=%s", item.name, item.participantId); + mTempActiveVideoParticipants.add(item); + } else if (//participant.hasVideo() && + participant.getParticipantId().contentEquals(activeConferenceCall.mConference.participantId()) && + participant.isMe() && + activeConferenceCall.mData.isOwnStreamActive) { + ////////////////////////////////////////////////////////////////////////////////////////////////// + // WORKAROUND !!!!!!!!!! + item.hasVideo = true; + item.isMe = true; + ////////////////////////////////////////////////////////////////////////////////////////////////// + Timber.d("ConferenceVideoModule::getConferenceVideoMembers(): updated video feed item participant name=%s; ID=%s", item.name, item.participantId); + mTempActiveVideoParticipants.add(item); } } } - } - - for (String participantId : participatIds) { - if (!mActiveVideoParticipantsMap.containsKey(participantId)) { - enableVideoForTrack(participantId, false); - String trackId = getTrackIdByParticipantId(participantId); - setVideoRendererForTrack(null, trackId, StringUtils.isEmpty(trackId)); +// if (activeConferenceCall.mData.mActiveParticipanTracks != null) { +// for (Map.Entry set : activeConferenceCall.mData.mActiveParticipanTracks.entrySet()) { +// NYNCallParticipant participant = activeConferenceCall.mConference.getParticipantByTrackId(set.getKey()); +// if (participant == null) participant = activeConferenceCall.mConference.getParticipantById(set.getValue()); +// if (participant != null && +// (participant.hasVideo() || +// (participant.isMe() && activeConferenceCall.mData.isOwnStreamActive) || +// activeConferenceCall.mData.mActiveParticipanTracks.containsKey(participant.getParticipantId()))) { +// ConferenceListItem item = conferenceParticipant(participant, participant.isOwner(), activeConferenceCall.mData.isOwnStreamActive); +// item.hasVideo = true; +// item.participantId = set.getValue(); +// mActiveVideoParticipantsMap.put(item.participantId, item); +// } +// } +// } + Iterator itr = mActiveVideoParticipants.iterator(); + while(itr.hasNext()) { + ConferenceListItem item = itr.next(); + if (!mTempActiveVideoParticipants.contains(item)) { + Timber.d("ConferenceVideoModule::getConferenceVideoMembers(): removed participant with ID=%s", item.participantId); + enableVideoForTrack(item.participantId, false); + if (item.isMe) { + setVideoRendererForTrack(null, "", true); + } else { + String trackId = getTrackIdByParticipantId(item.participantId); + if (StringUtils.isNotEmpty(trackId)) { + setVideoRendererForTrack(null, trackId, false); + } + } + itr.remove(); + } else if (tempParticipants.contains(item)) { + Iterator itr2 = tempParticipants.iterator(); + while(itr2.hasNext()) { + ConferenceListItem item2 = itr2.next(); + if (item2.equals(item)) { + item.update(item2); + break; + } + } + } + } + Iterator itr2 = mTempActiveVideoParticipants.iterator(); + while(itr2.hasNext()) { + ConferenceListItem item = itr2.next(); + if (!mActiveVideoParticipants.contains(item)) { + Timber.d("ConferenceVideoModule::getConferenceVideoMembers(): added participant with ID=%s", item.participantId); + mActiveVideoParticipants.add(item); + } } } - ArrayList items = new ArrayList<>(mActiveVideoParticipantsMap.values()); + mTempActiveVideoParticipants.clear(); + ArrayList items = new ArrayList<>(mActiveVideoParticipants); mActiveVideoParticipantsCount = items.size(); + Timber.d("ConferenceVideoModule::getConferenceVideoMembers(): items new size=%d", mActiveVideoParticipantsCount); return items; } - @Synchronized - public ArrayList getUpdatedActiveSpeakersParticipants(ArrayList participantIds) { - ArrayList items = new ArrayList<>(mActiveVideoParticipantsMap.values()); + public synchronized void participantRemoved(String participantId) { + Iterator itr = mActiveVideoParticipants.iterator(); + while(itr.hasNext()) { + ConferenceListItem item = itr.next(); + if (!participantId.contentEquals(item.participantId)) { + itr.remove(); + break; + } + } + mActiveVideoParticipantsCount = mActiveVideoParticipants.size(); + } + + public synchronized ArrayList getUpdatedActiveSpeakersParticipants(ArrayList participantIds) { + ArrayList items = new ArrayList<>(mActiveVideoParticipants); for (ConferenceListItem item : items) { item.isSpeaking = false; for (String participandId : participantIds) { @@ -153,7 +230,7 @@ public class ConferenceVideoModule { 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 MAX_VIDEO_FEEDS_ROW_SIZE; + if ((position + 1 - lastPageBeginer)/MAX_VIDEO_FEEDS_COLUIMN_SIZE == MAX_VIDEO_FEEDS_ROW_SIZE) return MAX_VIDEO_FEEDS_ROW_SIZE + 1; if (row == 1) return row; if (row < MAX_VIDEO_FEEDS_ROW_SIZE) return row; return row - 1; @@ -220,6 +297,15 @@ public class ConferenceVideoModule { return MAX_VIDEO_FEEDS_COLUIMN_SIZE; } + public int getItemsPageCount() { + if (mActiveVideoParticipantsCount <= MAX_VIDEO_FEEDS_COLUIMN_SIZE) { + return 1; + } + int count = (mActiveVideoParticipantsCount / MAX_VIDEO_FEEDS_COUNT_PER_PAGE); + if ((mActiveVideoParticipantsCount - count*MAX_VIDEO_FEEDS_COUNT_PER_PAGE) % MAX_VIDEO_FEEDS_COUNT_PER_PAGE > 0) count++; + return count; + } + protected ConferenceListItem conferenceParticipant(NYNCallParticipant participant, boolean isModerator, boolean isOwnStreamActive) { 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 9c1ae7305cc79a10464f28039f47b09147fe0dd1..cf1044ccef8dc33743aa5c15334d1e6950a4d736 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 @@ -63,7 +63,7 @@ public abstract class ConferenceSDKPresenter extends Bas @Override public void onRemoteVideoTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) {} - @Override public void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) {} + @Override public void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId, String participantId) {} @Override public void onLocalVideoCapturerStarted(ActiveConferenceCall activeConferenceCall) {} 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 index 8f4042828852457c7412b222bb685a3a82509465..5fac62702aada9b24aad3a438ac3c028e1f06bc6 100644 --- 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 @@ -5,6 +5,7 @@ import com.nynja.sdk.NYNCallParticipant; import com.nynja.sdk.NYNCallParticipantArray; import java.util.HashMap; +import java.util.Map; /** * Created by Ergyun Syuleyman on 04/03/2020. @@ -19,8 +20,8 @@ public class ConferenceCallData { public String mRemoteSSTrackId; public String mRemoteVideoTrackId; public HashMap mActiveTracks; - public HashMap mActiveParticipanTracks; - public HashMap mActiveParticipanSSTracks; + public HashMap mActiveParticipanTracks; + public HashMap mActiveParticipanSSTracks; public boolean hasRemoteVideoTrack; public boolean hasRemoteScreenShareTrack; public boolean mIsConference; @@ -51,13 +52,13 @@ public class ConferenceCallData { public void onRemoteVideoAdded(String trackId, String participantId) { hasRemoteVideoTrack = true; mActiveTracks.put(trackId, participantId); - mActiveParticipanTracks.put(participantId, trackId); + mActiveParticipanTracks.put(trackId, participantId); mRemoteVideoTrackId = trackId; } public void onRemoteVideoRemoved(String trackId, String participantId) { mActiveTracks.remove(trackId); - mActiveParticipanTracks.remove(participantId); + mActiveParticipanTracks.remove(trackId); hasRemoteVideoTrack = !isRemoteVideoTracksEmpty(); mRemoteVideoTrackId = ""; } @@ -65,14 +66,14 @@ public class ConferenceCallData { public void onRemoteSSAdded(String trackId, String participantId) { hasRemoteScreenShareTrack = true; mActiveTracks.put(trackId, participantId); - mActiveParticipanSSTracks.put(participantId, trackId); + mActiveParticipanSSTracks.put(trackId, participantId); mRemoteSSTrackId = trackId; } public void onRemoteSSRemoved(String trackId, String participantId) { hasRemoteScreenShareTrack = false; mActiveTracks.remove(trackId); - mActiveParticipanSSTracks.remove(participantId); + mActiveParticipanSSTracks.remove(trackId); mRemoteSSTrackId = ""; } @@ -96,12 +97,16 @@ public class ConferenceCallData { protected String getTrackIdForParticipant(String participantId, boolean isVideo) { if (StringUtils.isEmpty(participantId)) return ""; if (isVideo) { - if (mActiveParticipanTracks.containsKey(participantId)) { - return mActiveParticipanTracks.get(participantId); + for (Map.Entry set : mActiveParticipanTracks.entrySet()) { + if (set.getValue().contentEquals(participantId)) { + return set.getKey(); + } } } else { - if (mActiveParticipanSSTracks.containsKey(participantId)) { - return mActiveParticipanSSTracks.get(participantId); + for (Map.Entry set : mActiveParticipanSSTracks.entrySet()) { + if (set.getValue().contentEquals(participantId)) { + return set.getKey(); + } } } 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 e44ee8610cdb0882da1e30bde6e954d364740698..518fd344c73a97caf8ef9a4b7cd4f5cc94722674 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 @@ -135,8 +135,8 @@ public class ConferenceListItem extends Object { this.hasScreen = participant.hasScreen; this.isVideoPaused = participant.isVideoPaused; this.isScreenPaused = participant.isScreenPaused; - this.isTrackActive = 0; - this.isMe = participant.isMe; +// this.isTrackActive = 0; +// this.isMe = participant.isMe; this.isFriend = participant.isFriend; this.isMuted = participant.isMuted; this.isSpeaking = participant.isSpeaking; 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 4710d6de072de19ab699bc03f4a91bb56dd74d0b..b6bbde5325b77787f8013b45603db66497af91da 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 @@ -48,7 +48,7 @@ public interface ConferenceSDKListener { void onRemoteVideoTrackAdded(ActiveConferenceCall activeConferenceCall, String trackId) ; - void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) ; + void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId, String participantId) ; void onLocalVideoCapturerStarted(ActiveConferenceCall activeConferenceCall) ; 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 dbc5d90c7fd59f6183b6fae3a672c816089f2244..911f83e2883503cc2d04b095f9c4ddf0227257bc 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 @@ -1678,7 +1678,7 @@ public class ConferenceSDKModule extends BaseSDKModule { if (mActiveConference.mConference == null) return; if (mActiveConference.mConference.callId().contentEquals(callId)) { for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { - conferenceSDKListener.onRemoteVideoTrackRemoved(mActiveConference, trackId); + conferenceSDKListener.onRemoteVideoTrackRemoved(mActiveConference, trackId, participantId); } } // if (isConference) { 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 aedd916dd5d24d2ad1f9930db4f193191997a98b..23b74d341ee06dbda76456e36180dee56a05fec7 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 @@ -43,7 +43,7 @@ public class CallActivityPresenter extends ConferenceSDKPresenter @Override public void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, - String trackId) { + String trackId, String participantId) { if (getAttachedViews().size() == 0) return; if (activeConferenceCall == null) return; if (activeConferenceCall.isConference()) return; 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 index 93ff6234b0e3ebb4d30a1f03899aace87af3a940..c1e6df26d1cac0eed419d56da022dfbc715b7d05 100644 --- 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 @@ -135,8 +135,9 @@ public class ConferenceVideoPresenter extends ConferenceSDKPresenter> exten private DisplayMetrics mDisplayMetrics = new DisplayMetrics(); private int mScreenWidth = 0; private int mScreenHeight = 0; - HashSet mHoldersSet; + LinkedHashSet< ConferenceVideoItemVh> mHolders; public ConferenceVideoAdapter(List list, OnConferenceVideoFeedClickListener onConferenceItemClickListener) { super(list); this.mOnConferenceVideoFeedClickListener = onConferenceItemClickListener; - mHoldersSet = new HashSet<>(); + mHolders = new LinkedHashSet<>(); setHasStableIds(true); } @@ -50,11 +54,21 @@ public class ConferenceVideoAdapter> exten @Override public long getItemId(int position) { if (position < getItemCount()) { - Timber.d("getItemId(): participantId = %s; feed ID = %d", getItem(position).participantId, - getItem(position).participantId.hashCode()); + Timber.d("ConferenceVideoAdapter::getItemId(): participantId=%s; feedID=%d, position=%d", getItem(position).participantId, + getItem(position).participantId.hashCode(), position); return getItem(position).participantId.hashCode(); } - return -1L; + return position; + } + + @Override + public int getItemsPageCount() { + return ConferenceVideoModule.getInstance().getItemsPageCount(); + } + + @Override + public int getMaxItemsPerPage() { + return ConferenceVideoModule.MAX_VIDEO_FEEDS_COUNT_PER_PAGE; } public void setIsModerator(boolean isModerator) { @@ -65,13 +79,53 @@ public class ConferenceVideoAdapter> exten return this.mIsModerator; } - public void releaseVideoCallRrenderers() { - for (ConferenceVideoItemVh vh : mHoldersSet) { + public void releaseVideoCallRrenderer(String participantId, boolean pause, boolean notify) { + Iterator itr = mHolders.iterator(); + while(itr.hasNext()) { + ConferenceVideoItemVh vh = itr.next(); + if (vh.getItem().participantId.contentEquals(participantId)) { + if (pause) { + onViewRecycled((T)vh); + } else { + releaseVideoCallRrenderer(vh, notify); + itr.remove(); + } + break; + } + } + } + + private void releaseVideoCallRrenderer(ConferenceVideoItemVh vh, boolean notify) { + String participantId = null; + if (vh != null) { + participantId = vh.getItem().participantId; vh.onViewRecycled(); vh.releaseVideoCallRrenderers(); } - mHoldersSet.clear(); + if (participantId == null) return; + int pos = 0; + for (ConferenceListItem item : mList) { + if (item.participantId.contentEquals(participantId)) { + mList.remove(participantId); + ConferenceVideoModule.getInstance().participantRemoved(participantId); + if (notify) notifyItemRemoved(pos); + break; + } + pos++; + } + } + public void releaseVideoCallRrenderer(ConferenceVideoItemVh vh) { + } + + public void releaseVideoCallRrenderers() { + Iterator itr = mHolders.iterator(); + while(itr.hasNext()) { + ConferenceVideoItemVh vh = itr.next(); + releaseVideoCallRrenderer(vh, false); + itr.remove(); + } + mHolders.clear(); clear(); } @@ -96,12 +150,13 @@ public class ConferenceVideoAdapter> exten mScreenWidth = mDisplayMetrics.widthPixels; mScreenHeight = (int) (mDisplayMetrics.heightPixels - mDisplayMetrics.densityDpi/mDisplayMetrics.density); } - mHoldersSet.add(viewHolder); + mHolders.add(viewHolder); return (T) viewHolder; } @Override public void onBindViewHolder(T holder, int position) { + Timber.d("ConferenceVideoAdapter::onBindViewHolder(): item ID=%d; pos=%s", holder.getItemId(), position); super.onBindViewHolder(holder, position); if (mScreenWidth > 0) { float divWidth = ConferenceVideoModule.getInstance().getWidthDiv(position); @@ -125,7 +180,9 @@ public class ConferenceVideoAdapter> exten } else { layoutParams.width = itemWidth - ConferenceVideoModule.getInstance().getWidthDividerLength(position); } + holder.itemView.setBottom(itemHeight - 32); holder.itemView.setLayoutParams(layoutParams); + holder.itemView.requestLayout(); } } 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 index 0bb05f5ebb9ec4b48851527c46f7ceee86d63b64..6f9d022f1da138fa1860271b10f9ef86ab4664ac 100644 --- 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 @@ -56,6 +56,7 @@ public class ConferenceVideoItemVh extends BaseVH { } @Override public void bind(ConferenceListItem item, int position) { + Timber.d("ConferenceVideoItemVh::bind(): item participant ID=%s, position=%d", item.participantId, position); mPosition = position; if (mUnbinder != null) { //mUnbinder.unbind(); @@ -98,25 +99,30 @@ public class ConferenceVideoItemVh extends BaseVH { onViewAttachedToWindow(); } } + Timber.d("ConferenceVideoItemVh::setData(): item participant name=%s; ID=%s", item.name, item.participantId); } public void onViewAttachedToWindow() { initVideoCallRenderer(); setVideoRendererForTrack(); enableVideoForTrack(true); + Timber.d("ConferenceVideoItemVh::onViewAttachedToWindow(): item participant name=%s; ID=%s", mItem.name, mItem.participantId); } public void onViewDetachedFromWindow() { enableVideoForTrack(false); //removeVideoRendererForTrack(); + Timber.d("ConferenceVideoItemVh::onViewDetachedFromWindow(): item participant name=%s; ID=%s", mItem.name, mItem.participantId); } public void onViewRecycled() { enableVideoForTrack(false); + Timber.d("ConferenceVideoItemVh::onViewRecycled(): item participant name=%s; ID=%s", mItem.name, mItem.participantId); } @Synchronized public void releaseVideoCallRrenderers() { + Timber.d("ConferenceVideoItemVh::releaseVideoCallRrenderers(): item participant name=%s; ID=%s", mItem.name, mItem.participantId); removeVideoRendererForTrack(); if (!mIsRenderersInitialized) return; if (mVideoFeed != null) { @@ -128,6 +134,7 @@ public class ConferenceVideoItemVh extends BaseVH { ///////////////////////////////////////////////////////////////////////////////////// // Internal Helpers private void setupInConference(ConferenceListItem item) { + Timber.d("ConferenceVideoItemVh::setupInConference(): item participant name=%s; ID=%s", mItem.name, mItem.participantId); initVideoCallRenderer(); if (item.participantId == null || !item.isActive) { // in Conference - but not joined yet @@ -187,6 +194,7 @@ public class ConferenceVideoItemVh extends BaseVH { } private void drawIsFeedVideoIfNeeded(boolean visible) { + Timber.d("ConferenceVideoItemVh::drawIsFeedVideoIfNeeded(): item participant ID=%s; visible=%s", mItem.participantId, visible? "true":"false"); if (visible) { mVideoFeed.setVisibility(View.VISIBLE); } else { @@ -195,9 +203,10 @@ public class ConferenceVideoItemVh extends BaseVH { refreshFeedVideo(); } - private void refreshFeedVideo() { + public void refreshFeedVideo() { + Timber.d("ConferenceVideoItemVh::refreshFeedVideo(): item participant name=%s; ID=%s", mItem.name, mItem.participantId); if (mVideoFeed != null) { - mVideoFeed.invalidate(); + mVideoFeed.postInvalidate(); mVideoFeed.refreshDrawableState(); } } @@ -213,10 +222,13 @@ public class ConferenceVideoItemVh extends BaseVH { @Synchronized private void initVideoCallRenderer() { + Timber.d("ConferenceVideoItemVh::initVideoCallRenderer(): item participant ID=%s; mIsRenderersInitialized=%s", mItem.participantId, mIsRenderersInitialized? "true":"false"); if (mIsRenderersInitialized) return; + Timber.d("ConferenceVideoItemVh::initVideoCallRenderer():2"); try { mVideoFeed.init(createRootEglBase().getEglBaseContext(), null); mVideoFeed.setScalingType(RendererCommon.ScalingType.SCALE_ASPECT_FIT); + mVideoFeed.setWillNotDraw(false); mVideoFeed.setEnableHardwareScaler(true); if (mItem != null && mItem.isMe) { mVideoFeed.setMirror(true); @@ -229,30 +241,40 @@ public class ConferenceVideoItemVh extends BaseVH { @Synchronized private void setVideoRendererForTrack() { + Timber.d("setVideoRendererForTrack::setVideoRendererForTrack(): item participant name=%s; ID=%s", mItem.name, mItem.participantId); if (!mIsRenderersInitialized) return; if (mItem == null) return; if (mItem.isTrackActive == 1) return; String trackId = ConferenceVideoModule.getInstance().getTrackIdByParticipantId(mItem.participantId); + Timber.d("setVideoRendererForTrack::setVideoRendererForTrack(): item participant ID=%s; trackId=%s", mItem.participantId, trackId); if (StringUtils.isNotEmpty(trackId) || mItem.isMe) { mItem.isTrackActive = 1; + Timber.d("setVideoRendererForTrack::setVideoRendererForTrack(): item participant ID=%s; isMe=%s", mItem.participantId, mItem.isMe? "true":"false"); ConferenceVideoModule.getInstance().setVideoRendererForTrack(mVideoFeed, trackId, mItem.isMe); + refreshFeedVideo(); } } @Synchronized private void removeVideoRendererForTrack() { + Timber.d("setVideoRendererForTrack::removeVideoRendererForTrack(): item participant name=%s; ID=%s", mItem.name, mItem.participantId); if (!mIsRenderersInitialized) return; if (mItem == null) return; if (mItem.isTrackActive == 0) return; String trackId = ConferenceVideoModule.getInstance().getTrackIdByParticipantId(mItem.participantId); + Timber.d("setVideoRendererForTrack::removeVideoRendererForTrack(): item participant ID=%s; trackId=%s", mItem.participantId, trackId); if (StringUtils.isNotEmpty(trackId) || mItem.isMe) { + Timber.d("setVideoRendererForTrack::removeVideoRendererForTrack(): item participant ID=%s; isMe=%s", mItem.participantId, mItem.isMe? "true":"false"); ConferenceVideoModule.getInstance().setVideoRendererForTrack(null, trackId, mItem.isMe); mItem.isTrackActive = 0; + refreshFeedVideo(); } } @Synchronized private void enableVideoForTrack(boolean enable) { + Timber.d("setVideoRendererForTrack::enableVideoForTrack(): item participant name=%s; ID=%s", mItem.name, mItem.participantId); + Timber.d("ConferenceVideoItemVh::enableVideoForTrack(): item participant ID=%s; enable=%s", mItem.participantId, enable? "true":"false"); if (!mIsRenderersInitialized) return; if (mItem == null) return; if (StringUtils.isNotEmpty(mItem.participantId)) { 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 fe33b3d069c80db66bad8104fff0acc37d53fa23..c88465c9a120bcaae4c80c4f4a0922340006509f 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 @@ -44,6 +44,14 @@ public abstract class BaseAdapter> extends RecyclerVie } } + public int getItemsPageCount() { + return getItemCount(); + } + + public int getMaxItemsPerPage() { + return 1; + } + public List getItems() { return mList; } 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 index eaad10fb5cf52701faa81bdb171b3b389f0a756d..af28f6a56fc9f9f077a8fd803c0d2e2d2ccf51d0 100644 --- 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 @@ -4,6 +4,7 @@ import android.os.Bundle; import android.os.Handler; import android.support.annotation.NonNull; import android.support.annotation.Nullable; +import android.support.v4.content.ContextCompat; import android.support.v7.widget.DefaultItemAnimator; import android.support.v7.widget.DividerItemDecoration; import android.support.v7.widget.GridLayoutManager; @@ -30,6 +31,7 @@ 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.ui.views.CirclePagerIndicatorDecoration; import com.nynja.mobile.communicator.utils.ActionSheetDialog; import com.nynja.mobile.communicator.utils.DialogFactory; import com.nynja.mobile.communicator.utils.StringUtils; @@ -38,6 +40,7 @@ import org.webrtc.ContextUtils; import org.webrtc.EglBase; import java.util.ArrayList; +import java.util.List; import butterknife.BindView; import timber.log.Timber; @@ -166,23 +169,17 @@ public class ConferenceVideoFragment extends BaseFragment implements ConferenceV 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 CirclePagerIndicatorDecoration(12, 24, 64, + ContextCompat.getColor(getActivity(), R.color.icon_spreadsheet_files), + ContextCompat.getColor(getActivity(), R.color.active_call_bg))); mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.HORIZONTAL)); mRecyclerView.addItemDecoration(new DividerItemDecoration(getActivity(), DividerItemDecoration.VERTICAL)); + SnapHelper snapHelper = new LinearSnapHelper(); + snapHelper.attachToRecyclerView(mRecyclerView); // mRecyclerView.addOnChildAttachStateChangeListener(new RecyclerView.OnChildAttachStateChangeListener() { // @Override // public void onChildViewAttachedToWindow(View view) { @@ -259,10 +256,10 @@ public class ConferenceVideoFragment extends BaseFragment implements ConferenceV } @Override - public void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId) { + public void onRemoteVideoTrackRemoved(ActiveConferenceCall activeConferenceCall, String trackId, String participantId) { if (getActivity() == null || !isAdded()) return; getActivity().runOnUiThread(() -> { - removeRemoteVideoRenderer(activeConferenceCall, trackId); + removeRemoteVideoRenderer(activeConferenceCall, trackId, participantId); setConferenceState(activeConferenceCall); }); } @@ -280,7 +277,7 @@ public class ConferenceVideoFragment extends BaseFragment implements ConferenceV public void onLocalVideoCapturerStopped(ActiveConferenceCall activeConferenceCall) { if (getActivity() == null || !isAdded()) return; mHandler.post(() -> { - removeLocalVideoRenderer(activeConferenceCall); + removeLocalVideoRenderer(activeConferenceCall, false); setConferenceState(activeConferenceCall); }); } @@ -300,7 +297,6 @@ public class ConferenceVideoFragment extends BaseFragment implements ConferenceV @Override public void onSpeakerStateChanged(boolean isSpeakerOn) { if (getActivity() == null || !isAdded()) return; -// getActivity().runOnUiThread(() -> updateSpeakerState(isSpeakerOn)); } @Override @@ -347,6 +343,7 @@ public class ConferenceVideoFragment extends BaseFragment implements ConferenceV } public void setActiveState(boolean active) { + Timber.d("ConferenceVideoFragment::setActiveState(): active=%s", active? "true" : "false"); ActiveConferenceCall activeConferenceCall = mConferencePresenter.getActiveConference(); isActive = active; if (activeConferenceCall == null) return; @@ -365,10 +362,9 @@ public class ConferenceVideoFragment extends BaseFragment implements ConferenceV if (activeConferenceCall.isVideoEnabled) { if (activeConferenceCall.mData.isOwnStreamActive) { mConferencePresenter.pauseCallVideoCapturer(); - removeLocalVideoRenderer(mConferencePresenter.getActiveConference()); + removeLocalVideoRenderer(mConferencePresenter.getActiveConference(), true); } removeRemoteVideoRenderers(mConferencePresenter.getActiveConference()); - setConferenceState(activeConferenceCall); } } } @@ -493,7 +489,9 @@ public class ConferenceVideoFragment extends BaseFragment implements ConferenceV } private void loadConferenceMembers(ArrayList members) { + Timber.d("ConferenceVideoFragment::loadConferenceMembers()"); mConfParticipantsAdapter.setItems(members); + mConfParticipantsAdapter.notifyDataSetChanged(); } private void setRemoteVideoRenderer(ActiveConferenceCall activeConferenceCall) { @@ -503,10 +501,11 @@ public class ConferenceVideoFragment extends BaseFragment implements ConferenceV if (!activeConferenceCall.mConference.isConference()) return; if (!activeConferenceCall.isVideoEnabled) return; + Timber.d("ConferenceVideoFragment::setRemoteVideoRenderer()"); mConferencePresenter.loadConferenceParticipants(activeConferenceCall); } - private void removeRemoteVideoRenderer(ActiveConferenceCall activeConferenceCall, String trackId) { + private void removeRemoteVideoRenderer(ActiveConferenceCall activeConferenceCall, String trackId, String participantId) { if (activeConferenceCall == null) return; if (activeConferenceCall.mConference == null) return; if (!activeConferenceCall.mConference.isConference()) return; @@ -514,20 +513,23 @@ public class ConferenceVideoFragment extends BaseFragment implements ConferenceV if (!activeConferenceCall.isVideoEnabled) return; if (StringUtils.isNotEmpty(trackId)) { - ConferenceVideoModule.getInstance().setVideoRendererForTrack(null, trackId, false); + Timber.d("ConferenceVideoFragment::removeRemoteVideoRenderer()"); + if (StringUtils.isNotEmpty(participantId)) { + mConfParticipantsAdapter.releaseVideoCallRrenderer(participantId, false, true); + } } - mConfParticipantsAdapter.notifyDataSetChanged(); } private void removeRemoteVideoRenderers(ActiveConferenceCall activeConferenceCall) { if (activeConferenceCall == null) return; - ArrayList members = mConferencePresenter.getConferenceVideoMembers(activeConferenceCall); - for (ConferenceListItem item : members) { + Timber.d("ConferenceVideoFragment::removeRemoteVideoRenderers()"); + List list = mConfParticipantsAdapter.getItems(); + Timber.d("ConferenceVideoFragment::removeRemoteVideoRenderers(): list size=%d", list.size()); + for (ConferenceListItem item : list) { //members) { String trackId = ConferenceVideoModule.getInstance().getTrackIdByParticipantId(item.participantId); - if (StringUtils.isNotEmpty(trackId) || item.isMe) { - ConferenceVideoModule.getInstance().setVideoRendererForTrack(null, trackId, item.isMe); - } + Timber.d("ConferenceVideoFragment::removeRemoteVideoRenderers(): participantID=%s", item.participantId); + mConfParticipantsAdapter.releaseVideoCallRrenderer(item.participantId, true, true); } } @@ -535,17 +537,25 @@ public class ConferenceVideoFragment extends BaseFragment implements ConferenceV if (activeConferenceCall == null) return; if (activeConferenceCall.mConference == null) return; if (!activeConferenceCall.isVideoEnabled) return; + Timber.d("ConferenceVideoFragment::setLocalVideoRenderer()"); mConferencePresenter.loadConferenceParticipants(activeConferenceCall); } - private void removeLocalVideoRenderer(ActiveConferenceCall activeConferenceCall) { + private void removeLocalVideoRenderer(ActiveConferenceCall activeConferenceCall, boolean pause) { if (activeConferenceCall == null) return; if (activeConferenceCall.mConference == null) return; if (!activeConferenceCall.mConference.isRunning()) return; if (!activeConferenceCall.isVideoEnabled) return; - - mConferencePresenter.loadConferenceParticipants(activeConferenceCall); + Timber.d("ConferenceVideoFragment::removeLocalVideoRenderer()"); + List list = mConfParticipantsAdapter.getItems(); + for (ConferenceListItem item : list) { + if (item.participantId.contentEquals(activeConferenceCall.mConference.participantId()) && + item.isMe) { + Timber.d("ConferenceVideoFragment::removeLocalVideoRenderer(): participantID=%s", item.participantId); + mConfParticipantsAdapter.releaseVideoCallRrenderer(item.participantId, pause,true); + } + } } private void setConferenceState(ActiveConferenceCall activeConferenceCall) { 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 index 898677c168a01a441789bfca58e738252a37ee38..0caaba0297ce22891a20170efef15edab6f445fc 100644 --- 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 @@ -2,14 +2,19 @@ package com.nynja.mobile.communicator.ui.views; import android.content.res.Resources; import android.graphics.Canvas; +import android.graphics.Color; import android.graphics.Paint; import android.graphics.Rect; +import android.graphics.drawable.Drawable; 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 com.nynja.mobile.communicator.R; +import com.nynja.mobile.communicator.ui.base.BaseAdapter; + import org.jetbrains.annotations.NotNull; /** @@ -48,29 +53,35 @@ public class CirclePagerIndicatorDecoration extends RecyclerView.ItemDecoration public void onDrawOver(@NotNull Canvas c, @NotNull RecyclerView parent, @NotNull RecyclerView.State state) { super.onDrawOver(c, parent, state); - final RecyclerView.Adapter adapter = parent.getAdapter(); + final BaseAdapter adapter = (BaseAdapter) parent.getAdapter(); if (adapter == null) { return; } - int itemCount = adapter.getItemCount(); + int pageCount = adapter.getItemsPageCount(); + if (pageCount <= 1) { + return; + } + + int maxItemsPerPage = adapter.getMaxItemsPerPage(); // center horizontally, calculate width and subtract half from center - float totalLength = this.radius * 2 * itemCount; - float paddingBetweenItems = Math.max(0, itemCount - 1) * indicatorItemPadding; + float totalLength = this.radius * 2 * pageCount; + float paddingBetweenItems = Math.max(0, pageCount - 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); + drawInactiveDots(c, indicatorStartX, indicatorPosY, pageCount); - final int activePosition; + int activePosition; if (parent.getLayoutManager() instanceof GridLayoutManager) { - activePosition = ((GridLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition(); + activePosition = ((GridLayoutManager) parent.getLayoutManager()).findFirstCompletelyVisibleItemPosition(); +// activePosition = ((GridLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition(); } else if (parent.getLayoutManager() instanceof LinearLayoutManager) { activePosition = ((LinearLayoutManager) parent.getLayoutManager()).findFirstVisibleItemPosition(); } else { @@ -87,6 +98,7 @@ public class CirclePagerIndicatorDecoration extends RecyclerView.ItemDecoration if (activeChild == null) { return; } + activePosition = activePosition/maxItemsPerPage; drawActiveDot(c, indicatorStartX, indicatorPosY, activePosition); } @@ -107,7 +119,7 @@ public class CirclePagerIndicatorDecoration extends RecyclerView.ItemDecoration // 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); + c.drawCircle(highlightStart, indicatorPosY, radius+2, activePaint); } @Override diff --git a/app/src/main/res/layout/li_conference_video.xml b/app/src/main/res/layout/li_conference_video.xml index a93662320390f3a0f2d675d308857b2ff58ed469..e3efcbbe913caafcc3016c04c11d7300a01e2fbf 100644 --- a/app/src/main/res/layout/li_conference_video.xml +++ b/app/src/main/res/layout/li_conference_video.xml @@ -24,22 +24,12 @@ android:layout_gravity="center" android:padding="2dp"> - - - - - +