Skip to content

Commit df577da

Browse files
committed
4.2.0-videochat-kotlin
1 parent 8ca8994 commit df577da

26 files changed

+816
-239
lines changed

sample-videochat-kotlin/app/build.gradle

Lines changed: 13 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,10 @@ buildscript {
33
google()
44
mavenCentral()
55
}
6-
dependencies {
7-
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31"
8-
}
96
}
107

118
apply plugin: 'com.android.application'
129
apply plugin: 'kotlin-android'
13-
apply plugin: 'kotlin-android-extensions'
14-
15-
androidExtensions {
16-
experimental = true
17-
}
1810

1911
repositories {
2012
google()
@@ -26,20 +18,19 @@ repositories {
2618

2719
android {
2820
def versionQACode = 1
29-
30-
compileSdkVersion 31
31-
buildToolsVersion "31.0.0"
32-
flavorDimensions "default"
21+
namespace 'com.quickblox.sample.videochat.kotlin'
3322

3423
defaultConfig {
24+
compileSdk 34
3525
applicationId "com.quickblox.sample.videochat.kotlin"
3626
minSdkVersion 21
37-
targetSdkVersion 31
38-
versionCode 410000
39-
versionName '4.1.0'
27+
targetSdkVersion 34
28+
versionCode 420000
29+
versionName '4.2.0'
4030
multiDexEnabled true
4131
}
4232

33+
flavorDimensions "default"
4334
productFlavors {
4435
dev {
4536
dimension "default"
@@ -81,19 +72,19 @@ android {
8172
}
8273
}
8374

84-
lintOptions {
85-
abortOnError false
86-
}
8775

8876
compileOptions {
89-
sourceCompatibility JavaVersion.VERSION_1_8
90-
targetCompatibility JavaVersion.VERSION_1_8
77+
sourceCompatibility JavaVersion.VERSION_17
78+
targetCompatibility JavaVersion.VERSION_17
79+
}
80+
lint {
81+
abortOnError false
9182
}
9283
}
9384

9485
dependencies {
95-
implementation 'com.quickblox:quickblox-android-sdk-videochat-webrtc:3.10.1'
96-
implementation 'com.quickblox:quickblox-android-sdk-messages:3.10.1'
86+
implementation 'com.quickblox:quickblox-android-sdk-videochat-webrtc:4.1.3'
87+
implementation 'com.quickblox:quickblox-android-sdk-messages:4.1.3'
9788
implementation 'com.google.firebase:firebase-core:21.1.0'
9889
implementation 'com.google.android.material:material:1.6.1'
9990
implementation 'com.github.johnkil.android-robototextview:robototextview:4.0.0'

sample-videochat-kotlin/app/proguard-rules.pro

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,4 +44,7 @@
4444
-keep class com.google.android.gms.** { *; }
4545

4646
#json
47-
-keep class org.json.** { *; }
47+
-keep class org.json.** { *; }
48+
-keep class com.google.gson.reflect.TypeToken
49+
-keep class * extends com.google.gson.reflect.TypeToken
50+
-keep public class * implements java.lang.reflect.Type

sample-videochat-kotlin/app/src/main/AndroidManifest.xml

Lines changed: 11 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
<?xml version="1.0" encoding="utf-8"?>
22
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
3-
xmlns:tools="http://schemas.android.com/tools"
4-
package="com.quickblox.sample.videochat.kotlin">
3+
xmlns:tools="http://schemas.android.com/tools">
54

65
<uses-permission android:name="android.permission.CAMERA" />
76
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
@@ -13,6 +12,13 @@
1312
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
1413
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
1514
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
15+
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
16+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
17+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
18+
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />
19+
20+
<uses-feature android:name="android.hardware.camera" />
21+
<uses-feature android:name="android.hardware.camera.autofocus" />
1622

1723
<application
1824
android:name=".App"
@@ -60,8 +66,9 @@
6066
<service android:name=".services.LoginService" />
6167

6268
<service
63-
android:foregroundServiceType="mediaProjection"
64-
android:name=".services.CallService" />
69+
android:name=".services.CallService"
70+
android:exported="false"
71+
android:foregroundServiceType="camera|microphone|mediaProjection" />
6572

6673
<service
6774
android:name=".services.fcm.PushListenerService"

sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/CallActivity.kt

Lines changed: 51 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,9 @@ import android.annotation.TargetApi
44
import android.app.Activity
55
import android.app.KeyguardManager
66
import android.content.*
7+
import android.media.projection.MediaProjectionManager
78
import android.net.Uri
89
import android.os.*
9-
import android.preference.PreferenceManager
1010
import android.provider.Settings
1111
import android.util.Log
1212
import android.view.View
@@ -24,6 +24,10 @@ import com.quickblox.sample.videochat.kotlin.utils.*
2424
import com.quickblox.users.QBUsers
2525
import com.quickblox.users.model.QBUser
2626
import com.quickblox.videochat.webrtc.*
27+
import com.quickblox.videochat.webrtc.BaseSession.QBRTCSessionState.QB_RTC_SESSION_NEW
28+
import com.quickblox.videochat.webrtc.BaseSession.QBRTCSessionState.QB_RTC_SESSION_PENDING
29+
import com.quickblox.videochat.webrtc.QBRTCTypes.QBRTCReconnectionState
30+
import com.quickblox.videochat.webrtc.audio.QBAudioManager
2731
import com.quickblox.videochat.webrtc.callbacks.QBRTCClientSessionCallbacks
2832
import com.quickblox.videochat.webrtc.callbacks.QBRTCClientVideoTracksCallbacks
2933
import com.quickblox.videochat.webrtc.callbacks.QBRTCSessionEventsCallback
@@ -33,10 +37,11 @@ import org.jivesoftware.smack.AbstractConnectionListener
3337
import org.jivesoftware.smack.ConnectionListener
3438
import org.webrtc.CameraVideoCapturer
3539
import java.util.*
36-
import kotlin.collections.ArrayList
40+
3741

3842
private const val INCOME_CALL_FRAGMENT = "income_call_fragment"
3943
private const val REQUEST_PERMISSION_SETTING = 545
44+
private const val REQUEST_SHARING_MEDIA_PROJECTION = 1060
4045

4146
class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSessionStateCallback<QBRTCSession>,
4247
QBRTCClientSessionCallbacks, ConversationFragmentCallback, ScreenShareFragment.OnSharingEvents {
@@ -83,6 +88,32 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
8388
)
8489
}
8590
connectionView = View.inflate(this, R.layout.connection_popup, null) as LinearLayout
91+
92+
val isVideoCall = isVideoSession()
93+
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU && isVideoCall && isNewSession()) {
94+
requestSharingPermissions(this)
95+
return
96+
}
97+
98+
CallService.start(this)
99+
}
100+
101+
private fun isNewSession(): Boolean {
102+
val currentSession: QBRTCSession? = WebRtcSessionManager.getCurrentSession()
103+
return currentSession != null && currentSession.state == QB_RTC_SESSION_NEW || currentSession?.state == QB_RTC_SESSION_PENDING
104+
}
105+
106+
private fun isVideoSession(): Boolean {
107+
val currentSession: QBRTCSession? = WebRtcSessionManager.getCurrentSession()
108+
return currentSession != null && currentSession.conferenceType == QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO
109+
}
110+
111+
private fun requestSharingPermissions(context: Activity) {
112+
val mMediaProjectionManager = context.getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
113+
context.startActivityForResult(
114+
mMediaProjectionManager.createScreenCaptureIntent(),
115+
REQUEST_SHARING_MEDIA_PROJECTION
116+
)
86117
}
87118

88119
private fun initScreen() {
@@ -158,6 +189,14 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
158189
Log.i(TAG, "Starting screen capture")
159190
}
160191
}
192+
193+
if (requestCode == REQUEST_SHARING_MEDIA_PROJECTION) {
194+
if (resultCode == Activity.RESULT_OK) {
195+
CallService.start(this)
196+
} else {
197+
finish()
198+
}
199+
}
161200
}
162201

