diff --git a/app/build.gradle b/app/build.gradle index dec91bee4620de54a442bb525ab06f99951ade1a..5feab5355ba72629335e61ef3eee29e492e529af 100644 --- a/app/build.gradle +++ b/app/build.gradle @@ -679,8 +679,8 @@ dependencies { implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk7:$kotlin_version" // Conference, Calls mobile SDK - implementation 'com.nynja.sdk:NynjaSdk:1.21.5@aar' - //implementation(name: 'NynjaSdk-1.21.5', ext: 'aar') + implementation 'com.nynja.sdk:NynjaSdk:1.22.2@aar' + //implementation(name: 'NynjaSdk-1.21.5.1', 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/models/nynjamodels/ContactModel.java b/app/src/main/java/com/nynja/mobile/communicator/data/models/nynjamodels/ContactModel.java index 8be1a62beea27dbdaff3928f120689f180976458..8dd2aee4c246a69588410f132fc4d43d3e096014 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/data/models/nynjamodels/ContactModel.java +++ b/app/src/main/java/com/nynja/mobile/communicator/data/models/nynjamodels/ContactModel.java @@ -243,6 +243,12 @@ public class ContactModel extends BaseNynjaModel implements PhoneId, Conferencea return name.trim(); } + public String getFullNameOrUsername() { + String fullName = getFullName(); + return StringUtils.isNotEmpty(fullName) ? fullName : nick; + } + + public String getUsernameOrFullName() { return StringUtils.isNotEmpty(nick) ? nick : getFullName(); } 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 cf1044ccef8dc33743aa5c15334d1e6950a4d736..9e3b3ee8cc29ace888d557c56e347aee221c2e81 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 @@ -51,6 +51,8 @@ public abstract class ConferenceSDKPresenter extends Bas @Override public void onStateChangedForCall(NYNCall iConference) {} + @Override public void onAcceptedElsewhereConference(String conferenceId) {} + @Override public void onCreateVideoCallReady(ActiveConferenceCall activeConferenceCall) {} @Override public void onScreenShareState(boolean active) {} @@ -100,4 +102,7 @@ public abstract class ConferenceSDKPresenter extends Bas @Override public void showPurchaseDialog(boolean isModerator, final String message) {} @Override public void onPurchaseFinished() {} + + @Override public void pickupCallWhileAnotherActiveCall() {} + } diff --git a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/NynjaSDKManager.java b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/NynjaSDKManager.java index a4db15d1067681697cc1dc2a49a0348aa3564896..7678e9d28a59869596713c3ee4f49121a2c1763f 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/data/sdk/NynjaSDKManager.java +++ b/app/src/main/java/com/nynja/mobile/communicator/data/sdk/NynjaSDKManager.java @@ -66,7 +66,7 @@ public class NynjaSDKManager { } else { communicator = NynjaCommunicator.newInstance(); } - communicator.setDeviceId(Utils.getAndroidId(mContext)); + communicator.setDeviceId(Utils.getDeviceId(mContext));//)getAndroidId(mContext)); mAccountSDKModule = new AccountSDKModule(mContext, communicator); mConferenceSDKModule = new ConferenceSDKModule(context, communicator, NynjaApp.getComponent().dbHelper(), mPreferenceHelper.getSettingNotifications(), new StateDevice(false), 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 b6bbde5325b77787f8013b45603db66497af91da..4b7d023f763963f5a478d341899f5f74647189d4 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 @@ -36,6 +36,8 @@ public interface ConferenceSDKListener { void onStateChangedForCall(NYNCall iConference); + void onAcceptedElsewhereConference(String conferenceId); + void onCreateVideoCallReady(ActiveConferenceCall activeConferenceCall); void onScreenShareState(boolean active); @@ -85,4 +87,6 @@ public interface ConferenceSDKListener { void showPurchaseDialog(boolean isModerator, final String message); void onPurchaseFinished(); + + void pickupCallWhileAnotherActiveCall(); } 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 791a9ce7c18f10fc6e92ed5b23934c69f3b5eb41..b94423c42d116a357b52e8f32a035dd31118a953 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 @@ -26,7 +26,6 @@ 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.conference.ActiveCallBase; -import com.nynja.mobile.communicator.data.conference.ConferenceVideoModule; import com.nynja.mobile.communicator.data.db.DbHelper; import com.nynja.mobile.communicator.data.models.SettingNotifications; import com.nynja.mobile.communicator.data.models.StateDevice; @@ -62,6 +61,7 @@ import com.nynja.sdk.NYNCallParticipant; import com.nynja.sdk.NYNCallParticipantArray; import com.nynja.sdk.NYNCallRoomType; import com.nynja.sdk.NYNCallState; +import com.nynja.sdk.NYNCallsArray; import com.nynja.sdk.NYNConfMemberOption; import com.nynja.sdk.NYNError; import com.nynja.sdk.NynjaCallHistoryManager; @@ -77,7 +77,6 @@ import java.util.Map; import java.util.Timer; import java.util.TimerTask; -import kotlin.jvm.Synchronized; import timber.log.Timber; import static com.nynja.sdk.NYNCallEndReason.NYNCallEndReasonFailed; @@ -142,7 +141,8 @@ public class ConferenceSDKModule extends BaseSDKModule { private boolean isSpeakerPhoneForced = false; private HashMap mConferenceRoomLimits = new HashMap<>(); private HashMap mConferenceWaitingRoomLNames = new HashMap<>(); - private HashMap mMyAcceptedElsewhereCalls = new HashMap<>(); + //private HashMap mMyAcceptedElsewhereCalls = new HashMap<>(); + private HashMap mMyAcceptedElsewhereCalls = new HashMap<>(); private int mLastPhoneCallState = TelephonyManager.CALL_STATE_IDLE; @@ -159,6 +159,7 @@ public class ConferenceSDKModule extends BaseSDKModule { mStates = States.Connecting; } mActiveRosterId = conference.id; + getMobileSDKCommunicator().setDeviceId(getDeviceId()); getMobileSDKCommunicator().login(conference.login, conference.password); } @@ -525,6 +526,110 @@ public class ConferenceSDKModule extends BaseSDKModule { } } + public boolean hasCallForPickup() { + if (mMyAcceptedElsewhereCalls.size() == 0) { + return false; + } + return true; + } + + public synchronized String callPickupText() { + String text = ""; + synchronized (mMyAcceptedElsewhereCalls) { + for (String callId : mMyAcceptedElsewhereCalls.keySet()) { +// NYNCall call = mCallManager.getCallById(callId); + NYNCall call = mMyAcceptedElsewhereCalls.get(callId); + if (call != null) { + if (StringUtils.isNotEmpty(text)) { + text += ", "; + } + if (!call.isConference()) { + text += (call.getExternalInfo()); + } + } + } + } + return text; + } + + public synchronized HashMap pickupCallsInfo() { + HashMap pickupeCalls = new HashMap<>(); + synchronized (mMyAcceptedElsewhereCalls) { + for (String callId : mMyAcceptedElsewhereCalls.keySet()) { + NYNCall call = mCallManager.getCallById(callId); + if (call != null) { + pickupeCalls.put(callId, call.getExternalInfo()); + } + } + } + return pickupeCalls; + } + + public synchronized HashMap pickupCalls() { + return mMyAcceptedElsewhereCalls; + } + + public synchronized boolean callPickup(String callId) { + if (callId == null) { + Timber.w("callPickup(): try to pickup with callId=null!!!"); + return false; + } + if (hasCreatedActiveCall()) { + if (mActiveConference.mConference != null && + callId.contentEquals(mActiveConference.mConference.callId())) { + Timber.w("callPickup(): try to pickup the same already pickingUP call - ID=%s",callId); + return false; + } + if (mActiveConference.mConference != null) { + Timber.w("callPickup(): while has another active call!! callID=%s", + mActiveConference.mConference.callId()); + mHandler.postDelayed(() -> { + synchronized (mConferenceSDKListener) { + for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { + conferenceSDKListener.pickupCallWhileAnotherActiveCall(); + } + } + }, Consts.DELAY_50); + return false; + } + } + NYNCall call = mCallManager.pickupCall(Utils.generateUUID(), callId); + if (call != null) { + mConferenceDetails = new ConferenceDetails(getDeviceId(), callId); + mActiveConference = createActiveConference(call); + if (hasCreatedActiveCall()) { + initConferenceCall(false); + mActiveConference.isCallInProgress = true; + //startConferenceActivity(); + return true; + } + } + return false; + } + + public void loadPickupCalls() { + new Handler(Looper.getMainLooper()).postDelayed(() -> { + if (!isLoggedIn()) return; + NYNCallsArray pickups = mCallManager.callsActiveElsewhere(); + if (pickups != null) { + if (pickups.size() == 0) return; + mMyAcceptedElsewhereCalls.clear(); + for (int index = 0; index< pickups.size(); index++) { + NYNCall call = pickups.get(index); + if (call != null && StringUtils.isNotEmpty(call.callId())) { + mMyAcceptedElsewhereCalls.put(call.callId(), call); + } + } + mHandler.postDelayed(() -> { + synchronized (mConferenceSDKListener) { + for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { + conferenceSDKListener.onConferenceStateChanged(mActiveConference); + } + } + }, Consts.DELAY_50); + } + },Consts.DELAY_200); + } public boolean isCameraRunning() { if (!hasCreatedActiveCall()) return false; @@ -1058,7 +1163,11 @@ public class ConferenceSDKModule extends BaseSDKModule { if (mActiveConference.mConference == null) return; Timber.d("Request conference member with \'ConferenceId\'=\'" + conferenceId + "\' " + (result ? "succeed" : "failed!!!")); - mActiveConference.mData.mParticipantArray = mActiveConference.mConference.getParticipants(); + synchronized (mActiveConference.mData) { + mActiveConference.mData.mParticipantArray = mActiveConference.mConference.getParticipants(); + Timber.d("Request conference member with members size=\'" + + mActiveConference.mData.mParticipantArray.size()); + } // update chatRoomId if (mActiveConference.mData.mParticipantArray != null && @@ -1160,9 +1269,15 @@ public class ConferenceSDKModule extends BaseSDKModule { NYNCallState state = call.callState(); boolean isActive = !NYNCallState.NYNCallStateClosed.equals(state); if (StringUtils.isNotEmpty(roomId)) { - //if (!mMyAcceptedElsewhereCalls.containsKey(roomId) && isActive) { - // mMyAcceptedElsewhereCalls.put(roomId, callId); - //} else + if (NYNCallState.NYNCallStateConnected.equals(state) && + !mMyAcceptedElsewhereCalls.containsKey(callId)) { + if (!((hasCreatedActiveCall() && mActiveConference.mConference != null + && mActiveConference.mConference.callId().contentEquals(callId)) || + call.isStartedOnThisDevice())) { + //mMyAcceptedElsewhereCalls.put(callId, roomId); + mMyAcceptedElsewhereCalls.put(callId, mCallManager.getCallById(callId)); + } + } if (!forceFireEvent) { if ((hasCreatedActiveCall() && mActiveConference.mConference != null && mActiveConference.mConference.callId().contentEquals(callId))) { @@ -1182,14 +1297,15 @@ public class ConferenceSDKModule extends BaseSDKModule { private void onCallEndedHandle(NYNCall call, boolean forceFireEvent) { if (call != null) { + String callId = call.callId(); String roomId = (call.isConference() ? call.getChatRoomId() : (call.isOutgoing() ? call.callee() : call.caller())); - //if (mMyAcceptedElsewhereCalls.containsKey(roomId)) { - // mMyAcceptedElsewhereCalls.remove(roomId); - //} else + if (mMyAcceptedElsewhereCalls.containsKey(callId)) { + mMyAcceptedElsewhereCalls.remove(callId); + } if (!forceFireEvent) { if (!(hasCreatedActiveCall() && mActiveConference.mConference != null - && mActiveConference.mConference.callId().contentEquals(call.callId()))) { + && mActiveConference.mConference.callId().contentEquals(callId))) { return; } } @@ -1396,6 +1512,7 @@ public class ConferenceSDKModule extends BaseSDKModule { @Override public void receivedRingingCall(NYNCall call) { + Timber.d("receivedRingingCall(): call with \'CallId\'=\'%s", (call !=null ? call.callId(): "null")); onIncomingConference(call); } @@ -1406,7 +1523,8 @@ public class ConferenceSDKModule extends BaseSDKModule { @Override public void receivedAcceptedElsewhere(String conferenceId) { - onAcceptedElsewhereConferenceRinging(conferenceId); + Timber.d("receivedAcceptedElsewhere(): call with \'CallId\'=\'%s", conferenceId); + onAcceptedElsewhereConference(conferenceId); } @Override @@ -1436,6 +1554,7 @@ public class ConferenceSDKModule extends BaseSDKModule { @Override public void conferenceIdByLinkIdFinished(NYNError err, String requestId, String confId) { + Timber.d("conferenceIdByLinkIdFinished(): call with \'CallId\'=\'%s", confId); if (onJoinConferenceByLinkIdFailed(confId, err)) { return; } @@ -1444,17 +1563,20 @@ public class ConferenceSDKModule extends BaseSDKModule { @Override public void joinConferenceByLinkIdFinished(NYNError err, String requestId, String confId) { + Timber.d("joinConferenceByLinkIdFinished(): call with \'CallId\'=\'%s", confId); onJoinConferenceByLinkIdFinished(err, confId); } @Override public void receivedAcceptedCall(NYNCall call) { + Timber.d("receivedAcceptedCall(): call with \'CallId\'=\'%s", (call !=null ? call.callId(): "null")); onAcceptedCall(call); } @Override public void receivedCompletedCall(NYNCall call) { - String callId = call.callId(); + String callId = (call != null ? call.callId(): null); + Timber.d("receivedCompletedCall(): call with \'CallId\'=\'%s", callId); onCallEndedHandle(call, true); if (call != null && callId != null && hasCreatedActiveCall() && mActiveConference.mConference != null @@ -1506,6 +1628,11 @@ public class ConferenceSDKModule extends BaseSDKModule { onEscalateConferenceFinished(err, replaceRef, roomId, conferenceId); } + @Override + public void pickupCallFinished(String requestId, String callId, boolean success) { + onPickupCallFinished(requestId, callId, success); + } + @Override public void signalCallCompleted(NYNCall call) { onSignalCallCompleted(call); @@ -1516,6 +1643,12 @@ public class ConferenceSDKModule extends BaseSDKModule { onCallTimedOut(call); } + @Override + public void elsewhereCallFinished(NYNCall call) { + if (call == null) return; + onElsewhereCallFinished(call.callId()); + } + }; mCallManager.setListener(mCallManagerListener); } @@ -2325,7 +2458,7 @@ public class ConferenceSDKModule extends BaseSDKModule { public boolean hasRunningCallWithRoom(String roomId) { return (mCallManager.hasRunningCallWithRoom(roomId) || - (//mMyAcceptedElsewhereCalls.containsKey(roomId) && + (//mMyAcceptedElsewhereCalls.containsValue(roomId) && mCallManager.hasRunningCallWithContact(roomId))); } @@ -2540,7 +2673,7 @@ public class ConferenceSDKModule extends BaseSDKModule { boolean isVideoEnabled = iConference.recvVideo(); String name = iConference.getSubject(); if (!iConference.isConference()) { - personId = iConference.caller(); + personId = (iConference.isOutgoing()) ? iConference.callee() : iConference.caller(); callType = ActiveCallBase.CallType.P2PCall; name = ""; // iConference.getExternalInfo(); } else { @@ -2821,7 +2954,9 @@ public class ConferenceSDKModule extends BaseSDKModule { } synchronized (mConferenceSDKListener) { for (ConferenceSDKListener sdkListener : mConferenceSDKListener) { - sdkListener.timeAway(isVideoConference, time); + sdkListener.timeAway((isVideoConference && + (mActiveConference.mData.hasRemoteVideoTrack || mActiveConference.mData.isOwnStreamActive)), + time); if (message != null && !hasPurchasedProducts[0]) { sdkListener.showPurchaseDialog(isModerator, message); } @@ -2954,10 +3089,13 @@ public class ConferenceSDKModule extends BaseSDKModule { createIncomingConference(iConference, true); } + private boolean isP2P(NYNCall call) { + return (call != null && !call.isConference()); + } + public synchronized boolean isP2P() { return (mActiveConference != null - && mActiveConference.mConference != null - && !mActiveConference.mConference.isConference() + && isP2P(mActiveConference.mConference) ); } @@ -3030,22 +3168,72 @@ public class ConferenceSDKModule extends BaseSDKModule { } } - private void onAcceptedElsewhereConferenceRinging(String conferenceId) { - if (!hasCreatedActiveCall()) return; - if (mActiveConference.mConference == null) return; + private void onAcceptedElsewhereConference(String conferenceId) { + if (StringUtils.isNotEmpty(conferenceId)) { + NYNCall call = mCallManager.getCallById(conferenceId); + if (call != null) { + if (isP2P(call)) { + //String roomId = (call.isConference() ? call.getChatRoomId() : + // (call.isOutgoing() ? call.callee() : call.caller())); + //mMyAcceptedElsewhereCalls.put(conferenceId, roomId); + mMyAcceptedElsewhereCalls.put(conferenceId, call); + for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { + conferenceSDKListener.onAcceptedElsewhereConference(conferenceId); + } + } + } + } - if (mActiveConference.mConference.callId().contentEquals(conferenceId)) { + if (mActiveConference != null && + mActiveConference.mConference != null && + mActiveConference.mConference.callId().contentEquals(conferenceId)) { mActiveConference.isRinging = false; if (mActiveConference.mConference.isOutgoing() && mActiveConference.isOutgoingCall) { // Nothing to do when is my outgoing call - may need check when allow multiple login } else { - //mMyAcceptedElsewhereCalls.put(mActiveConference.mEndPointId, conferenceId); + //if (isP2P() && !mActiveConference.isCallRinging()) return; onConferenceEnded(conferenceId); } } } + private synchronized void removeCallFromElsewhereCallsList(String callId, boolean fireEvent) { + if (mMyAcceptedElsewhereCalls.containsKey(callId)) { + //String roomId = (call.isConference() ? call.getChatRoomId() : + // (call.isOutgoing() ? call.callee() : call.caller())); + //mMyAcceptedElsewhereCalls.put(conferenceId, roomId); + mMyAcceptedElsewhereCalls.remove(callId); + if (!fireEvent) return; + for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { + conferenceSDKListener.onAcceptedElsewhereConference(callId); + } + } + } + + private void onPickupCallFinished(String requestId, String callId, boolean success) { + Timber.d("onPickupCallFinished(): callId=%s, result=%s", + callId, success ? "true" : "false"); + if (success) { + removeCallFromElsewhereCallsList(callId, false); + if (mActiveConference != null && + mActiveConference.mConference != null && + mActiveConference.mConference.callId().contentEquals(callId)) { + initConferenceCall(true); + mActiveConference.isCallInProgress = true; + mActiveConference.mConference.hangup(); + } + } else { + onConferenceFailedInternal("Pickup failed.", false); + } + } + + private void onElsewhereCallFinished(String callId) { + if (callId == null) return; + Timber.d("onElsewhereCallFinished(): callId=%s", callId); + removeCallFromElsewhereCallsList(callId, true); + } + private synchronized void onStateChangedForCall(NYNCall iConference) { onCallStateHandle(iConference, false); for (ConferenceSDKListener conferenceSDKListener : mConferenceSDKListener) { @@ -3070,13 +3258,18 @@ public class ConferenceSDKModule extends BaseSDKModule { private synchronized void onConferenceConnected(NYNCall iConference) { if (iConference != null && hasCreatedActiveCall() - && ((!mActiveConference.isOutgoingCall + //P2P - pickup + && ((isP2P() && !mActiveConference.isOutgoingCall) + || + // Conference - participant rejoin + ((!mActiveConference.isOutgoingCall && mActiveConference.isCallInProgress() && !mActiveConference.mConference.isModerator() - && mActiveConference.mConference.isConference()) || // participant rejoin - (mActiveConference.isOutgoingCall || // is outgoing - /* moderator rejoin */ - (mActiveConference.mConference != null && mActiveConference.mConference.isModerator())))) { + && mActiveConference.mConference.isConference()) || + // is outgoing + (mActiveConference.isOutgoingCall || + // moderator rejoin + (mActiveConference.mConference != null && mActiveConference.mConference.isModerator()))))) { final boolean videoEnabled = (iConference != null && (iConference.recvVideo() || iConference.sendVideo())); final String callId = iConference.callId(); 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 fd01699566b77cf3c4cd164b78315bbf9406a1a3..ed613959fb5287cef1f2486aaab5433c2d93e74a 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 @@ -31,9 +31,13 @@ import com.nynja.mobile.communicator.utils.navigation.NynjaRouter; import com.nynja.mobile.communicator.utils.navigation.Screen; import com.nynja.mobile.communicator.utils.navigation.navigators.HomeNavigator; import com.nynja.mobile.communicator.utils.navigation.navigators.NynjaNavigator; +import com.nynja.sdk.NYNCall; import org.jetbrains.annotations.NotNull; +import java.util.ArrayList; +import java.util.HashMap; + import io.reactivex.android.schedulers.AndroidSchedulers; import io.reactivex.disposables.CompositeDisposable; import io.reactivex.disposables.Disposable; @@ -266,6 +270,82 @@ public abstract class BasePresenter extends MvpPresenter< mDataManager.getConferenceSDK().isScreenShareAllowed = allowed; } + public boolean hasCallForPickup() { + return mDataManager.getConferenceSDK().hasCallForPickup(); + } + + public String callPickupText() { + if (!hasCallForPickup()) return ""; + return callPickupsText(); + } + + public synchronized String callPickupsText() { + String text = ""; + HashMap myAcceptedElsewhereCalls = pickupCallsInfo(); + synchronized (myAcceptedElsewhereCalls) { + for (String callId : myAcceptedElsewhereCalls.keySet()) { + String name = myAcceptedElsewhereCalls.get(callId); + if (StringUtils.isNotEmpty(name)) { + if (StringUtils.isNotEmpty(text)) { + text += ", "; + } + text += name; + } + } + } + return text; + } + + public synchronized ArrayList callPickupsAvatar() { + ArrayList avatars = new ArrayList<>(); + HashMap myAcceptedElsewhereCalls = pickupCalls(); + synchronized (myAcceptedElsewhereCalls) { + for (String callId : myAcceptedElsewhereCalls.keySet()) { + NYNCall call = myAcceptedElsewhereCalls.get(callId); + if (call != null) { + String contactId = (call.isOutgoing()) ? call.callee() : call.caller(); + ContactModel contact = mDataManager.getContactsByPhoneId(contactId); + if (contact != null) { + avatars.add(contact.avatar); + } else { + avatars.add(null); + } + } + } + } + return avatars; + } + + public synchronized HashMap pickupCallsInfo() { + HashMap pickupeCalls = new HashMap<>(); + HashMap myAcceptedElsewhereCalls = pickupCalls(); + synchronized (myAcceptedElsewhereCalls) { + for (String callId : myAcceptedElsewhereCalls.keySet()) { + NYNCall call = myAcceptedElsewhereCalls.get(callId); + if (call != null) { + if (!call.isConference()) { + if (call.isOutgoing()) { + String contactId = call.callee(); + ContactModel contact = mDataManager.getContactsByPhoneId(contactId); + if (contact != null) { + pickupeCalls.put(callId, contact.getFullNameOrUsername()); + } else { + pickupeCalls.put(callId, contactId); + } + } else { + pickupeCalls.put(callId, call.getExternalInfo()); + } + } + } + } + } + return pickupeCalls; + } + + public synchronized HashMap pickupCalls() { + return mDataManager.getConferenceSDK().pickupCalls(); + } + @Override public void showDefaultError(@NotNull Object data, @StringRes int errorRes) { getViewState().showDefaultServerError(errorRes); } 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 bcba0112862d18b86d8aead0f044f6f8a7b5d4c7..78b044939568d790bff80f4b277c5f7104726863 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 @@ -37,6 +37,7 @@ import com.nynja.mobile.communicator.utils.iap.InAppPurchaseManager import com.nynja.mobile.communicator.utils.navigation.Screen import com.nynja.mobile.communicator.utils.navigation.navigators.HomeNavigator import com.nynja.mobile.communicator.utils.navigation.navigators.NynjaNavigator +import com.nynja.sdk.NYNCall import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.functions.Function import io.reactivex.schedulers.Schedulers @@ -127,6 +128,7 @@ class MainActivityPresenter : ConferenceSDKPresenter() { viewState.onInitWheel(mIsConferenceActive) val activeCall = mDataManager.conferenceSDK.activeConference if (activeCall != null) onConferenceStateChanged(activeCall) + onAcceptedElsewhereConference(""); } fun continueToJojnGroup() { @@ -625,16 +627,20 @@ class MainActivityPresenter : ConferenceSDKPresenter() { override fun activeConference(activeConferenceCall: ActiveConferenceCall) {} override fun timeAway(videoEnabled: Boolean, time: String) { + if (attachedViews.size == 0) return viewState.onConferenceTimeChange(time) } override fun conferenceEnded() { - viewState.onConferenceEnded() + //if (attachedViews.size > 0) { + viewState.onConferenceEnded() + //} invalidateWheelAfterCallStateChaged() clearIncomingCallNotification() } override fun onConferenceRinging() { + if (attachedViews.size == 0) return viewState.onConferenceRinging() } @@ -643,21 +649,40 @@ class MainActivityPresenter : ConferenceSDKPresenter() { } override fun onConferenceJoinFailed(reason: String) { - viewState.onConferenceJoinFailed(reason) + if (attachedViews.size > 0) { + viewState.onConferenceJoinFailed(reason) + } invalidateWheelAfterCallStateChaged() } - override fun onConferenceStateChanged(activeConferenceCall: ActiveConferenceCall) { + override fun onConferenceStateChanged(activeConferenceCall: ActiveConferenceCall?) { + if (attachedViews.size == 0) return viewState.onConferenceStateChanged(activeConferenceCall) } + override fun onStateChangedForCall(iConference: NYNCall?) { + if (attachedViews.size == 0) return + if (iConference != null) { + viewState.onStateChangedForCall(iConference.callId()) + } + } + + override fun onAcceptedElsewhereConference(conferenceId: String) { + if (attachedViews.size == 0) return + if (conferenceId != null) { + viewState.onAcceptedElsewhereConference(conferenceId) + } + } + override fun showPurchaseDialog(isModerator: Boolean, message: String?) { if (attachedViews.size == 0) return viewState.showPurchaseDialog(isModerator, message) } fun openActiveConference() { - viewState.openActiveConference() + if (attachedViews.size > 0) { + viewState.openActiveConference() + } mRouter.backTo(HomeNavigator.CHAT) } @@ -763,6 +788,10 @@ class MainActivityPresenter : ConferenceSDKPresenter() { } + override fun pickupCallWhileAnotherActiveCall() { + //if (attachedViews.size == 0) return + viewState.pickupCallWhileAnotherActiveCall() + } private fun showActiveCallDialog() { viewState.showActiveCallDialog() @@ -896,4 +925,14 @@ class MainActivityPresenter : ConferenceSDKPresenter() { } } } + + fun callPickup(callId: String?) { + if (mDataManager.conferenceSDK.callPickup(callId)) { + mRouter.navigateTo(NynjaNavigator.ACTIVE_CALL) + } + } + + fun loadPickupCalls() { + mDataManager.conferenceSDK.loadPickupCalls(); + } } 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 4ecb0e41d9a0887eab0044780ce2142157d5960c..604679b0e03685794392496c507039d4fc1e317a 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 @@ -41,6 +41,10 @@ public interface MainActivityView extends JoinGroupView { void onConferenceStateChanged(ActiveConferenceCall activeConferenceCall); + void onStateChangedForCall(String callId); + + void onAcceptedElsewhereConference(String conferenceId); + void openActiveConference(); void onConferenceRinging(); @@ -76,4 +80,6 @@ public interface MainActivityView extends JoinGroupView { void tryStartCall(boolean createNew, boolean isGroup, boolean isVideo, Parcelable prevModel); void showAlertMessage(int res); + + void pickupCallWhileAnotherActiveCall(); } 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 d3ec2a1c170d69e573b0ffc25313171dccef8c2c..48c1361dd09910b67ef64bf0192fd5dcbb40f3d1 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 @@ -6,6 +6,7 @@ import android.app.NotificationManager; import android.content.ComponentName; import android.content.ContentResolver; import android.content.Context; +import android.content.DialogInterface; import android.content.Intent; import android.media.AudioManager; import android.net.Uri; @@ -60,6 +61,7 @@ import com.nynja.mobile.communicator.ui.wheel.entity.actions.WheelMenuKey; import com.nynja.mobile.communicator.ui.wheel.entity.adapters.WheelChatAdapter; import com.nynja.mobile.communicator.ui.wheel.entity.items.BaseWheelItem; import com.nynja.mobile.communicator.ui.wheel.entity.items.WheelChatItem; +import com.nynja.mobile.communicator.utils.ActionSheetDialog; import com.nynja.mobile.communicator.utils.CallUtils; import com.nynja.mobile.communicator.utils.Consts; import com.nynja.mobile.communicator.utils.DefaultWheelClickListener; @@ -71,13 +73,16 @@ import com.nynja.mobile.communicator.utils.Utils; import com.nynja.mobile.communicator.utils.iap.InAppPurchaseManager; import com.nynja.mobile.communicator.utils.navigation.header.NynjaStatusHeaderView; import com.nynja.mobile.communicator.utils.navigation.navigators.HomeNavigator; +import com.nynja.sdk.NYNCall; import com.nynja.sdk.NYNCallState; import org.webrtc.SurfaceViewRenderer; import java.util.ArrayList; import java.util.Arrays; +import java.util.HashMap; import java.util.List; +import java.util.Set; import butterknife.BindView; import butterknife.OnClick; @@ -102,27 +107,24 @@ public class MainActivity extends BaseActivity implements MainActivityView, private static final String OPEN_SCREEN_KEY = "open_screen_key"; private static final String FILE_TYPE = "file"; - @BindView(R.id.nynja_navigation_view) - NynjaNavigationView mNynjaNavigationView; - @BindView(R.id.chat_active_video) - SurfaceViewRenderer video; - @BindView(R.id.chat_audio_layout) - View chatAudioLayout; - @BindView(R.id.chat_audio_name) - TextView chatAudioName; - @BindView(R.id.chat_audio_duration) - TextView duration; - @BindView(R.id.chat_return_text) - TextView headerStatusText; - @BindView(R.id.chat_audio_photo) - ImageView audioAvatar; - @BindView(R.id.home_activity_container_cl) - CoordinatorLayout mHomeActivityContainerCl; - @BindView(R.id.a_home_container) - RelativeLayout mHomeContainer; + @BindView(R.id.nynja_navigation_view) NynjaNavigationView mNynjaNavigationView; + @BindView(R.id.chat_active_video) SurfaceViewRenderer video; + @BindView(R.id.chat_audio_layout) View chatAudioLayout; + @BindView(R.id.chat_audio_name) TextView chatAudioName; + @BindView(R.id.chat_audio_duration) TextView duration; + @BindView(R.id.chat_return_text) TextView headerStatusText; + @BindView(R.id.chat_audio_photo) ImageView audioAvatar; + @BindView(R.id.home_activity_container_cl) CoordinatorLayout mHomeActivityContainerCl; + @BindView(R.id.a_home_container) RelativeLayout mHomeContainer; //Second Keyboard Container - @BindView(R.id.a_home_keyboard_container) - ViewGroup mSecondKeyboardContainer; + @BindView(R.id.a_home_keyboard_container) ViewGroup mSecondKeyboardContainer; + + // Call pickup banner + @BindView(R.id.call_pickup_layout) View callPickupLayout; + @BindView(R.id.call_pickup_name) TextView callPickupName; + @BindView(R.id.call_pickup_count) TextView callPickupCount; + @BindView(R.id.call_pickup_title_text) TextView callPickupHeaderText; + @BindView(R.id.call_pickup_photo) ImageView allPickupAvatar; @InjectPresenter MainActivityPresenter mPresenter; @@ -190,7 +192,7 @@ public class MainActivity extends BaseActivity implements MainActivityView, mContainer = new Container(this); mContainer.setOnScreenChangedListener(this); RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.MATCH_PARENT); - params.addRule(RelativeLayout.BELOW, R.id.chat_audio_layout); + params.addRule(RelativeLayout.BELOW, R.id.call_top_banner_layout); mHomeContainer.addView(mContainer, params); mNavigator = new HomeNavigator(this); @@ -214,6 +216,7 @@ public class MainActivity extends BaseActivity implements MainActivityView, } catch (Exception ex) { Timber.e(ex); } + mPresenter.loadPickupCalls(); } @Override @@ -395,13 +398,25 @@ public class MainActivity extends BaseActivity implements MainActivityView, private void onCallPermission(boolean incomming, boolean enableVideo, boolean createNew, boolean isGroup, Parcelable prevModel) { + onCallPermission(false, null, + incomming, enableVideo, + createNew, isGroup, + prevModel); + } + + private void onCallPermission(boolean pickup, String callId, + boolean incomming, boolean enableVideo, + boolean createNew, boolean isGroup, + Parcelable prevModel) { ArrayList permissions = getPermissionsForCall(enableVideo); String[] perms = new String[0]; addDisposable(mRxPermissions.requestEachCombined(permissions.toArray(perms)) .subscribe(permission -> { if (permission.granted) { Timber.d("All permission(s) requests finished"); - if (incomming) { + if (pickup) { + mPresenter.callPickup(callId); + } else if (incomming) { mPresenter.setContactForP2P(); mPresenter.navigateToActiveCall(); mPresenter.clearIncomingCallNotification(); @@ -592,14 +607,7 @@ public class MainActivity extends BaseActivity implements MainActivityView, @Override public void onConferenceEnded() { - mHandler.postDelayed(() -> releaseWakeLock(), Consts.DELAY_500); - runOnUiThread(() -> { - video.setVisibility(View.GONE); - chatAudioLayout.setVisibility(View.GONE); - duration.setText(""); - headerStatusText.setText(R.string.call_connecting); - chatAudioName.setText(""); - }); + onCallEnded(); } @Override @@ -610,41 +618,23 @@ public class MainActivity extends BaseActivity implements MainActivityView, @Override public void onConferenceStateChanged(ActiveConferenceCall activeConferenceCall) { - if (activeConferenceCall == null) return; - if (!mPresenter.isConferenceActive()) return; - if (!activeConferenceCall.isCallInProgress() - && !activeConferenceCall.isRinging - && !activeConferenceCall.isWaiting()) return; - if (activeConferenceCall.mConference != null && - activeConferenceCall.mConference.callState() == NYNCallState.NYNCallStateConnected) { - mHandler.post(() -> wakeLock()); - } + onCallStateChanged(activeConferenceCall); + } + + @Override + public void onStateChangedForCall(String callId) { runOnUiThread(() -> { - video.setVisibility(View.GONE); - if (activeConferenceCall != null && activeConferenceCall.isWaiting()) { - chatAudioLayout.setBackgroundResource(R.color.waiting_call_bg); - headerStatusText.setText(R.string.call_waiting_period); - } else { - chatAudioLayout.setBackgroundResource(R.color.active_call_bg); - } - chatAudioLayout.setVisibility(View.VISIBLE); - mPresenter.requestUser(); - //TODO: video feeds support in next features - //if (!activeConferenceCall.mData.isOwnStreamActive || !activeConferenceCall.hasRemoteVideoTrack) { - // video.setVisibility(View.GONE); - // chatAudioLayout.setVisibility(View.VISIBLE); - // mPresenter.requestUser(); - // if (activeConferenceCall.mConference != null) - // activeConferenceCall.mConference.setVideoRendererForTrack(video); - //} else { - // video.setVisibility(View.VISIBLE); - // chatAudioLayout.setVisibility(View.GONE); - // if (activeConferenceCall.mConference != null) { - // activeConferenceCall.mConference.setVideoRendererForTrack(null); - // } - //} - } - ); + onCallsStateChanged(); + }); + + } + + @Override + public void onAcceptedElsewhereConference(String conferenceId) { + runOnUiThread(() -> { + onCallsStateChanged(); + }); + } @Override @@ -1004,4 +994,144 @@ public class MainActivity extends BaseActivity implements MainActivityView, } ); } + + @Override + public void pickupCallWhileAnotherActiveCall() { + Dialog createdDialog = DialogFactory.createOkNoDialog(this, R.string.call_pickup_alert_while_another_call, + (dialog, which) -> { + }, null); + createdDialog.show(); + DialogFactory.changeMessageTextSize(createdDialog); + } + + private void onCallStateChanged(ActiveConferenceCall activeConferenceCall) { + if ((activeConferenceCall == null) || + (!mPresenter.isConferenceActive()) || + (!activeConferenceCall.isCallInProgress() + //&& !activeConferenceCall.isRinging + && !activeConferenceCall.isWaiting())) { + runOnUiThread(() -> { + onActiveCallStateChanged(null); + onCallsStateChanged(); + }); + return; + } + if (activeConferenceCall.mConference != null && + activeConferenceCall.mConference.callState() == NYNCallState.NYNCallStateConnected) { + mHandler.post(() -> wakeLock()); + } + runOnUiThread(() -> { + if (!mPresenter.isConferenceActive()) return; + if (activeConferenceCall.isRinging) { + onActiveCallStateChanged(null); + }else { + onActiveCallStateChanged(activeConferenceCall); + } + }); + } + + private void onCallEnded() { + mHandler.postDelayed(() -> releaseWakeLock(), Consts.DELAY_500); + runOnUiThread(() -> { + onActiveCallEnded(); + }); + } + + private void onActiveCallStateChanged(ActiveConferenceCall activeConferenceCall) { + video.setVisibility(View.GONE); + if (activeConferenceCall != null) { + if (activeConferenceCall.isWaiting()) { + chatAudioLayout.setBackgroundResource(R.color.waiting_call_bg); + headerStatusText.setText(R.string.call_waiting_period); + } else { + chatAudioLayout.setBackgroundResource(R.color.active_call_bg); + } + hideCallPickupLayout(); + chatAudioLayout.setVisibility(View.VISIBLE); + mPresenter.requestUser(); + } else { + chatAudioLayout.setVisibility(View.GONE); + } + //TODO: video feeds support in next features + //if (!activeConferenceCall.mData.isOwnStreamActive || !activeConferenceCall.hasRemoteVideoTrack) { + // video.setVisibility(View.GONE); + // chatAudioLayout.setVisibility(View.VISIBLE); + // mPresenter.requestUser(); + // if (activeConferenceCall.mConference != null) + // activeConferenceCall.mConference.setVideoRendererForTrack(video); + //} else { + // video.setVisibility(View.VISIBLE); + // chatAudioLayout.setVisibility(View.GONE); + // if (activeConferenceCall.mConference != null) { + // activeConferenceCall.mConference.setVideoRendererForTrack(null); + // } + //} + } + + private void onActiveCallEnded() { + video.setVisibility(View.GONE); + chatAudioLayout.setVisibility(View.GONE); + duration.setText(""); + headerStatusText.setText(R.string.call_connecting); + chatAudioName.setText(""); + + onCallsStateChanged(); + } + + private void onClickCallPickup() { + final HashMap calls = mPresenter.pickupCallsInfo(); + ArrayList keys = new ArrayList(calls.keySet()); + String[] values = (new ArrayList<>(calls.values())).toArray(new String[0]); +// final Context context = this; +// DialogFactory.showActionSheetDialog(this, itemPosition -> { +// if (itemPosition < keys.size()) { +// String callId = keys.get(itemPosition); +// mPresenter.callPickup(callId); +// } +// }, values); + + DialogFactory.createContextPopupDialog(this, R.string.call_pickup_alert_title_text, values, new DialogInterface.OnClickListener() { + @Override + public void onClick(DialogInterface dialogInterface, int i) { + if (i < keys.size()) { + String callId = keys.get(i); + onCallPermission(true, callId, + false, false, + false, false, + null); + } + + } + }).show(); + } + + private void hideCallPickupLayout() { + callPickupLayout.setOnClickListener(null); + callPickupLayout.setVisibility(View.GONE); + } + + + private void onCallsStateChanged() { + if (mPresenter.hasCallForPickup()) { + callPickupLayout.setOnClickListener(new View.OnClickListener() { + @Override + public void onClick(View view) { + onClickCallPickup(); + } + }); + //callPickupCount.setText(mPresenter.callPickupCount()); + callPickupLayout.setVisibility(View.VISIBLE); + callPickupName.setText(mPresenter.callPickupsText()); + ArrayList avatars = mPresenter.callPickupsAvatar(); + if (avatars.size() > 0) { + ImageUtils.loadAvatarImage(avatars.get(0), allPickupAvatar); + allPickupAvatar.setVisibility(View.VISIBLE); + } else { + allPickupAvatar.setVisibility(View.GONE); + } + } else { + hideCallPickupLayout(); + } + } + } diff --git a/app/src/main/java/com/nynja/mobile/communicator/utils/DialogFactory.java b/app/src/main/java/com/nynja/mobile/communicator/utils/DialogFactory.java index 4354878781437bb6b7923801a86c6585f349df81..18e2aa172bb956af5e2ff1e939ce1d7e94d3f226 100644 --- a/app/src/main/java/com/nynja/mobile/communicator/utils/DialogFactory.java +++ b/app/src/main/java/com/nynja/mobile/communicator/utils/DialogFactory.java @@ -34,6 +34,7 @@ import com.nynja.mobile.communicator.R; import com.nynja.mobile.communicator.utils.iap.InAppPurchaseManager; import com.wdullaer.materialdatetimepicker.date.DatePickerDialog; +import java.util.ArrayList; import java.util.List; import timber.log.Timber; @@ -231,11 +232,17 @@ public class DialogFactory { public static AlertDialog createContextDialog(Activity activity, @ArrayRes List itemsRes, DialogInterface.OnClickListener onClickListener) { - AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity, R.style.Nynja_Context_Dialog); String[] items = new String[itemsRes.size()]; for (int i = 0; i < itemsRes.size(); i++) { items[i] = activity.getString(itemsRes.get(i)); } + return createContextDialog(activity, items, onClickListener); + } + + public static AlertDialog createContextDialog(Activity activity, + String[] items, + DialogInterface.OnClickListener onClickListener) { + AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity, R.style.Nynja_Context_Dialog); dialogBuilder.setItems(items, onClickListener); return dialogBuilder.create(); } @@ -243,16 +250,25 @@ public class DialogFactory { public static AlertDialog createContextPopupDialog(Activity activity, @ArrayRes List itemsRes, DialogInterface.OnClickListener onClickListener) { + String[] items = new String[itemsRes.size()]; + for (int i = 0; i < itemsRes.size(); i++) { + items[i] = (activity.getString(itemsRes.get(i))); + } + return createContextPopupDialog(activity, items, onClickListener); + } + + public static AlertDialog createContextPopupDialog(Activity activity, + String[] items, + DialogInterface.OnClickListener onClickListener) { AlertDialog.Builder dialogBuilder = new AlertDialog.Builder(activity, R.style.Nynja_PopupMenu_AlertDialog); + final ArrayAdapter arrayAdapter = new ArrayAdapter(activity, R.layout.li_alert_popup_menu_item); - for (int i = 0; i < itemsRes.size(); i++) { - arrayAdapter.add(activity.getString(itemsRes.get(i))); - } + arrayAdapter.addAll(items); dialogBuilder.setAdapter(arrayAdapter, onClickListener); AlertDialog dialog = dialogBuilder.create(); ListView listView = dialog.getListView(); - listView.setDivider(new ColorDrawable(ContextCompat.getColor(activity, R.color.lite_grey))); + listView.setDivider(new ColorDrawable(ContextCompat.getColor(activity, R.color.colorScheduleBackground))); listView.setDividerHeight(1); @@ -279,6 +295,26 @@ public class DialogFactory { return dialog; } + public static AlertDialog createContextPopupDialog(Activity activity, int title, + String[] items, + DialogInterface.OnClickListener onClickListener) { + AlertDialog dialog = createContextPopupDialog(activity, items, onClickListener); + if (title > 0) { + dialog.setTitle(title); + } + return dialog; + } + + public static AlertDialog createContextPopupDialog(Activity activity, String title, + String[] items, + DialogInterface.OnClickListener onClickListener) { + AlertDialog dialog = createContextPopupDialog(activity, items, onClickListener); + if (StringUtils.isNotEmpty(title)) { + dialog.setTitle(title); + } + return dialog; + } + public static AlertDialog createContextPopupDialog(Activity activity, int title, int message, diff --git a/app/src/main/res/layout/activity_home.xml b/app/src/main/res/layout/activity_home.xml index 31948f84636f4823fc0068be4687490d80910048..3b17c6014a921bc224eef3b3b7082e29a1c48dd2 100644 --- a/app/src/main/res/layout/activity_home.xml +++ b/app/src/main/res/layout/activity_home.xml @@ -1,11 +1,11 @@ - + android:orientation="vertical" + xmlns:tools="http://schemas.android.com/tools"> + android:layout_below="@+id/call_top_banner_layout"/> - + android:orientation="vertical"> + + + + + + diff --git a/app/src/main/res/layout/li_alert_popup_menu_item.xml b/app/src/main/res/layout/li_alert_popup_menu_item.xml index 19e4bfb0d7a315486bb5cbf7497b034048fe5826..c118f92f8713330a16330e205c255abc4cbf58b9 100644 --- a/app/src/main/res/layout/li_alert_popup_menu_item.xml +++ b/app/src/main/res/layout/li_alert_popup_menu_item.xml @@ -3,7 +3,7 @@ xmlns:android="http://schemas.android.com/apk/res/android" android:layout_width="match_parent" android:layout_height="wrap_content" - android:background="@drawable/dialog_popup_menu_item_selector" + android:background="@color/colorContextMenuSeparator" android:ellipsize="marquee" android:gravity="center" android:paddingBottom="@dimen/padding_small" diff --git a/app/src/main/res/layout/partial_call_pickup.xml b/app/src/main/res/layout/partial_call_pickup.xml new file mode 100644 index 0000000000000000000000000000000000000000..7697cd40281f47a7a83a5f6a6c444f08d88a64e9 --- /dev/null +++ b/app/src/main/res/layout/partial_call_pickup.xml @@ -0,0 +1,79 @@ + + + + + + + + + + + + + + + + + + + + \ 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 4b8c7a2ef62afcd0871ab972d4c5234d6ba40b44..8da13b5fc27f8f228536bb445248183e6648ad26 100644 --- a/app/src/main/res/values-en/strings.xml +++ b/app/src/main/res/values-en/strings.xml @@ -1341,4 +1341,9 @@ To start your camera, you need to stop screen sharing first. Continue + + Call pickup + Choose call to pickup + You can\'t call when you are talking + diff --git a/app/src/main/res/values-es/strings.xml b/app/src/main/res/values-es/strings.xml index a93d6de9c0065509c19642db2785d60894cecef2..cc31e962dc3c4e076481e5f05c6550eed4eb8247 100644 --- a/app/src/main/res/values-es/strings.xml +++ b/app/src/main/res/values-es/strings.xml @@ -1340,4 +1340,9 @@ To start your camera, you need to stop screen sharing first. Continue + + Call pickup + Choose call to pickup + You can\'t call when you are talking + diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml index 3fb29a0c692d48716fa723198251cb3c45944326..58547c814f7c170b6611b4ec5f369f9891531fa7 100644 --- a/app/src/main/res/values-ko/strings.xml +++ b/app/src/main/res/values-ko/strings.xml @@ -1340,4 +1340,9 @@ To start your camera, you need to stop screen sharing first. Continue + + Call pickup + Choose call to pickup + You can\'t call when you are talking + diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 4716478128cd8713aa676fa53c4df514a509cfac..c2bf97f05a2cdd97cc977d489d94bdc06eace2fd 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -1340,4 +1340,9 @@ To start your camera, you need to stop screen sharing first. Continue + + Call pickup + Choose call to pickup + You can\'t call when you are talking + diff --git a/app/src/main/res/values-zh/strings.xml b/app/src/main/res/values-zh/strings.xml index 4716478128cd8713aa676fa53c4df514a509cfac..c2bf97f05a2cdd97cc977d489d94bdc06eace2fd 100644 --- a/app/src/main/res/values-zh/strings.xml +++ b/app/src/main/res/values-zh/strings.xml @@ -1340,4 +1340,9 @@ To start your camera, you need to stop screen sharing first. Continue + + Call pickup + Choose call to pickup + You can\'t call when you are talking + diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml index 70dc74265cba73b9ad4c01c623f11682fc14b095..e43dd50e4982b992b549a9f7a4619d3f338d67ca 100644 --- a/app/src/main/res/values/colors.xml +++ b/app/src/main/res/values/colors.xml @@ -99,4 +99,6 @@ #A6ffffff #00000000 + #F3AF22 + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index d18544ed30fecdf3ed420eb97e5ff3aae882f39b..7930de99a5f9f0a7f523ba2359588673806499cb 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -1342,4 +1342,9 @@ To start your camera, you need to stop screen sharing first. Continue + + Call pickup + Choose call to pickup + You can\'t call when you are talking + diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 61669101efce91af20350ecb2dc1c204cc5e2bd2..83cd4e10db6629e6316ea2f277fdc60e4c9fe724 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -43,7 +43,7 @@