Skip to content

Commit

Permalink
4.2.0-videochat-kotlin
Browse files Browse the repository at this point in the history
  • Loading branch information
vdovbnya-qb committed Sep 5, 2024
1 parent 8ca8994 commit df577da
Show file tree
Hide file tree
Showing 26 changed files with 816 additions and 239 deletions.
35 changes: 13 additions & 22 deletions sample-videochat-kotlin/app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,10 @@ buildscript {
google()
mavenCentral()
}
dependencies {
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31"
}
}

apply plugin: 'com.android.application'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'

androidExtensions {
experimental = true
}

repositories {
google()
Expand All @@ -26,20 +18,19 @@ repositories {

android {
def versionQACode = 1

compileSdkVersion 31
buildToolsVersion "31.0.0"
flavorDimensions "default"
namespace 'com.quickblox.sample.videochat.kotlin'

defaultConfig {
compileSdk 34
applicationId "com.quickblox.sample.videochat.kotlin"
minSdkVersion 21
targetSdkVersion 31
versionCode 410000
versionName '4.1.0'
targetSdkVersion 34
versionCode 420000
versionName '4.2.0'
multiDexEnabled true
}

flavorDimensions "default"
productFlavors {
dev {
dimension "default"
Expand Down Expand Up @@ -81,19 +72,19 @@ android {
}
}

lintOptions {
abortOnError false
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
sourceCompatibility JavaVersion.VERSION_17
targetCompatibility JavaVersion.VERSION_17
}
lint {
abortOnError false
}
}

dependencies {
implementation 'com.quickblox:quickblox-android-sdk-videochat-webrtc:3.10.1'
implementation 'com.quickblox:quickblox-android-sdk-messages:3.10.1'
implementation 'com.quickblox:quickblox-android-sdk-videochat-webrtc:4.1.3'
implementation 'com.quickblox:quickblox-android-sdk-messages:4.1.3'
implementation 'com.google.firebase:firebase-core:21.1.0'
implementation 'com.google.android.material:material:1.6.1'
implementation 'com.github.johnkil.android-robototextview:robototextview:4.0.0'
Expand Down
5 changes: 4 additions & 1 deletion sample-videochat-kotlin/app/proguard-rules.pro
Original file line number Diff line number Diff line change
Expand Up @@ -44,4 +44,7 @@
-keep class com.google.android.gms.** { *; }

#json
-keep class org.json.** { *; }
-keep class org.json.** { *; }
-keep class com.google.gson.reflect.TypeToken
-keep class * extends com.google.gson.reflect.TypeToken
-keep public class * implements java.lang.reflect.Type
15 changes: 11 additions & 4 deletions sample-videochat-kotlin/app/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.quickblox.sample.videochat.kotlin">
xmlns:tools="http://schemas.android.com/tools">

<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.MODIFY_AUDIO_SETTINGS" />
Expand All @@ -13,6 +12,13 @@
<uses-permission android:name="android.permission.FOREGROUND_SERVICE" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.ACTION_MANAGE_OVERLAY_PERMISSION" />
<uses-permission android:name="android.permission.POST_NOTIFICATIONS" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_CAMERA" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MICROPHONE" />
<uses-permission android:name="android.permission.FOREGROUND_SERVICE_MEDIA_PROJECTION" />

<uses-feature android:name="android.hardware.camera" />
<uses-feature android:name="android.hardware.camera.autofocus" />

<application
android:name=".App"
Expand Down Expand Up @@ -60,8 +66,9 @@
<service android:name=".services.LoginService" />

<service
android:foregroundServiceType="mediaProjection"
android:name=".services.CallService" />
android:name=".services.CallService"
android:exported="false"
android:foregroundServiceType="camera|microphone|mediaProjection" />

<service
android:name=".services.fcm.PushListenerService"
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ import android.annotation.TargetApi
import android.app.Activity
import android.app.KeyguardManager
import android.content.*
import android.media.projection.MediaProjectionManager
import android.net.Uri
import android.os.*
import android.preference.PreferenceManager
import android.provider.Settings
import android.util.Log
import android.view.View
Expand All @@ -24,6 +24,10 @@ import com.quickblox.sample.videochat.kotlin.utils.*
import com.quickblox.users.QBUsers
import com.quickblox.users.model.QBUser
import com.quickblox.videochat.webrtc.*
import com.quickblox.videochat.webrtc.BaseSession.QBRTCSessionState.QB_RTC_SESSION_NEW
import com.quickblox.videochat.webrtc.BaseSession.QBRTCSessionState.QB_RTC_SESSION_PENDING
import com.quickblox.videochat.webrtc.QBRTCTypes.QBRTCReconnectionState
import com.quickblox.videochat.webrtc.audio.QBAudioManager
import com.quickblox.videochat.webrtc.callbacks.QBRTCClientSessionCallbacks
import com.quickblox.videochat.webrtc.callbacks.QBRTCClientVideoTracksCallbacks
import com.quickblox.videochat.webrtc.callbacks.QBRTCSessionEventsCallback
Expand All @@ -33,10 +37,11 @@ import org.jivesoftware.smack.AbstractConnectionListener
import org.jivesoftware.smack.ConnectionListener
import org.webrtc.CameraVideoCapturer
import java.util.*
import kotlin.collections.ArrayList


private const val INCOME_CALL_FRAGMENT = "income_call_fragment"
private const val REQUEST_PERMISSION_SETTING = 545
private const val REQUEST_SHARING_MEDIA_PROJECTION = 1060

class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSessionStateCallback<QBRTCSession>,
QBRTCClientSessionCallbacks, ConversationFragmentCallback, ScreenShareFragment.OnSharingEvents {
Expand Down Expand Up @@ -83,6 +88,32 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
)
}
connectionView = View.inflate(this, R.layout.connection_popup, null) as LinearLayout

val isVideoCall = isVideoSession()
if (Build.VERSION.SDK_INT > Build.VERSION_CODES.TIRAMISU && isVideoCall && isNewSession()) {
requestSharingPermissions(this)
return
}

CallService.start(this)
}

private fun isNewSession(): Boolean {
val currentSession: QBRTCSession? = WebRtcSessionManager.getCurrentSession()
return currentSession != null && currentSession.state == QB_RTC_SESSION_NEW || currentSession?.state == QB_RTC_SESSION_PENDING
}

private fun isVideoSession(): Boolean {
val currentSession: QBRTCSession? = WebRtcSessionManager.getCurrentSession()
return currentSession != null && currentSession.conferenceType == QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO
}

private fun requestSharingPermissions(context: Activity) {
val mMediaProjectionManager = context.getSystemService(MEDIA_PROJECTION_SERVICE) as MediaProjectionManager
context.startActivityForResult(
mMediaProjectionManager.createScreenCaptureIntent(),
REQUEST_SHARING_MEDIA_PROJECTION
)
}

private fun initScreen() {
Expand Down Expand Up @@ -158,6 +189,14 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
Log.i(TAG, "Starting screen capture")
}
}

if (requestCode == REQUEST_SHARING_MEDIA_PROJECTION) {
if (resultCode == Activity.RESULT_OK) {
CallService.start(this)
} else {
finish()
}
}
}