163202
private fun startScreenSharing(data: Intent?) {
@@ -323,7 +362,7 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
323362
}
324363

325364
override fun onBackPressed() {
326-
// to prevent returning from Call Fragment
365+
super.onBackPressed()
327366
}
328367

329368
private fun addIncomeCallFragment() {
@@ -425,6 +464,10 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
425464
}
426465
}
427466

467+
override fun onChangeReconnectionState(p0: QBRTCSession?, p1: Int?, p2: QBRTCTypes.QBRTCReconnectionState?) {
468+
// empty
469+
}
470+
428471
override fun onCallAcceptByUser(session: QBRTCSession?, userId: Int?, map: MutableMap<String, String>?) {
429472
if (callService.isCurrentSession(session)) {
430473
callService.stopRingtone()
@@ -598,6 +641,10 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
598641
return callService.getVideoTrack(userId)
599642
}
600643

644+
override fun getState(userId: Int): QBRTCReconnectionState? {
645+
return callService.getState(userId)
646+
}
647+
601648
override fun onStopPreview() {
602649
callService.stopScreenSharing()
603650
addConversationFragment(isInComingCall)
@@ -652,7 +699,7 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
652699
}
653700

654701
interface OnChangeAudioDevice {
655-
fun audioDeviceChanged(newAudioDevice: AppRTCAudioManager.AudioDevice)
702+
fun audioDeviceChanged(newAudioDevice: QBAudioManager.AudioDevice)
656703
}
657704

658705
interface CallStateListener {

sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/SplashActivity.kt

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,32 @@
11
package com.quickblox.sample.videochat.kotlin.activities
22

3+
import android.Manifest
34
import android.content.Intent
5+
import android.content.pm.PackageManager
46
import android.net.Uri
57
import android.os.Build
68
import android.os.Bundle
79
import android.os.Handler
810
import android.provider.Settings
911
import android.util.Log
1012
import android.widget.TextView
13+
import androidx.annotation.RequiresApi
1114
import androidx.appcompat.app.AlertDialog
15+
import androidx.core.app.ActivityCompat
16+
import androidx.core.content.ContextCompat
1217
import com.quickblox.sample.videochat.kotlin.R
1318
import com.quickblox.sample.videochat.kotlin.services.LoginService
1419
import com.quickblox.sample.videochat.kotlin.utils.SharedPrefsHelper
1520
import com.quickblox.sample.videochat.kotlin.utils.isMiUi
1621
import com.quickblox.sample.videochat.kotlin.utils.longToast
1722

23+
1824
private const val SPLASH_DELAY = 1500
1925

2026
private const val OVERLAY_PERMISSION_CHECKED_KEY = "overlay_checked"
2127
private const val MI_OVERLAY_PERMISSION_CHECKED_KEY = "mi_overlay_checked"
2228
private const val ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 1764
29+
private const val NOTIFICATION_PERMISSION_REQUEST_CODE = 1010
2330

2431
class SplashActivity : BaseActivity() {
2532
private val TAG = SplashActivity::class.java.simpleName
@@ -31,11 +38,37 @@ class SplashActivity : BaseActivity() {
3138
fillVersion()
3239
supportActionBar?.hide()
3340

41+
checkNotificationPermission()
42+
3443
if (checkOverlayPermissions()) {
3544
runNextScreen()
3645
}
3746
}
3847

48+
private fun checkNotificationPermission() {
49+
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
50+
val isNotificationPermissionDenied = ContextCompat.checkSelfPermission(
51+
this,
52+
Manifest.permission.POST_NOTIFICATIONS
53+
) == PackageManager.PERMISSION_DENIED
54+
if (isNotificationPermissionDenied) {
55+
ActivityCompat.requestPermissions(
56+
this,
57+
arrayOf<String>(Manifest.permission.POST_NOTIFICATIONS),
58+
NOTIFICATION_PERMISSION_REQUEST_CODE
59+
)
60+
}
61+
}
62+
}
63+
64+
@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
65+
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
66+
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
67+
if (requestCode == NOTIFICATION_PERMISSION_REQUEST_CODE && grantResults.size > 0 && grantResults[0] != 0) {
68+
longToast(getString(R.string.permission_unavailable, Manifest.permission.POST_NOTIFICATIONS))
69+
}
70+
}
71+
3972
private fun runNextScreen() {
4073
if (SharedPrefsHelper.hasCurrentUser()) {
4174
LoginService.loginToChatAndInitRTCClient(this, SharedPrefsHelper.getCurrentUser())
Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
package com.quickblox.sample.videochat.kotlin.adapters
2+
3+
import android.content.Context
4+
import android.text.TextUtils
5+
import android.view.LayoutInflater
6+
import android.view.View
7+
import android.view.ViewGroup
8+
import android.widget.TextView
9+
import androidx.recyclerview.widget.RecyclerView
10+
import com.quickblox.sample.videochat.kotlin.R
11+
12+
class AudioCallAdapter(context: Context?, private var usersList: List<ReconnectingUserModel>) :
13+
RecyclerView.Adapter<AudioCallAdapter.ViewHolder>() {
14+
private val inflater: LayoutInflater
15+
16+
init {
17+
inflater = LayoutInflater.from(context)
18+
}
19+
20+
fun updateList(usersList: List<ReconnectingUserModel>) {
21+
this.usersList = usersList
22+
notifyDataSetChanged()
23+
}
24+
25+
fun getItemByUserId(userId: Int): ReconnectingUserModel? {
26+
for (item in usersList) {
27+
if (item.getUser().id == userId) {
28+
return item
29+
}
30+
}
31+
return null
32+
}
33+
34+
override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
35+
val view: View = inflater.inflate(R.layout.audio_call_item, parent, false)
36+
return ViewHolder(view)
37+
}
38+
39+
override fun onBindViewHolder(holder: ViewHolder, position: Int) {
40+
val user = usersList[position].getUser()
41+
val name: String
42+
name = if (TextUtils.isEmpty(user.fullName)) {
43+
user.login
44+
} else {
45+
user.fullName
46+
}
47+
holder.setName(name)
48+
if (!TextUtils.isEmpty(usersList[position].getReconnectingState())) {
49+
holder.setStatus(usersList[position].getReconnectingState())
50+
}
51+
}
52+
53+
override fun getItemCount(): Int {
54+
return usersList.size
55+
}
56+
57+
class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
58+
private val nameView: TextView
59+
private val statusView: TextView
60+
61+
init {
62+
nameView = itemView.findViewById<View>(R.id.name) as TextView
63+
statusView = itemView.findViewById<View>(R.id.status) as TextView
64+
}
65+
66+
fun setStatus(status: String?) {
67+
statusView.text = status
68+
}
69+
70+
fun setName(name: String?) {
71+
nameView.text = name
72+
}
73+
}
74+
}

0 commit comments

Comments
 (0)