Skip to content

Commit 773f129

Browse files
authored
Merge pull request #91 from namra25/rel3.13
Release 3.13.0
2 parents 7baf279 + 6082646 commit 773f129

13 files changed

Lines changed: 365 additions & 31 deletions

File tree

README.md

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -79,19 +79,19 @@ This demo support Android device with **Android 7.0** or later
7979
- For Full SDK
8080
```
8181
dependencies {
82-
implementation 'com.ciscowebex:webexsdk:3.12.0'
82+
implementation 'com.ciscowebex:webexsdk:3.13.0'
8383
}
8484
```
8585
- For Meeting SDK
8686
```
8787
dependencies {
88-
implementation 'com.ciscowebex:webexsdk-meeting:3.12.0'
88+
implementation 'com.ciscowebex:webexsdk-meeting:3.13.0'
8989
}
9090
```
9191
- For WebexCalling SDK
9292
```
9393
dependencies {
94-
implementation 'com.ciscowebex:webexsdk-wxc:3.12.0'
94+
implementation 'com.ciscowebex:webexsdk-wxc:3.13.0'
9595
}
9696
```
9797

app/build.gradle

Lines changed: 5 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 3120000
36-
versionName "3.12.0"
35+
versionCode 3130000
36+
versionName "3.13.0"
3737

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

