Skip to content

Commit bd8568c

Browse files
authored
Merge pull request #74 from ciscoRankush/master
Release 3.11.0
2 parents 740d2b9 + bfe80ed commit bd8568c

16 files changed

+491
-28
lines changed

app/build.gradle

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@ android {
3232
applicationId "com.cisco.sdk_android"
3333
minSdkVersion Versions.minSdk
3434
targetSdkVersion Versions.targetSdk
35-
versionCode 3100100
36-
versionName "3.10.1"
35+
versionCode 3110000
36+
versionName "3.11.0"
3737

3838
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
3939

@@ -63,6 +63,11 @@ android {
6363
buildConfigField "String", "SCOPE", "${SCOPE}"
6464
}
6565

66+
packagingOptions {
67+
jniLibs {
68+
pickFirsts += ['lib/armeabi-v7a/libc++_shared.so', 'lib/arm64-v8a/libc++_shared.so', 'lib/x86/libc++_shared.so', 'lib/x86_64/libc++_shared.so']
69+
}
70+
}
6671
buildTypes {
6772
release {
6873
minifyEnabled true
@@ -114,10 +119,10 @@ android {
114119

115120
dependencies {
116121

117-
//At a time only one WebexSDK should be used.
118-
implementation 'com.ciscowebex:webexsdk:3.10.1' // For full flavor
119-
//implementation 'com.ciscowebex:webexsdk-wxc:3.10.1' //For webexCalling flavor
120-
//implementation 'com.ciscowebex:webexsdk-meeting:3.10.1' // For meeting flavor
122+
//At a time only one WebexSDK should be used.
123+
implementation 'com.ciscowebex:webexsdk:3.11.0' // For full flavor
124+
//implementation 'com.ciscowebex:webexsdk-wxc:3.11.0' //For webexCalling flavor
125+
//implementation 'com.ciscowebex:webexsdk-meeting:3.11.0' // For meeting flavor
121126

122127
implementation fileTree(dir: "libs", include: ["*.jar"])
123128
implementation Dependencies.kotlinStdLib

app/proguard-rules.pro

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -117,4 +117,7 @@
117117

118118
-keep enum com.ciscowebex.androidsdk.utils.internal.NetTypes{
119119
*;
120-
}
120+
}
121+
-keep class com.cisco.newb.** {
122+
*;
123+
}

app/src/main/java/com/ciscowebex/androidsdk/kitchensink/WebexRepository.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -106,7 +106,8 @@ class WebexRepository(val webex: Webex) : WebexUCLoginDelegate {
106106
InCorrectPasswordWithCaptcha,
107107
InCorrectPasswordOrHostKey,
108108
InCorrectPasswordOrHostKeyWithCaptcha,
109-
WrongApiCalled
109+
WrongApiCalled,
110+
CannotStartInstantMeeting
110111
}
111112

112113
enum class CalendarMeetingEvent {

app/src/main/java/com/ciscowebex/androidsdk/kitchensink/WebexViewModel.kt

Lines changed: 142 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,7 @@ import com.ciscowebex.androidsdk.kitchensink.firebase.RegisterTokenService
1212
import com.ciscowebex.androidsdk.kitchensink.person.PersonModel
1313
import com.ciscowebex.androidsdk.CompletionHandler
1414
import com.ciscowebex.androidsdk.WebexError
15+
import com.ciscowebex.androidsdk.annotation.renderer.LiveAnnotationRenderer
1516
import com.google.android.gms.tasks.OnCompleteListener
1617
import com.google.android.gms.tasks.Task
1718
import com.google.firebase.messaging.FirebaseMessaging
@@ -34,6 +35,7 @@ import com.ciscowebex.androidsdk.phone.CallMembership
3435
import com.ciscowebex.androidsdk.phone.Phone
3536
import com.ciscowebex.androidsdk.phone.CallAssociationType
3637
import com.ciscowebex.androidsdk.phone.AdvancedSetting
38+
import com.ciscowebex.androidsdk.phone.MakeHostError
3739
import com.ciscowebex.androidsdk.phone.AuxStream
3840
import com.ciscowebex.androidsdk.phone.VirtualBackground
3941
import com.ciscowebex.androidsdk.phone.CameraExposureISO
@@ -44,10 +46,14 @@ import com.ciscowebex.androidsdk.phone.MediaStreamQuality
4446
import com.ciscowebex.androidsdk.phone.BreakoutSession
4547
import com.ciscowebex.androidsdk.phone.Breakout
4648
import com.ciscowebex.androidsdk.phone.DirectTransferResult
49+
import com.ciscowebex.androidsdk.phone.InviteParticipantError
4750
import com.ciscowebex.androidsdk.phone.SwitchToAudioVideoCallResult
4851
import com.ciscowebex.androidsdk.phone.PhoneConnectionResult
4952
import com.ciscowebex.androidsdk.phone.ReceivingNoiseInfo
5053
import com.ciscowebex.androidsdk.phone.ReceivingNoiseRemovalEnableResult
54+
import com.ciscowebex.androidsdk.phone.ReclaimHostError
55+
import com.ciscowebex.androidsdk.phone.annotation.LiveAnnotationListener
56+
import com.ciscowebex.androidsdk.phone.annotation.LiveAnnotationsPolicy
5157
import com.ciscowebex.androidsdk.phone.closedCaptions.CaptionItem
5258
import com.ciscowebex.androidsdk.phone.closedCaptions.ClosedCaptionsInfo
5359
import com.google.firebase.installations.FirebaseInstallations
@@ -98,6 +104,14 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
98104
private val _initialSpacesSyncCompletedLiveData = MutableLiveData<Boolean>()
99105
val initialSpacesSyncCompletedLiveData: LiveData<Boolean> = _initialSpacesSyncCompletedLiveData
100106

107+
private val _annotationEvent = MutableLiveData<AnnotationEvent>()
108+
val annotationEvent: LiveData<AnnotationEvent> get() = _annotationEvent
109+
sealed class AnnotationEvent {
110+
data class PERMISSION_ASK(val personId: String) : AnnotationEvent()
111+
data class PERMISSION_EXPIRED(val personId: String) : AnnotationEvent()
112+
}
113+
114+
101115
var selfPersonId: String? = null
102116
var compositedLayoutState = MediaOption.CompositedVideoLayout.NOT_SUPPORTED
103117

@@ -403,6 +417,9 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
403417
WebexError.ErrorCode.INVALID_PASSWORD_OR_HOST_KEY_WITH_CAPTCHA.code -> {
404418
_callingLiveData.postValue(WebexRepository.CallLiveData(WebexRepository.CallEvent.InCorrectPasswordOrHostKeyWithCaptcha, null, error.data as Phone.Captcha))
405419
}
420+
WebexError.ErrorCode.CANNOT_START_INSTANT_MEETING.code -> {
421+
_callingLiveData.postValue(WebexRepository.CallLiveData(WebexRepository.CallEvent.CannotStartInstantMeeting, null, null, result.error?.errorMessage))
422+
}
406423
else -> {
407424
_callingLiveData.postValue(WebexRepository.CallLiveData(WebexRepository.CallEvent.DialFailed, null, null, result.error?.errorMessage))
408425
}
@@ -463,6 +480,7 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
463480
override fun onDisconnected(event: CallObserver.CallDisconnectedEvent?) {
464481
Log.d(tag, "CallObserver onDisconnected event: ${this@WebexViewModel} $callObserverInterface $event")
465482
callObserverInterface?.onDisconnected(call, event)
483+
annotationRenderer?.stopRendering()
466484
}
467485

468486
override fun onInfoChanged(call: Call?) {
@@ -621,15 +639,22 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
621639
}
622640

623641
fun startShare(callId: String, shareConfig: ShareConfig?) {
624-
getCall(callId)?.startSharing(CompletionHandler { result ->
625-
_startShareLiveData.postValue(result.isSuccessful)
626-
}, shareConfig)
642+
val call = getCall(callId)
643+
call?.let {
644+
it.startSharing(CompletionHandler { result ->
645+
_startShareLiveData.postValue(result.isSuccessful)
646+
647+
}, shareConfig)
648+
}
627649
}
628650

629651
fun startShare(callId: String, notification: Notification?, notificationId: Int, shareConfig: ShareConfig?) {
630-
getCall(callId)?.startSharing(notification, notificationId, CompletionHandler { result ->
631-
_startShareLiveData.postValue(result.isSuccessful)
632-
}, shareConfig)
652+
val call = getCall(callId)
653+
call?.let {
654+
it.startSharing(notification, notificationId, CompletionHandler { result ->
655+
_startShareLiveData.postValue(result.isSuccessful)
656+
}, shareConfig)
657+
}
633658
}
634659

635660
fun setSendingSharing(callId: String, value: Boolean) {
@@ -642,6 +667,80 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
642667
})
643668
}
644669

670+
private var annotationRenderer: LiveAnnotationRenderer? = null
671+
fun initalizeAnnotations(renderer: LiveAnnotationRenderer) {
672+
getCall(currentCallId.orEmpty())?.getLiveAnnotationHandle()?.let {annotations->
673+
674+
annotations.setLiveAnnotationsPolicy(LiveAnnotationsPolicy.NeedAskForAnnotate){
675+
if (it.isSuccessful) {
676+
Log.d(tag, "setLiveAnnotationsPolicy successful")
677+
} else {
678+
Log.d(tag, "setLiveAnnotationsPolicy error: ${it.error?.errorMessage}")
679+
}
680+
}
681+
682+
annotations.setLiveAnnotationListener(object : LiveAnnotationListener {
683+
override fun onLiveAnnotationRequestReceived(personId: String) {
684+
_annotationEvent.postValue(AnnotationEvent.PERMISSION_ASK(personId))
685+
}
686+
687+
override fun onLiveAnnotationRequestExpired(personId: String) {
688+
_annotationEvent.postValue(AnnotationEvent.PERMISSION_EXPIRED(personId))
689+
}
690+
691+
override fun onLiveAnnotationsStarted() {
692+
annotationRenderer = renderer.apply {
693+
setAnnotationRendererCallback(object : LiveAnnotationRenderer.LiveAnnotationRendererCallback {
694+
override fun onAnnotationRenderingReady() {
695+
Log.d(tag, "onAnnotationRenderingReady")
696+
}
697+
698+
override fun onAnnotationRenderingStopped() {
699+
Log.d(tag, "onAnnotationRenderingStopped")
700+
getCall(currentCallId.orEmpty())?.getLiveAnnotationHandle()?.stopLiveAnnotations()
701+
annotationRenderer = null
702+
}
703+
})
704+
startRendering()
705+
}
706+
}
707+
708+
override fun onLiveAnnotationDataArrived(data: String) {
709+
annotationRenderer?.renderData(data)
710+
}
711+
712+
override fun onLiveAnnotationsStopped() {
713+
annotationRenderer?.stopRendering()
714+
}
715+
716+
})
717+
}
718+
}
719+
720+
fun handleAnnotationPermission(grant: Boolean, personId: String) {
721+
getCall(currentCallId.orEmpty())?.getLiveAnnotationHandle()?.respondToLiveAnnotationRequest(personId, grant) {
722+
if (it.isSuccessful) {
723+
Log.d(tag, "permission handled")
724+
} else {
725+
Log.d(tag, "permission error: ${it.error?.errorMessage}")
726+
}
727+
}
728+
}
729+
730+
fun getCurrentLiveAnnotationPolicy(): LiveAnnotationsPolicy? {
731+
return getCall(currentCallId.orEmpty())?.getLiveAnnotationHandle()?.getLiveAnnotationsPolicy()
732+
}
733+
734+
fun setLiveAnnotationPolicy(policy: LiveAnnotationsPolicy) {
735+
getCall(currentCallId.orEmpty())?.getLiveAnnotationHandle()?.setLiveAnnotationsPolicy(policy) {
736+
if (it.isSuccessful) {
737+
Log.d(tag, "setLiveAnnotationsPolicy successful")
738+
} else {
739+
Log.d(tag, "setLiveAnnotationsPolicy error: ${it.error?.errorMessage}")
740+
}
741+
}
742+
}
743+
645744
fun sendFeedback(callId: String, rating: Int, comment: String) {
646745
getCall(callId)?.sendFeedback(rating, comment)
647746
}
@@ -1268,6 +1367,30 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
12681367
return webex.phone.getCallingType()
12691368
}
12701369

1370+
fun makeHost(participantId: String, handler: CompletionHandler<MakeHostError>) {
1371+
getCall(currentCallId.orEmpty())?.makeHost(participantId) { result ->
1372+
if (result.isSuccessful) {
1373+
Log.d(tag, "Make host successful")
1374+
handler.onComplete(ResultImpl.success())
1375+
} else {
1376+
Log.d(tag, "Make host failed")
1377+
handler.onComplete(ResultImpl.error(result.error?.errorMessage))
1378+
}
1379+
}
1380+
}
1381+
1382+
fun reclaimHost(hostKey:String, handler: CompletionHandler<ReclaimHostError>) {
1383+
getCall(currentCallId.orEmpty())?.reclaimHost(hostKey) { result ->
1384+
if (result.isSuccessful) {
1385+
Log.d(tag, "reclaimHost successful")
1386+
handler.onComplete(ResultImpl.success())
1387+
} else {
1388+
Log.d(tag, "reclaimHost failed")
1389+
handler.onComplete(ResultImpl.error(result.error?.errorMessage))
1390+
}
1391+
}
1392+
}
1393+
12711394
fun setOnInitialSpacesSyncCompletedListener() {
12721395
repository.setOnInitialSpacesSyncCompletedListener() {
12731396
_initialSpacesSyncCompletedLiveData.postValue(true)
@@ -1294,6 +1417,18 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
12941417
return getCall(currentCallId.orEmpty())?.isVideoEnabled() ?: false
12951418
}
12961419

1420+
fun inviteParticipant(invitee: String, callback: CompletionHandler<InviteParticipantError>) {
1421+
getCall(currentCallId.orEmpty())?.inviteParticipant(invitee) { result ->
1422+
if (result.isSuccessful) {
1423+
Log.d(tag, "InviteParticipant successful")
1424+
callback.onComplete(ResultImpl.success())
1425+
} else {
1426+
Log.d(tag, "InviteParticipant failed")
1427+
callback.onComplete(ResultImpl.error(result.error?.errorMessage))
1428+
}
1429+
}
1430+
}
1431+
12971432
fun cleanup() {
12981433
repository.removeIncomingCallListener("viewmodel"+this)
12991434
for (entry in callObserverMap.entries.iterator()) {
@@ -1317,4 +1452,5 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
13171452
writer.println("******************")
13181453
repository.printObservers(writer)
13191454
}
1455+
13201456
}

app/src/main/java/com/ciscowebex/androidsdk/kitchensink/calling/CallBottomSheetFragment.kt

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ class CallBottomSheetFragment(val showIncomingCallsClickListener: (Call?) -> Uni
2626
val cameraOptionsClickListener: (Call?) -> Unit,
2727
val multiStreamOptionsClickListener: (Call?) -> Unit,
2828
val sendDTMFClickListener: (Call?) -> Unit,
29+
val claimHostClickListener: () -> Unit,
2930
val showBreakoutSessions: () -> Unit,
3031
val closedCaptionOptions: (Call?) -> Unit): BottomSheetDialogFragment() {
3132
companion object {
@@ -183,6 +184,11 @@ class CallBottomSheetFragment(val showIncomingCallsClickListener: (Call?) -> Uni
183184
transcriptionClickListener(call)
184185
}
185186

187+
claimHost.setOnClickListener {
188+
dismiss()
189+
claimHostClickListener()
190+
}
191+
186192
showIncomingCall.setOnClickListener {
187193
dismiss()
188194
showIncomingCallsClickListener(call)

0 commit comments

Comments
 (0)