private fun startScreenSharing(data: Intent?) {
Expand Down Expand Up @@ -323,7 +362,7 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
}

override fun onBackPressed() {
// to prevent returning from Call Fragment
super.onBackPressed()
}

private fun addIncomeCallFragment() {
Expand Down Expand Up @@ -425,6 +464,10 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
}
}

override fun onChangeReconnectionState(p0: QBRTCSession?, p1: Int?, p2: QBRTCTypes.QBRTCReconnectionState?) {
// empty
}

override fun onCallAcceptByUser(session: QBRTCSession?, userId: Int?, map: MutableMap<String, String>?) {
if (callService.isCurrentSession(session)) {
callService.stopRingtone()
Expand Down Expand Up @@ -598,6 +641,10 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
return callService.getVideoTrack(userId)
}

override fun getState(userId: Int): QBRTCReconnectionState? {
return callService.getState(userId)
}

override fun onStopPreview() {
callService.stopScreenSharing()
addConversationFragment(isInComingCall)
Expand Down Expand Up @@ -652,7 +699,7 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe
}

interface OnChangeAudioDevice {
fun audioDeviceChanged(newAudioDevice: AppRTCAudioManager.AudioDevice)
fun audioDeviceChanged(newAudioDevice: QBAudioManager.AudioDevice)
}

interface CallStateListener {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,25 +1,32 @@
package com.quickblox.sample.videochat.kotlin.activities

import android.Manifest
import android.content.Intent
import android.content.pm.PackageManager
import android.net.Uri
import android.os.Build
import android.os.Bundle
import android.os.Handler
import android.provider.Settings
import android.util.Log
import android.widget.TextView
import androidx.annotation.RequiresApi
import androidx.appcompat.app.AlertDialog
import androidx.core.app.ActivityCompat
import androidx.core.content.ContextCompat
import com.quickblox.sample.videochat.kotlin.R
import com.quickblox.sample.videochat.kotlin.services.LoginService
import com.quickblox.sample.videochat.kotlin.utils.SharedPrefsHelper
import com.quickblox.sample.videochat.kotlin.utils.isMiUi
import com.quickblox.sample.videochat.kotlin.utils.longToast


private const val SPLASH_DELAY = 1500

private const val OVERLAY_PERMISSION_CHECKED_KEY = "overlay_checked"
private const val MI_OVERLAY_PERMISSION_CHECKED_KEY = "mi_overlay_checked"
private const val ACTION_MANAGE_OVERLAY_PERMISSION_REQUEST_CODE = 1764
private const val NOTIFICATION_PERMISSION_REQUEST_CODE = 1010

class SplashActivity : BaseActivity() {
private val TAG = SplashActivity::class.java.simpleName
Expand All @@ -31,11 +38,37 @@ class SplashActivity : BaseActivity() {
fillVersion()
supportActionBar?.hide()

checkNotificationPermission()

if (checkOverlayPermissions()) {
runNextScreen()
}
}

private fun checkNotificationPermission() {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.TIRAMISU) {
val isNotificationPermissionDenied = ContextCompat.checkSelfPermission(
this,
Manifest.permission.POST_NOTIFICATIONS
) == PackageManager.PERMISSION_DENIED
if (isNotificationPermissionDenied) {
ActivityCompat.requestPermissions(
this,
arrayOf<String>(Manifest.permission.POST_NOTIFICATIONS),
NOTIFICATION_PERMISSION_REQUEST_CODE
)
}
}
}

@RequiresApi(api = Build.VERSION_CODES.TIRAMISU)
override fun onRequestPermissionsResult(requestCode: Int, permissions: Array<String?>, grantResults: IntArray) {
super.onRequestPermissionsResult(requestCode, permissions, grantResults)
if (requestCode == NOTIFICATION_PERMISSION_REQUEST_CODE && grantResults.size > 0 && grantResults[0] != 0) {
longToast(getString(R.string.permission_unavailable, Manifest.permission.POST_NOTIFICATIONS))
}
}

private fun runNextScreen() {
if (SharedPrefsHelper.hasCurrentUser()) {
LoginService.loginToChatAndInitRTCClient(this, SharedPrefsHelper.getCurrentUser())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
package com.quickblox.sample.videochat.kotlin.adapters

import android.content.Context
import android.text.TextUtils
import android.view.LayoutInflater
import android.view.View
import android.view.ViewGroup
import android.widget.TextView
import androidx.recyclerview.widget.RecyclerView
import com.quickblox.sample.videochat.kotlin.R

class AudioCallAdapter(context: Context?, private var usersList: List<ReconnectingUserModel>) :
RecyclerView.Adapter<AudioCallAdapter.ViewHolder>() {
private val inflater: LayoutInflater

init {
inflater = LayoutInflater.from(context)
}

fun updateList(usersList: List<ReconnectingUserModel>) {
this.usersList = usersList
notifyDataSetChanged()
}

fun getItemByUserId(userId: Int): ReconnectingUserModel? {
for (item in usersList) {
if (item.getUser().id == userId) {
return item
}
}
return null
}

override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ViewHolder {
val view: View = inflater.inflate(R.layout.audio_call_item, parent, false)
return ViewHolder(view)
}

override fun onBindViewHolder(holder: ViewHolder, position: Int) {
val user = usersList[position].getUser()
val name: String
name = if (TextUtils.isEmpty(user.fullName)) {
user.login
} else {
user.fullName
}
holder.setName(name)
if (!TextUtils.isEmpty(usersList[position].getReconnectingState())) {
holder.setStatus(usersList[position].getReconnectingState())
}
}

override fun getItemCount(): Int {
return usersList.size
}

class ViewHolder(itemView: View) : RecyclerView.ViewHolder(itemView) {
private val nameView: TextView
private val statusView: TextView

init {
nameView = itemView.findViewById<View>(R.id.name) as TextView
statusView = itemView.findViewById<View>(R.id.status) as TextView
}

fun setStatus(status: String?) {
statusView.text = status
}

fun setName(name: String?) {
nameView.text = name
}
}
}
Loading

0 comments on commit df577da

Please sign in to comment.