@@ -121,10 +121,9 @@ android {
121121

122122
dependencies {
123123
//At a time only one WebexSDK should be used.
124-
implementation 'com.ciscowebex:webexsdk:3.12.0' // For full flavor
125-
//implementation 'com.ciscowebex:webexsdk-wxc:3.12.0' //For webexCalling flavor
126-
//implementation 'com.ciscowebex:webexsdk-meeting:3.12.0' // For meeting flavor
127-
124+
implementation 'com.ciscowebex:webexsdk:3.13.0' // For full flavor
125+
//implementation 'com.ciscowebex:webexsdk-wxc:3.13.0' //For webexCalling flavor
126+
//implementation 'com.ciscowebex:webexsdk-meeting:3.13.0' // For meeting flavor
128127
implementation fileTree(dir: "libs", include: ["*.jar"])
129128
implementation Dependencies.kotlinStdLib
130129
implementation Dependencies.coreKtx

app/proguard-rules.pro

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -112,14 +112,17 @@
112112
*;
113113
}
114114

115-
-keep public enum com.webex.scf.commonhead.models.**{
115+
-keep public enum com.ciscowebex.androidsdk.omniusenums.**{
116116
*;
117117
}
118118

119-
-keep public class com.webex.scf.commonhead.models.**{
119+
-keep public class com.ciscowebex.androidsdk.omniusmodels.**{
120120
*;
121121
}
122122

123123
-keep enum com.ciscowebex.androidsdk.utils.internal.NetTypes{
124124
*;
125+
}
126+
-keep class com.ciscowebex.androidsdk.phone.internal.RenderSink{
127+
*;
125128
}

app/src/androidTest/java/com/ciscowebex/androidsdk/kitchensink/teams/membership/TeamFragmentTest.kt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -74,13 +74,13 @@ class TeamFragmentTest : KitchenSinkTest() {
7474
onView(withId(R.id.teamsRecyclerView)).check(matches(hasDescendant(withText(testTeam))))
7575
}
7676

77-
// @Test
78-
// fun addPersonToTeam_teamFragment(){
79-
// goToMessagingActivity()
80-
// onView(withId(R.id.teamsRecyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition<TeamsClientViewHolder>(0, clickChildViewWithId(R.id.iv_add_to_team)))
81-
// WaitUtils.sleep(TIME_1_SEC)
82-
// intended(hasComponent(MessagingSearchActivity::class.java.name))
83-
// }
77+
@Test
78+
fun addPersonToTeam_teamFragment(){
79+
goToMessagingActivity()
80+
onView(withId(R.id.teamsRecyclerView)).perform(RecyclerViewActions.actionOnItemAtPosition<TeamsClientViewHolder>(0, clickChildViewWithId(R.id.iv_add_to_team)))
81+
WaitUtils.sleep(TIME_1_SEC)
82+
intended(hasComponent(MessagingSearchActivity::class.java.name))
83+
}
8484

8585
@Test
8686
fun testBottomSheetOptions_teamsFragment(){

app/src/main/AndroidManifest.xml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -201,10 +201,15 @@
201201
<action android:name="com.google.firebase.MESSAGING_EVENT" />
202202
</intent-filter>
203203
</service>
204+
204205
<service
205206
android:name=".KitchenSinkForegroundService"
206207
android:exported="false" />
207208

209+
<service android:name=".CallManagementService"
210+
android:foregroundServiceType="phoneCall"
211+
android:exported="false" />
212+
208213
<meta-data
209214
android:name="com.google.firebase.messaging.default_notification_icon"
210215
android:resource="@drawable/app_notification_icon" />
Lines changed: 129 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,129 @@
1+
package com.ciscowebex.androidsdk.kitchensink
2+
3+
import android.app.Notification
4+
import android.app.NotificationChannel
5+
import android.app.NotificationManager
6+
import android.app.Service
7+
import android.content.BroadcastReceiver
8+
import android.content.Context
9+
import android.content.Intent
10+
import android.os.Build
11+
import android.os.IBinder
12+
import android.util.Log
13+
import androidx.annotation.RequiresApi
14+
import androidx.core.app.NotificationCompat
15+
import com.ciscowebex.androidsdk.kitchensink.utils.CallObjectStorage
16+
import com.ciscowebex.androidsdk.phone.Call
17+
import com.ciscowebex.androidsdk.phone.CallObserver
18+
19+
20+
class CallManagementService : Service() {
21+
private val tag = "CallManagementService"
22+
private var receiver: BroadcastReceiver? = null
23+
24+
override fun onBind(intent: Intent?): IBinder? {
25+
return null
26+
}
27+
28+
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
29+
if (isForegroundServiceSupported()) {
30+
startForeground(1, getNotification())
31+
}
32+
return START_STICKY
33+
}
34+
35+
override fun onTaskRemoved(rootIntent: Intent?) {
36+
Log.d(tag, "onTaskRemoved called!")
37+
super.onTaskRemoved(rootIntent)
38+
val call = getOngoingCall()
39+
if (call != null) {
40+
endCall(call)
41+
} else {
42+
Log.d(tag, "No call found in connected state. Killing service!")
43+
stopSelf()
44+
}
45+
}
46+
47+
override fun onDestroy() {
48+
super.onDestroy()
49+
if (receiver != null) {
50+
unregisterReceiver(receiver)
51+
}
52+
}
53+
54+
@RequiresApi(Build.VERSION_CODES.O)
55+
private fun getNotification(): Notification {
56+
// Create a notification for the foreground service
57+
val channelId = "ks_02"
58+
val notificationChannel =
59+
NotificationChannel(
60+
channelId, "Call Management Service Notifications",
61+
NotificationManager.IMPORTANCE_MIN
62+
)
63+
notificationChannel.enableLights(false)
64+
notificationChannel.lockscreenVisibility = Notification.VISIBILITY_SECRET
65+
val mNotificationManager = getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager
66+
mNotificationManager.createNotificationChannel(notificationChannel)
67+
68+
val title = getString(R.string.call_management_service_notification_title)
69+
return NotificationCompat.Builder(
70+
this,
71+
channelId
72+
).setSmallIcon(R.drawable.app_notification_icon)
73+
.setWhen(0)
74+
.setContentTitle(title)
75+
.setOngoing(true)
76+
.build()
77+
}
78+
79+
private fun getOngoingCall(): Call? {
80+
var call: Call? = null
81+
82+
for (i in 0 until CallObjectStorage.size()) {
83+
val callObj = CallObjectStorage.getCallObjectFromIndex(i)
84+
val status = callObj?.getStatus()
85+
if (status == Call.CallStatus.CONNECTED || status == Call.CallStatus.RINGING
86+
|| status == Call.CallStatus.WAITING || status == Call.CallStatus.INITIATED) {
87+
Log.d(tag, "Call with id = ${callObj.getCallId()} found in ${status.name} state. ")
88+
call = callObj
89+
break
90+
}
91+
}
92+
return call
93+
}
94+
95+
private fun endCall(call: Call) {
96+
Log.d(tag, "Ending call with id = ${call.getCallId()}")
97+
call.setObserver(object : CallObserver {
98+
override fun onDisconnected(event: CallObserver.CallDisconnectedEvent?) {
99+
Log.d(tag, "Call disconnected fired, stopping callManagementService!")
100+
stopSelf()
101+
}
102+
103+
})
104+
if (call.getDirection() == Call.Direction.INCOMING && call.getStatus() == Call.CallStatus.RINGING) {
105+
call.reject {
106+
if (it.isSuccessful) {
107+
Log.d(tag, "Call rejected successfully. Waiting for call disconnected event")
108+
} else {
109+
Log.e(tag, "Call reject failed, reason : ${it.error?.errorMessage}")
110+
stopSelf()
111+
}
112+
}
113+
} else {
114+
call.hangup { result ->
115+
if (result.isSuccessful) {
116+
Log.d(tag, "Call hung up success. Waiting for call disconnected event")
117+
} else {
118+
Log.e(tag, "Call hangup failed, reason : ${result.error?.errorMessage}")
119+
stopSelf()
120+
}
121+
}
122+
}
123+
124+
}
125+
126+
private fun isForegroundServiceSupported(): Boolean {
127+
return (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O)
128+
}
129+
}

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

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -164,7 +164,9 @@ class WebexRepository(val webex: Webex) : WebexUCLoginDelegate, WebexAuthDelegat
164164
var _startAssociationLiveData: MutableLiveData<CallLiveData>? = null
165165
var _startShareLiveData: MutableLiveData<Boolean>? = null
166166
var _stopShareLiveData: MutableLiveData<Boolean>? = null
167-
167+
var _startAudioDumpLiveData: MutableLiveData<Boolean>? = null
168+
var _stopAudioDumpLiveData: MutableLiveData<Boolean>? = null
169+
var _canStartAudioDumpLiveData: MutableLiveData<Boolean>? = null
168170
var _spaceEventLiveData: MutableLiveData<Pair<SpaceEvent, Any?>>? = null
169171
var spaceEventListener : SpaceEventListener? = null
170172
var _membershipEventLiveData: MutableLiveData<Pair<MembershipEvent, Membership?>>? = null
@@ -196,6 +198,9 @@ class WebexRepository(val webex: Webex) : WebexUCLoginDelegate, WebexAuthDelegat
196198
_startAssociationLiveData = null
197199
_startShareLiveData = null
198200
_stopShareLiveData = null
201+
_startAudioDumpLiveData = null
202+
_stopAudioDumpLiveData = null
203+
_canStartAudioDumpLiveData = null
199204
}
200205

201206
fun clearSpaceData(){

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

Lines changed: 47 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,6 @@ import com.ciscowebex.androidsdk.phone.MediaStream
4545
import com.ciscowebex.androidsdk.phone.MediaStreamQuality
4646
import com.ciscowebex.androidsdk.phone.BreakoutSession
4747
import com.ciscowebex.androidsdk.phone.Breakout
48-
import com.ciscowebex.androidsdk.phone.CompanionMode
4948
import com.ciscowebex.androidsdk.phone.DirectTransferResult
5049
import com.ciscowebex.androidsdk.phone.InviteParticipantError
5150
import com.ciscowebex.androidsdk.phone.SwitchToAudioVideoCallResult
@@ -74,6 +73,9 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
7473
val _setCompositeLayoutLiveData = MutableLiveData<Pair<Boolean, String>>()
7574
val _setRemoteVideoRenderModeLiveData = MutableLiveData<Pair<Boolean, String>>()
7675
val _forceSendingVideoLandscapeLiveData = MutableLiveData<Boolean>()
76+
val _startAudioDumpLiveData = MutableLiveData<Boolean>()
77+
val _stopAudioDumpLiveData = MutableLiveData<Boolean>()
78+
val _canStartAudioDumpLiveData = MutableLiveData<Boolean>()
7779

7880
var callMembershipsLiveData: LiveData<List<CallMembership>> = _callMembershipsLiveData
7981
val muteAllLiveData: LiveData<Boolean> = _muteAllLiveData
@@ -86,6 +88,9 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
8688
val setCompositeLayoutLiveData: LiveData<Pair<Boolean, String>> = _setCompositeLayoutLiveData
8789
val setRemoteVideoRenderModeLiveData: LiveData<Pair<Boolean, String>> = _setRemoteVideoRenderModeLiveData
8890
val forceSendingVideoLandscapeLiveData: LiveData<Boolean> = _forceSendingVideoLandscapeLiveData
91+
val startAudioDumpLiveData: LiveData<Boolean> = _startAudioDumpLiveData
92+
val stopAudioDumpLiveData: LiveData<Boolean> = _stopAudioDumpLiveData
93+
val canStartAudioDumpLiveData: LiveData<Boolean> = _canStartAudioDumpLiveData
8994

9095
private val _incomingListenerLiveData = MutableLiveData<Call?>()
9196
val incomingListenerLiveData: LiveData<Call?> = _incomingListenerLiveData
@@ -279,6 +284,9 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
279284
repository._startAssociationLiveData = _startAssociationLiveData
280285
repository._startShareLiveData = _startShareLiveData
281286
repository._stopShareLiveData = _stopShareLiveData
287+
repository._startAudioDumpLiveData = _startAudioDumpLiveData
288+
repository._stopAudioDumpLiveData = _stopAudioDumpLiveData
289+
repository._canStartAudioDumpLiveData = _canStartAudioDumpLiveData
282290
}
283291

284292
fun setLogLevel(logLevel: String) {
@@ -1458,4 +1466,42 @@ class WebexViewModel(val webex: Webex, val repository: WebexRepository) : BaseVi
14581466
repository.printObservers(writer)
14591467
}
14601468

1469+
fun startAudioDump() {
1470+
getCall(currentCallId.orEmpty())?.startRecordingAudioDump(KitchenSinkApp.applicationContext()) {
1471+
if (it.isSuccessful) {
1472+
Log.d(tag, "[AudioDump] startAudioDump successful")
1473+
} else {
1474+
Log.d(tag, "[AudioDump] startAudioDump error: ${it.error?.errorMessage}")
1475+
}
1476+
_startAudioDumpLiveData.postValue(it.isSuccessful)
1477+
}
1478+
}
1479+
1480+
fun stopAudioDump() {
1481+
getCall(currentCallId.orEmpty())?.stopRecordingAudioDump() {
1482+
if (it.isSuccessful) {
1483+
Log.d(tag, "[AudioDump] stopAudioDump successful")
1484+
} else {
1485+
Log.d(tag, "[AudioDump] stopAudioDump error: ${it.error?.errorMessage}")
1486+
}
1487+
_stopAudioDumpLiveData.postValue(it.isSuccessful)
1488+
}
1489+
1490+
}
1491+
1492+
fun canStartRecordingAudioDump() {
1493+
getCall(currentCallId.orEmpty())?.canStartRecordingAudioDump {
1494+
if (it.isSuccessful) {
1495+
Log.d(tag, "[AudioDump] canStartRecordingAudioDump successful")
1496+
} else {
1497+
Log.d(tag, "[AudioDump] canStartRecordingAudioDump error: ${it.error?.errorMessage}")
1498+
}
1499+
_canStartAudioDumpLiveData.postValue(it.isSuccessful)
1500+
}
1501+
}
1502+
1503+
1504+
fun isRecordingAudioDump(): Boolean {
1505+
return getCall(currentCallId.orEmpty())?.isRecordingAudioDump() ?: false
1506+
}
14611507
}

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

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ class CallBottomSheetFragment(val showIncomingCallsClickListener: (Call?) -> Uni
2828
val sendDTMFClickListener: (Call?) -> Unit,
2929
val claimHostClickListener: () -> Unit,
3030
val showBreakoutSessions: () -> Unit,
31-
val closedCaptionOptions: (Call?) -> Unit): BottomSheetDialogFragment() {
31+
val closedCaptionOptions: (Call?) -> Unit,
32+
val startAudioDumpListener: () -> Unit): BottomSheetDialogFragment() {
3233
companion object {
3334
val TAG = "CallBottomSheetFragment"
3435
}
@@ -227,6 +228,11 @@ class CallBottomSheetFragment(val showIncomingCallsClickListener: (Call?) -> Uni
227228
closedCaptionOptions(call)
228229
}
229230

231+
startAudioDump.setOnClickListener {
232+
dismiss()
233+
startAudioDumpListener()
234+
}
235+
230236
cancel.setOnClickListener { dismiss() }
231237
}.root
232238
}

0 commit comments

Comments
 (0)