diff --git a/sample-videochat-kotlin/app/build.gradle b/sample-videochat-kotlin/app/build.gradle index 6d6d3bdf8..3d08a2c63 100644 --- a/sample-videochat-kotlin/app/build.gradle +++ b/sample-videochat-kotlin/app/build.gradle @@ -1,19 +1,16 @@ buildscript { repositories { google() - jcenter() - maven { url 'https://maven.fabric.io/public' } + mavenCentral() } dependencies { classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinGradlePluginVersion" - classpath "io.fabric.tools:gradle:$fabricToolsVersion" } } apply plugin: 'com.android.application' apply plugin: 'kotlin-android' apply plugin: 'kotlin-android-extensions' -apply plugin: 'io.fabric' androidExtensions { experimental = true @@ -21,27 +18,25 @@ androidExtensions { repositories { google() - jcenter() + mavenCentral() maven { url "https://github.com/QuickBlox/quickblox-android-sdk-releases/raw/master/" } - maven { url 'https://maven.fabric.io/public' } - flatDir { dirs 'libs' } } android { - def versionQACode = 3 + def versionQACode = 1 - compileSdkVersion 28 - buildToolsVersion "28.0.3" + compileSdkVersion 31 + buildToolsVersion "31.0.0" flavorDimensions dimensionDefault defaultConfig { applicationId "com.quickblox.sample.videochat.kotlin" - minSdkVersion 16 - targetSdkVersion 28 - versionCode 404000 - versionName '4.0.4' + minSdkVersion 21 + targetSdkVersion 31 + versionCode 405000 + versionName '4.0.5' multiDexEnabled true } @@ -65,12 +60,14 @@ android { minifyEnabled false shrinkResources false proguardFile 'proguard-rules.pro' - zipAlignEnabled false resValue "string", "versionName", "QuickBlox Video Chat Kotlin\nBuild version " + defaultConfig.getVersionName() } release { signingConfig signingConfigs.debug + minifyEnabled true + shrinkResources true + proguardFile 'proguard-rules.pro' resValue "string", "versionName", "QuickBlox Video Chat Kotlin\nBuild version " + defaultConfig.getVersionName() } } @@ -95,21 +92,14 @@ android { } dependencies { - implementation "com.quickblox:quickblox-android-sdk-videochat-webrtc:$qbSdkVersion" implementation "com.quickblox:quickblox-android-sdk-messages:$qbSdkVersion" - implementation "com.google.firebase:firebase-core:$firebaseCoreVersion" - implementation "com.github.bumptech.glide:glide:$glideVersion" implementation "com.google.android.material:material:$materialVersion" implementation "com.github.johnkil.android-robototextview:robototextview:$robotoTextViewVersion" - implementation "androidx.fragment:fragment:$fragmentAndroidXVersion" - implementation "androidx.lifecycle:lifecycle-viewmodel:$lifecycleViewmodelAndroidXVersion" + implementation "androidx.fragment:fragment-ktx:$fragmentAndroidXVersion" + implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleViewmodelAndroidXVersion" implementation "androidx.core:core-ktx:$coreKtxVersion" - implementation("com.crashlytics.sdk.android:crashlytics:$crashlyticsVersion@aar") { - transitive = true - } - compile "org.jetbrains.kotlin:kotlin-reflect:$kotlinReflectVersion" } apply from: "../artifacts.gradle" diff --git a/sample-videochat-kotlin/app/google-services.json b/sample-videochat-kotlin/app/google-services.json index 542e3ac38..97ec23e8e 100644 --- a/sample-videochat-kotlin/app/google-services.json +++ b/sample-videochat-kotlin/app/google-services.json @@ -1,154 +1,29 @@ { "project_info": { - "project_number": "247738611464", - "firebase_url": "https://qb-prod-samples.firebaseio.com", - "project_id": "qb-prod-samples", - "storage_bucket": "qb-prod-samples.appspot.com" + "project_number": "Put here your value", + "firebase_url": "https://qb-samples.firebaseio.com", + "project_id": "qb-samples", + "storage_bucket": "qb-samples.appspot.com" }, "client": [ { "client_info": { - "mobilesdk_app_id": "1:247738611464:android:beb270faa2c3a789", + "mobilesdk_app_id": "Put here your value", "android_client_info": { - "package_name": "com.quickblox.sample.chat.java" - } - }, - "oauth_client": [], - "api_key": [ - { - "current_key": "AIzaSyBFXAfVr6kkFJdDNOm8U-c7iju0qIUkc_A" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "247738611464-v2nvd29bmqum7niosnfuh28oq3beh9f6.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:247738611464:android:1cdc72e9ffd29448", - "android_client_info": { - "package_name": "com.quickblox.sample.chat.kotlin" - } - }, - "oauth_client": [], - "api_key": [ - { - "current_key": "AIzaSyBFXAfVr6kkFJdDNOm8U-c7iju0qIUkc_A" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "247738611464-v2nvd29bmqum7niosnfuh28oq3beh9f6.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:247738611464:android:06cb0de4c719ad84", - "android_client_info": { - "package_name": "com.quickblox.sample.pushnotifications.java" - } - }, - "oauth_client": [], - "api_key": [ - { - "current_key": "AIzaSyBFXAfVr6kkFJdDNOm8U-c7iju0qIUkc_A" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "247738611464-v2nvd29bmqum7niosnfuh28oq3beh9f6.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:247738611464:android:c2749661061637f0", - "android_client_info": { - "package_name": "com.quickblox.sample.pushnotifications.kotlin" - } - }, - "oauth_client": [], - "api_key": [ - { - "current_key": "AIzaSyBFXAfVr6kkFJdDNOm8U-c7iju0qIUkc_A" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "247738611464-v2nvd29bmqum7niosnfuh28oq3beh9f6.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:247738611464:android:99e5b55a490c901c", - "android_client_info": { - "package_name": "com.quickblox.sample.videochat.java" + "package_name": "com.quickblox.sample.videochat.kotlin" } }, - "oauth_client": [], - "api_key": [ + "oauth_client": [ { - "current_key": "AIzaSyBFXAfVr6kkFJdDNOm8U-c7iju0qIUkc_A" + "client_id": "Put here your value", + "client_type": 3 } ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "247738611464-v2nvd29bmqum7niosnfuh28oq3beh9f6.apps.googleusercontent.com", - "client_type": 3 - } - ] - } - } - }, - { - "client_info": { - "mobilesdk_app_id": "1:247738611464:android:ac22e0d1b3a3e86b", - "android_client_info": { - "package_name": "com.quickblox.sample.videochat.kotlin" - } - }, - "oauth_client": [], "api_key": [ { - "current_key": "AIzaSyBFXAfVr6kkFJdDNOm8U-c7iju0qIUkc_A" - } - ], - "services": { - "appinvite_service": { - "other_platform_oauth_client": [ - { - "client_id": "247738611464-v2nvd29bmqum7niosnfuh28oq3beh9f6.apps.googleusercontent.com", - "client_type": 3 - } - ] + "current_key": "Put here your value" } - } + ] } ], "configuration_version": "1" diff --git a/sample-videochat-kotlin/app/proguard-rules.pro b/sample-videochat-kotlin/app/proguard-rules.pro index f1b424510..7c835c9ff 100644 --- a/sample-videochat-kotlin/app/proguard-rules.pro +++ b/sample-videochat-kotlin/app/proguard-rules.pro @@ -1,6 +1,8 @@ # Add project specific ProGuard rules here. -# You can control the set of applied configuration files using the -# proguardFiles setting in build.gradle. +# By default, the flags in this file are appended to flags specified +# in /home/tereha/Android/sdk/tools/proguard/proguard-android.txt +# You can edit the include path and order by changing the proguardFiles +# directive in build.gradle. # # For more details, see # http://developer.android.com/guide/developing/tools/proguard.html @@ -11,11 +13,32 @@ #-keepclassmembers class fqcn.of.javascript.interface.for.webview { # public *; #} +#-dontusemixedcaseclassnames +#-dontskipnonpubliclibraryclasses +#-verbose +# + +##---------------Begin: proguard configuration for Gson ---------- +# Gson uses generic type information stored in a class file when working with fields. Proguard +# removes such information by default, so configure it to keep all of it. +-keepattributes EnclosingMethod +-keepattributes InnerClasses +-keepattributes Signature +-keepattributes Exceptions + +# For using GSON @Expose annotation +-keepattributes *Annotation* + +#quickblox sdk +-keep class com.quickblox.** { *; } + +#smack xmpp library +-keep class org.jxmpp.** { *; } +-keep class org.jivesoftware.** { *; } +-dontwarn org.jivesoftware.** -# Uncomment this to preserve the line number information for -# debugging stack traces. -#-keepattributes SourceFile,LineNumberTable +#webrtc +-keep class org.webrtc.** { *; } -# If you keep the line number information, uncomment this to -# hide the original source file name. -#-renamesourcefileattribute SourceFile +#google gms +-keep class com.google.android.gms.** { *; } \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/AndroidManifest.xml b/sample-videochat-kotlin/app/src/main/AndroidManifest.xml index b271a0c97..1f9e4169f 100644 --- a/sample-videochat-kotlin/app/src/main/AndroidManifest.xml +++ b/sample-videochat-kotlin/app/src/main/AndroidManifest.xml @@ -3,7 +3,6 @@ xmlns:tools="http://schemas.android.com/tools" package="com.quickblox.sample.videochat.kotlin"> - @@ -12,7 +11,7 @@ - + @@ -64,15 +64,21 @@ - + - + - + diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/App.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/App.kt index 3de0ac38d..81bd0a966 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/App.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/App.kt @@ -1,10 +1,8 @@ package com.quickblox.sample.videochat.kotlin import android.app.Application -import com.crashlytics.android.Crashlytics import com.quickblox.auth.session.QBSettings import com.quickblox.sample.videochat.kotlin.db.DbHelper -import io.fabric.sdk.android.Fabric //User default credentials const val DEFAULT_USER_PASSWORD = "quickblox" @@ -30,17 +28,10 @@ class App : Application() { super.onCreate() instance = this dbHelper = DbHelper(this) - initFabric() checkCredentials() initCredentials() } - private fun initFabric() { - if (!BuildConfig.DEBUG) { - Fabric.with(this, Crashlytics()) - } - } - private fun checkCredentials() { if (APPLICATION_ID.isEmpty() || AUTH_KEY.isEmpty() || AUTH_SECRET.isEmpty() || ACCOUNT_KEY.isEmpty()) { throw AssertionError(getString(R.string.error_qb_credentials_empty)) diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/AppInfoActivity.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/AppInfoActivity.kt index 98f403eef..626995549 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/AppInfoActivity.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/AppInfoActivity.kt @@ -75,8 +75,8 @@ class AppInfoActivity : AppCompatActivity() { } } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { android.R.id.home -> { finish() return true diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/BaseActivity.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/BaseActivity.kt index e3f42bc5d..a6a88b1a3 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/BaseActivity.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/BaseActivity.kt @@ -36,7 +36,7 @@ abstract class BaseActivity : AppCompatActivity() { } internal fun hideProgressDialog() { - if (progressDialog != null && progressDialog!!.isShowing) { + if (progressDialog != null && progressDialog?.isShowing == true) { progressDialog!!.dismiss() } } diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/CallActivity.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/CallActivity.kt index 292f66abf..54197eaae 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/CallActivity.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/CallActivity.kt @@ -19,6 +19,7 @@ import com.quickblox.sample.videochat.kotlin.db.QbUsersDbManager import com.quickblox.sample.videochat.kotlin.fragments.* import com.quickblox.sample.videochat.kotlin.services.CallService import com.quickblox.sample.videochat.kotlin.services.LoginService +import com.quickblox.sample.videochat.kotlin.services.ONE_OPPONENT import com.quickblox.sample.videochat.kotlin.util.loadUsersByIds import com.quickblox.sample.videochat.kotlin.utils.* import com.quickblox.users.model.QBUser @@ -31,7 +32,7 @@ import com.quickblox.videochat.webrtc.view.QBRTCVideoTrack import org.jivesoftware.smack.AbstractConnectionListener import org.jivesoftware.smack.ConnectionListener import org.webrtc.CameraVideoCapturer -import java.util.HashMap +import java.util.* import kotlin.collections.ArrayList private const val INCOME_CALL_FRAGMENT = "income_call_fragment" @@ -265,17 +266,9 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } private fun initIncomingCallTask() { - showIncomingCallWindowTaskHandler = Handler(Looper.myLooper()) + showIncomingCallWindowTaskHandler = Handler(Looper.getMainLooper()) showIncomingCallWindowTask = Runnable { if (callService.currentSessionExist()) { - /*val currentSessionState = callService.getCurrentSessionState() - if (BaseSession.QBRTCSessionState.QB_RTC_SESSION_NEW == currentSessionState) { - callService.rejectCurrentSession(HashMap()) - } else { - callService.stopRingtone() - hangUpCurrentSession() - }*/ - // This is a fix to prevent call stop in case calling to user with more then one device logged in. longToast("Call was stopped by UserNoActions timer") callService.clearCallState() callService.clearButtonsState() @@ -316,14 +309,14 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } override fun finish() { - //Fix bug when user returns to call from service and the backstack doesn't have any screens + // fix bug when user returns to call from service and the backstack doesn't have any screens OpponentsActivity.start(this) CallService.stop(this) super.finish() } override fun onBackPressed() { - // To prevent returning from Call Fragment + // to prevent returning from Call Fragment } private fun addIncomeCallFragment() { @@ -366,8 +359,6 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } } - ////////////////////////////// ConnectionListener ////////////////////////////// - private inner class ConnectionListenerImpl : AbstractConnectionListener() { override fun connectionClosedOnError(e: Exception?) { showNotificationPopUp(R.string.connection_was_lost, true) @@ -378,10 +369,8 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } } - ////////////////////////////// QBRTCSessionStateCallbackListener /////////////////////////// - override fun onDisconnectedFromUser(session: QBRTCSession?, userId: Int?) { - + // empty } override fun onConnectedToUser(session: QBRTCSession?, userId: Int?) { @@ -393,15 +382,13 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } override fun onConnectionClosedForUser(session: QBRTCSession?, userId: Int?) { - + // empty } override fun onStateChanged(session: QBRTCSession?, sessiontState: BaseSession.QBRTCSessionState?) { - + // empty } - ////////////////////////////// QBRTCClientSessionCallbacks ////////////////////////////// - override fun onUserNotAnswer(session: QBRTCSession?, userId: Int?) { if (callService.isCurrentSession(session)) { callService.stopRingtone() @@ -417,9 +404,9 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe override fun onReceiveHangUpFromUser(session: QBRTCSession?, userId: Int?, map: MutableMap?) { if (callService.isCurrentSession(session)) { - if (userId == session?.callerID) { + val numberOpponents = session?.opponents?.size + if (numberOpponents == ONE_OPPONENT) { hangUpCurrentSession() - Log.d(TAG, "initiator hung up the call") } val participant = QbUsersDbManager.getUserById(userId) val participantName = if (participant != null) participant.fullName else userId.toString() @@ -434,7 +421,7 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } override fun onReceiveNewSession(session: QBRTCSession?) { - + // empty } override fun onUserNoActions(session: QBRTCSession?, userId: Int?) { @@ -454,8 +441,6 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } } - ////////////////////////////// IncomeCallFragmentCallbackListener //////////////////////////// - override fun onAcceptCurrentSession() { if (callService.currentSessionExist()) { addConversationFragment(true) @@ -468,8 +453,6 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe callService.rejectCurrentSession(HashMap()) } - ////////////////////////////// ConversationFragmentCallback //////////////////////////// - override fun addConnectionListener(connectionCallback: ConnectionListener?) { callService.addConnectionListener(connectionCallback) } @@ -533,9 +516,11 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } override fun addOnChangeAudioDeviceListener(onChangeDynamicCallback: OnChangeAudioDevice?) { + // empty } override fun removeOnChangeAudioDeviceListener(onChangeDynamicCallback: OnChangeAudioDevice?) { + // empty } override fun acceptCall(userInfo: Map) { @@ -621,14 +606,14 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe private inner class CallServiceConnection : ServiceConnection { override fun onServiceDisconnected(name: ComponentName?) { - + // empty } override fun onServiceConnected(name: ComponentName?, service: IBinder?) { val binder = service as CallService.CallServiceBinder callService = binder.getService() if (callService.currentSessionExist()) { - //we have already currentSession == null, so it's no reason to do further initialization + // we have already currentSession == null, so it's no reason to do further initialization if (QBChatService.getInstance().isLoggedIn) { initScreen() } else { diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/LoginActivity.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/LoginActivity.kt index ba59192f0..15f9d8a4b 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/LoginActivity.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/LoginActivity.kt @@ -29,7 +29,8 @@ class LoginActivity : BaseActivity() { private lateinit var user: QBUser companion object { - fun start(context: Context) = context.startActivity(Intent(context, LoginActivity::class.java)) + fun start(context: Context) = + context.startActivity(Intent(context, LoginActivity::class.java)) } override fun onCreate(savedInstanceState: Bundle?) { @@ -125,7 +126,7 @@ class LoginActivity : BaseActivity() { isLoginSuccess = it.getBooleanExtra(EXTRA_LOGIN_RESULT, false) } - var errorMessage = getString(R.string.unknown_error) + var errorMessage: String? = getString(R.string.unknown_error) data?.let { errorMessage = it.getStringExtra(EXTRA_LOGIN_ERROR_MESSAGE) } @@ -181,7 +182,8 @@ class LoginActivity : BaseActivity() { LoginService.start(this, qbUser, pendingIntent) } - private inner class LoginEditTextWatcher internal constructor(private val editText: EditText) : TextWatcher { + private inner class LoginEditTextWatcher internal constructor(private val editText: EditText) : + TextWatcher { override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/PermissionsActivity.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/PermissionsActivity.kt index ebd46d0ae..3c9c5cdde 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/PermissionsActivity.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/PermissionsActivity.kt @@ -34,9 +34,6 @@ class PermissionsActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - if (intent == null || !intent.hasExtra(EXTRA_PERMISSIONS)) { - throw RuntimeException("This Activity needs to be launched using the static startActivityForResult() method.") - } setContentView(R.layout.activity_permissions) supportActionBar?.hide() requiresCheck = true @@ -44,6 +41,10 @@ class PermissionsActivity : BaseActivity() { override fun onResume() { super.onResume() + if (intent == null || !intent.hasExtra(EXTRA_PERMISSIONS)) { + throw RuntimeException("This Activity needs to be launched using the static startActivityForResult() method.") + } + if (requiresCheck) { checkPermissions() } else { @@ -52,8 +53,9 @@ class PermissionsActivity : BaseActivity() { } private fun checkPermissions() { - val permissions = getPermissions() - val checkOnlyAudio = getCheckOnlyAudio() + val permissions = intent.getStringArrayExtra(EXTRA_PERMISSIONS)!! + + val checkOnlyAudio = intent.getBooleanExtra(CHECK_ONLY_AUDIO, false) if (checkOnlyAudio) { checkPermissionAudio(permissions[1]) @@ -78,14 +80,6 @@ class PermissionsActivity : BaseActivity() { } } - private fun getPermissions(): Array { - return intent.getStringArrayExtra(EXTRA_PERMISSIONS) - } - - private fun getCheckOnlyAudio(): Boolean { - return intent.getBooleanExtra(CHECK_ONLY_AUDIO, false) - } - private fun requestPermissions(vararg permissions: String) { ActivityCompat.requestPermissions(this, permissions, PERMISSION_CODE) } @@ -96,6 +90,7 @@ class PermissionsActivity : BaseActivity() { } override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { + super.onRequestPermissionsResult(requestCode, permissions, grantResults) if (requestCode == PERMISSION_CODE && hasAllPermissionsGranted(grantResults)) { requiresCheck = true allPermissionsGranted() diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/BaseConversationFragment.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/BaseConversationFragment.kt index c3b398b5a..9707db697 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/BaseConversationFragment.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/BaseConversationFragment.kt @@ -1,6 +1,7 @@ package com.quickblox.sample.videochat.kotlin.fragments import android.app.Activity +import android.content.Context import android.os.Bundle import android.util.Log import android.view.LayoutInflater @@ -50,8 +51,8 @@ abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.Cu } } - override fun onAttach(activity: Activity?) { - super.onAttach(activity) + override fun onAttach(context: Context) { + super.onAttach(context) try { conversationFragmentCallback = context as ConversationFragmentCallback } catch (e: ClassCastException) { @@ -191,7 +192,6 @@ abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.Cu } override fun onCallStopped() { - CallService.stop(activity as Activity) isStarted = false clearButtonsState() actionButtonsEnabled(false) diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/IncomeCallFragment.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/IncomeCallFragment.kt index 707f6ec36..e12090bc9 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/IncomeCallFragment.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/IncomeCallFragment.kt @@ -60,8 +60,8 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { private lateinit var incomeCallFragmentCallbackListener: IncomeCallFragmentCallbackListener private var currentSession: QBRTCSession? = null - override fun onAttach(activity: Activity?) { - super.onAttach(activity) + override fun onAttach(context: Context) { + super.onAttach(context) try { incomeCallFragmentCallbackListener = activity as IncomeCallFragmentCallbackListener } catch (e: ClassCastException) { diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/PreviewFragment.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/PreviewFragment.kt index 14c31b893..66d56f8ff 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/PreviewFragment.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/PreviewFragment.kt @@ -6,10 +6,8 @@ import android.view.View import android.view.ViewGroup import android.widget.ImageView import androidx.fragment.app.Fragment -import com.bumptech.glide.Glide -import com.bumptech.glide.load.engine.DiskCacheStrategy import com.quickblox.sample.videochat.kotlin.R -import com.quickblox.sample.videochat.kotlin.utils.getDimen +import com.quickblox.sample.videochat.kotlin.utils.getDrawable private const val PREVIEW_IMAGE = "preview_image" @@ -27,11 +25,13 @@ class PreviewFragment : Fragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_item_screen_share, container, false) - Glide.with(activity) - .load(arguments?.getInt(PREVIEW_IMAGE)) - .diskCacheStrategy(DiskCacheStrategy.ALL) - .override(getDimen(R.dimen.pager_image_width), getDimen(R.dimen.pager_image_height)) - .into(view.findViewById(R.id.image_preview) as ImageView) + + val ivPreview = view.findViewById(R.id.image_preview) + val imageDrawable = arguments?.getInt(PREVIEW_IMAGE) + + imageDrawable?.let { + ivPreview.setImageDrawable(getDrawable(it)) + } return view } } \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/ScreenShareFragment.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/ScreenShareFragment.kt index b8788e955..dfa07de19 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/ScreenShareFragment.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/ScreenShareFragment.kt @@ -48,13 +48,13 @@ class ScreenShareFragment : BaseToolBarFragment() { (activity as CallActivity).addCurrentCallStateListener(currentCallStateCallback!!) } - override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { - inflater?.inflate(R.menu.screen_share_fragment, menu) + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.screen_share_fragment, menu) super.onCreateOptionsMenu(menu, inflater) } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { R.id.stop_screen_share -> { Log.d(TAG, "stop_screen_share") onSharingEvents?.onStopPreview() @@ -64,7 +64,7 @@ class ScreenShareFragment : BaseToolBarFragment() { } } - override fun onAttach(context: Context?) { + override fun onAttach(context: Context) { super.onAttach(context) try { onSharingEvents = context as OnSharingEvents? diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/VideoConversationFragment.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/VideoConversationFragment.kt index b5747142a..f5514ac0b 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/VideoConversationFragment.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/VideoConversationFragment.kt @@ -1,6 +1,5 @@ package com.quickblox.sample.videochat.kotlin.fragments -import android.app.Activity import android.content.Context import android.graphics.Color import android.graphics.Rect @@ -18,8 +17,9 @@ import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.quickblox.sample.videochat.kotlin.R +import com.quickblox.sample.videochat.kotlin.activities.CallActivity import com.quickblox.sample.videochat.kotlin.adapters.OpponentsFromCallAdapter -import com.quickblox.sample.videochat.kotlin.services.CallService +import com.quickblox.sample.videochat.kotlin.db.QbUsersDbManager import com.quickblox.sample.videochat.kotlin.utils.SharedPrefsHelper import com.quickblox.sample.videochat.kotlin.utils.shortToast import com.quickblox.users.model.QBUser @@ -50,13 +50,12 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, OpponentsFromCallAdapter.OnAdapterEventListener { private val TAG = VideoConversationFragment::class.java.simpleName - //Views private lateinit var cameraToggle: ToggleButton private var parentView: View? = null private lateinit var actionVideoButtonsLayout: LinearLayout private lateinit var connectionStatusLocal: TextView private lateinit var recyclerView: RecyclerView - private lateinit var localVideoView: QBRTCSurfaceView + private var localVideoView: QBRTCSurfaceView? = null private var remoteFullScreenVideoView: QBRTCSurfaceView? = null private lateinit var opponentViewHolders: SparseArray @@ -74,90 +73,49 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, private var isCurrentCameraFront: Boolean = false private var isLocalVideoFullScreen: Boolean = false - override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { - parentView = super.onCreateView(inflater, container, savedInstanceState) - return parentView - } - - override fun configureOutgoingScreen() { - val context = activity!! - outgoingOpponentsRelativeLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.grey_transparent_50)) - allOpponentsTextView.setTextColor(ContextCompat.getColor(context, R.color.white)) - ringingTextView.setTextColor(ContextCompat.getColor(context, R.color.white)) - } - - override fun configureActionBar() { - actionBar.setDisplayShowTitleEnabled(false) - } - - override fun configureToolbar() { - val context = activity!! - toolbar.visibility = View.VISIBLE - toolbar.setBackgroundColor(ContextCompat.getColor(context, R.color.black_transparent_50)) - toolbar.setTitleTextColor(ContextCompat.getColor(context, R.color.white)) - toolbar.setSubtitleTextColor(ContextCompat.getColor(context, R.color.white)) - } - override fun getFragmentLayout(): Int { return R.layout.fragment_video_conversation } - override fun initFields() { - super.initFields() - localViewOnClickListener = LocalViewOnClickListener() - amountOpponents = opponents.size - allOpponents = Collections.synchronizedList(ArrayList(opponents.size)) - allOpponents.addAll(opponents) - - timerCallText = activity!!.findViewById(R.id.timer_call) - - isPeerToPeerCall = opponents.size == 1 - } - - private fun setDuringCallActionBar() { - actionBar.setDisplayShowTitleEnabled(true) - actionBar.title = currentUser.fullName - if (isPeerToPeerCall) { - actionBar.subtitle = getString(R.string.opponent, opponents[0].fullName) - } else { - actionBar.subtitle = getString(R.string.opponents, amountOpponents.toString()) - } - - actionButtonsEnabled(true) - } - - private fun addListeners() { - conversationFragmentCallback?.addSessionStateListener(this) - conversationFragmentCallback?.addSessionEventsListener(this) - conversationFragmentCallback?.addVideoTrackListener(this) - } - - private fun removeListeners() { - conversationFragmentCallback?.removeSessionStateListener(this) - conversationFragmentCallback?.removeSessionEventsListener(this) - conversationFragmentCallback?.removeVideoTrackListener(this) + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + setHasOptionsMenu(true) } - override fun actionButtonsEnabled(inability: Boolean) { - super.actionButtonsEnabled(inability) - cameraToggle.isEnabled = inability - // inactivate toggle buttons - cameraToggle.isActivated = inability + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { + parentView = super.onCreateView(inflater, container, savedInstanceState) + return parentView } override fun onStart() { super.onStart() - Log.i(TAG, "onStart") if (!allCallbacksInit) { addListeners() allCallbacksInit = true } } - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - Log.i(TAG, "onCreate") - setHasOptionsMenu(true) + override fun onResume() { + super.onResume() + toggleCamera(cameraToggle.isChecked) + } + + override fun onPause() { + // if camera state is CameraState.ENABLED_FROM_USER or CameraState.NONE + // than we turn off cam + toggleCamera(false) + + if (connectionEstablished) { + allCallbacksInit = false + } else { + Log.d(TAG, "We are in dialing process yet!") + } + + releaseViewHolders() + removeListeners() + releaseViews() + + super.onPause() } override fun initViews(view: View?) { @@ -171,7 +129,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, isCurrentCameraFront = true localVideoView = view.findViewById(R.id.local_video_view) initCorrectSizeForLocalView() - localVideoView.setZOrderMediaOverlay(true) + localVideoView?.setZOrderMediaOverlay(true) remoteFullScreenVideoView = view.findViewById(R.id.remote_video_view) remoteFullScreenVideoView?.setOnClickListener(localViewOnClickListener) @@ -183,10 +141,10 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, recyclerView.addItemDecoration(DividerItemDecoration(context, R.dimen.grid_item_divider)) recyclerView.setHasFixedSize(true) val columnsCount = defineColumnsCount() - val layoutManager = LinearLayoutManager(activity, LinearLayout.HORIZONTAL, false) + val layoutManager = LinearLayoutManager(activity, RecyclerView.HORIZONTAL, false) recyclerView.layoutManager = layoutManager - //for correct removing item in adapter + // for correct removing item in adapter recyclerView.itemAnimator = null recyclerView.viewTreeObserver.addOnGlobalLayoutListener(object : ViewTreeObserver.OnGlobalLayoutListener { override fun onGlobalLayout() { @@ -212,6 +170,68 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, restoreSession() } + override fun initFields() { + super.initFields() + localViewOnClickListener = LocalViewOnClickListener() + amountOpponents = opponents.size + allOpponents = Collections.synchronizedList(ArrayList(opponents.size)) + allOpponents.addAll(opponents) + + timerCallText = activity!!.findViewById(R.id.timer_call) + + isPeerToPeerCall = opponents.size == 1 + } + + override fun configureOutgoingScreen() { + val context = activity!! + outgoingOpponentsRelativeLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.grey_transparent_50)) + allOpponentsTextView.setTextColor(ContextCompat.getColor(context, R.color.white)) + ringingTextView.setTextColor(ContextCompat.getColor(context, R.color.white)) + } + + override fun configureActionBar() { + actionBar.setDisplayShowTitleEnabled(false) + } + + override fun configureToolbar() { + val context = activity!! + toolbar.visibility = View.VISIBLE + toolbar.setBackgroundColor(ContextCompat.getColor(context, R.color.black_transparent_50)) + toolbar.setTitleTextColor(ContextCompat.getColor(context, R.color.white)) + toolbar.setSubtitleTextColor(ContextCompat.getColor(context, R.color.white)) + } + + private fun setDuringCallActionBar() { + actionBar.setDisplayShowTitleEnabled(true) + actionBar.title = currentUser.fullName + if (isPeerToPeerCall) { + actionBar.subtitle = getString(R.string.opponent, opponents[0].fullName) + } else { + actionBar.subtitle = getString(R.string.opponents, amountOpponents.toString()) + } + + actionButtonsEnabled(true) + } + + private fun addListeners() { + conversationFragmentCallback?.addSessionStateListener(this) + conversationFragmentCallback?.addSessionEventsListener(this) + conversationFragmentCallback?.addVideoTrackListener(this) + } + + private fun removeListeners() { + conversationFragmentCallback?.removeSessionStateListener(this) + conversationFragmentCallback?.removeSessionEventsListener(this) + conversationFragmentCallback?.removeVideoTrackListener(this) + } + + override fun actionButtonsEnabled(inability: Boolean) { + super.actionButtonsEnabled(inability) + cameraToggle.isEnabled = inability + // inactivate toggle buttons + cameraToggle.isActivated = inability + } + private fun restoreSession() { Log.d(TAG, "restoreSession ") if (conversationFragmentCallback?.isCallState() == false) { @@ -246,7 +266,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } private fun initCorrectSizeForLocalView() { - val params = localVideoView.layoutParams + val params = localVideoView?.layoutParams val displaymetrics = resources.displayMetrics val screenWidthPx = displaymetrics.widthPixels @@ -256,7 +276,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, val height = width / 2 * 3 params?.width = width params?.height = height - localVideoView.layoutParams = params + localVideoView?.layoutParams = params } private fun setGrid(columnsCount: Int) { @@ -282,42 +302,19 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, return opponents.size - 1 } - override fun onResume() { - super.onResume() - Log.d(TAG, "onResume") - toggleCamera(cameraToggle.isChecked) - } - - override fun onPause() { - // If camera state is CameraState.ENABLED_FROM_USER or CameraState.NONE - // than we turn off cam - toggleCamera(false) - - if (connectionEstablished) { - allCallbacksInit = false - } else { - Log.d(TAG, "We are in dialing process yet!") - } - - releaseViewHolders() - removeListeners() - releaseViews() - - super.onPause() - } - - override fun onDetach() { - super.onDetach() - Log.d(TAG, "onDetach") - } - private fun releaseViewHolders() { opponentViewHolders.clear() } private fun releaseViews() { - localVideoView.release() + if (conversationFragmentCallback?.getCurrentSessionState() != BaseSession.QBRTCSessionState.QB_RTC_SESSION_CLOSED) { + for (item in (activity as CallActivity).getVideoTrackMap()) { + val renderer = item.value.renderer + item.value.removeRenderer(renderer) + } + } + localVideoView?.release() remoteFullScreenVideoView?.release() remoteFullScreenVideoView = null @@ -328,7 +325,6 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, override fun onCallStopped() { super.onCallStopped() - CallService.stop(activity as Activity) Log.i(TAG, "onCallStopped") } @@ -397,7 +393,6 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } } - //////////////////////////// callbacks from QBRTCClientVideoTracksCallbacks /////////////////// override fun onLocalVideoTrackReceive(qbrtcSession: QBRTCSession?, videoTrack: QBRTCVideoTrack) { Log.d(TAG, "onLocalVideoTrackReceive() run") localVideoTrack = videoTrack @@ -424,12 +419,9 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } } } - ///////////////////////////////////////// end //////////////////////////////////////////// - //last opponent parentView is bind override fun onBindLastViewHolder(holder: OpponentsFromCallAdapter.ViewHolder, position: Int) { Log.i(TAG, "onBindLastViewHolder position=$position") - } override fun onItemClick(position: Int) { @@ -465,12 +457,12 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } private fun swapUsersFullscreenToPreview(userId: Int) { - // get opponentVideoTrack - opponent's video track from recyclerView + // get opponentVideoTrack - opponent's video track from recyclerView val videoTrackMap = conversationFragmentCallback?.getVideoTrackMap() val opponentVideoTrack = videoTrackMap?.get(userId) - // get mainVideoTrack - opponent's video track from full screen + // get mainVideoTrack - opponent's video track from full screen val mainVideoTrack = videoTrackMap?.get(userIDFullScreen) val remoteVideoView = findHolder(userId)?.getOpponentView() @@ -503,6 +495,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, fillVideoView(remoteVideoView, videoTrack, true) } else { isRemoteShown = true + itemHolder.getOpponentView().release() opponentsAdapter.removeItem(itemHolder.adapterPosition) setDuringCallActionBar() setRecyclerViewVisibleState() @@ -562,7 +555,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, /** * @param userId set userId if it from fullscreen videoTrack */ - private fun fillVideoView(videoView: QBRTCSurfaceView, videoTrack: QBRTCVideoTrack, + private fun fillVideoView(videoView: QBRTCSurfaceView?, videoTrack: QBRTCVideoTrack, remoteRenderer: Boolean) { videoTrack.removeRenderer(videoTrack.renderer) videoTrack.addRenderer(videoView) @@ -572,12 +565,12 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, Log.d(TAG, (if (remoteRenderer) "remote" else "local") + " Track is rendering") } - private fun updateVideoView(videoView: SurfaceViewRenderer, mirror: Boolean) { + private fun updateVideoView(videoView: SurfaceViewRenderer?, mirror: Boolean) { val scalingType = RendererCommon.ScalingType.SCALE_ASPECT_FILL Log.i(TAG, "updateVideoView mirror:$mirror, scalingType = $scalingType") - videoView.setScalingType(scalingType) - videoView.setMirror(mirror) - videoView.requestLayout() + videoView?.setScalingType(scalingType) + videoView?.setMirror(mirror) + videoView?.requestLayout() } /** @@ -623,7 +616,6 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, val holder = getViewHolderForOpponent(userId) ?: return holder.getProgressBar().visibility = View.GONE - } private fun setBackgroundOpponentView(userId: Int?) { @@ -634,10 +626,8 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } } - /////////////////////////////// QBRTCSessionConnectionCallbacks /////////////////////////// - override fun onStateChanged(qbrtcSession: QBRTCSession, qbrtcSessionState: BaseSession.QBRTCSessionState) { - + // empty } override fun onConnectedToUser(qbrtcSession: QBRTCSession?, userId: Int) { @@ -660,9 +650,6 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, setStatusForOpponent(integer, getString(R.string.text_status_disconnected)) } - ////////////////////////////////// end ////////////////////////////////////////// - - /////////////////// Callbacks from CallActivity.QBRTCSessionUserCallback ////////////////////// override fun onUserNotAnswer(session: QBRTCSession, userId: Int) { setProgressBarForOpponentGone(userId) setStatusForOpponent(userId, getString(R.string.text_status_no_answer)) @@ -688,40 +675,41 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } override fun onSessionClosed(session: QBRTCSession) { - + // empty } - ////////////////////////////////// end ////////////////////////////////////////// - private fun setAnotherUserToFullScreen() { if (opponentsAdapter.opponents.isEmpty()) { return } - val userId = opponentsAdapter.getItem(0) - // get opponentVideoTrack - opponent's video track from recyclerView - val opponentVideoTrack = conversationFragmentCallback?.getVideoTrack(userId) ?: return + for (user in opponents){ + val videoTrack = conversationFragmentCallback?.getVideoTrack(user.id) + if (videoTrack != null){ + val userFullScreen = QbUsersDbManager.getUserById(userIDFullScreen) - remoteFullScreenVideoView?.let { - fillVideoView(userId, it, opponentVideoTrack) - Log.d(TAG, "fullscreen enabled") - } + val itemHolder = findHolder(user.id) + itemHolder?.setUserId(userIDFullScreen) + itemHolder?.setUserName(userFullScreen?.fullName.toString()) + itemHolder?.setStatus(getString(R.string.text_status_closed)) + itemHolder?.getOpponentView()?.release() - val itemHolder = findHolder(userId) - if (itemHolder != null) { - opponentsAdapter.removeItem(itemHolder.adapterPosition) - itemHolder.getOpponentView().release() - Log.d(TAG, "onConnectionClosedForUser opponentsAdapter.removeItem= $userId") + remoteFullScreenVideoView?.let { + fillVideoView(user.id, it, videoTrack) + Log.d(TAG, "fullscreen enabled") + } + return + } } } - override fun onCreateOptionsMenu(menu: Menu?, inflater: MenuInflater?) { - inflater?.inflate(R.menu.conversation_fragment, menu) + override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { + inflater.inflate(R.menu.conversation_fragment, menu) super.onCreateOptionsMenu(menu, inflater) optionsMenu = menu } - override fun onOptionsItemSelected(item: MenuItem?): Boolean { - when (item?.itemId) { + override fun onOptionsItemSelected(item: MenuItem): Boolean { + when (item.itemId) { R.id.camera_switch -> { Log.d("Conversation", "camera_switch") switchCamera(item) @@ -762,7 +750,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } private fun runUpdateUsersNames(newUsers: ArrayList) { - //need delayed for synchronization with recycler parentView initialization + // need delayed for synchronization with recycler parentView initialization mainHandler.postDelayed({ for (user in newUsers) { Log.d(TAG, "runUpdateUsersNames. foreach, user = " + user.fullName) @@ -804,7 +792,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, private fun hideToolBarAndButtons() { actionBar.hide() - localVideoView.visibility = View.INVISIBLE + localVideoView?.visibility = View.INVISIBLE actionVideoButtonsLayout.visibility = View.GONE if (!isPeerToPeerCall) { shiftBottomListOpponents() @@ -813,7 +801,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, private fun showToolBarAndButtons() { actionBar.show() - localVideoView.visibility = View.VISIBLE + localVideoView?.visibility = View.VISIBLE actionVideoButtonsLayout.visibility = View.VISIBLE if (!isPeerToPeerCall) { shiftMarginListOpponents() diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/CallService.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/CallService.kt index d8bd27817..5c3595b69 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/CallService.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/CallService.kt @@ -36,17 +36,17 @@ import org.webrtc.CameraVideoCapturer import java.util.* import kotlin.collections.HashMap - const val SERVICE_ID = 787 const val CHANNEL_ID = "Quickblox channel" const val CHANNEL_NAME = "Quickblox background service" +const val ONE_OPPONENT = 1 class CallService : Service() { private var TAG = CallService::class.java.simpleName private val callServiceBinder: CallServiceBinder = CallServiceBinder() - private var videoTrackMap: MutableMap = java.util.HashMap() + private var videoTrackMap: MutableMap = HashMap() private lateinit var networkConnectionListener: NetworkConnectionListener private lateinit var networkConnectionChecker: NetworkConnectionChecker private lateinit var sessionEventsListener: SessionEventsListener @@ -100,7 +100,6 @@ class CallService : Service() { super.onDestroy() networkConnectionChecker.unregisterListener(networkConnectionListener) removeConnectionListener(connectionListener) - removeVideoTrackRenders() releaseCurrentSession() releaseAudioManager() @@ -113,17 +112,23 @@ class CallService : Service() { notificationManager.cancelAll() } - override fun onBind(intent: Intent?): IBinder? { + override fun onBind(intent: Intent?): IBinder { return callServiceBinder } private fun initNotification(): Notification { + var intentFlag = 0 + val notifyIntent = Intent(this, CallActivity::class.java).apply { flags = Intent.FLAG_ACTIVITY_NEW_TASK or Intent.FLAG_ACTIVITY_CLEAR_TASK } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + intentFlag = PendingIntent.FLAG_IMMUTABLE + } + val notifyPendingIntent = PendingIntent.getActivity(this, 0, notifyIntent, - PendingIntent.FLAG_UPDATE_CURRENT) + intentFlag) val notificationTitle = getString(R.string.notification_title) var notificationText = getString(R.string.notification_text, "") @@ -232,7 +237,7 @@ class CallService : Service() { addSessionEventsListener(sessionEventsListener) } - fun initAudioManager() { + private fun initAudioManager() { appRTCAudioManager = AppRTCAudioManager.create(this) appRTCAudioManager.setOnWiredHeadsetStateListener { plugged, hasMicrophone -> @@ -269,7 +274,6 @@ class CallService : Service() { currentSession = null } - //Listeners fun addConnectionListener(connectionListener: ConnectionListener?) { QBChatService.getInstance().addConnectionListener(connectionListener) } @@ -294,11 +298,11 @@ class CallService : Service() { currentSession?.removeVideoTrackCallbacksListener(callback) } - fun addSignalingListener(callback: QBRTCSignalingCallback?) { + private fun addSignalingListener(callback: QBRTCSignalingCallback?) { currentSession?.addSignalingCallback(callback) } - fun removeSignalingListener(callback: QBRTCSignalingCallback?) { + private fun removeSignalingListener(callback: QBRTCSignalingCallback?) { currentSession?.removeSignalingCallback(callback) } @@ -310,7 +314,6 @@ class CallService : Service() { rtcClient.removeSessionsCallbacksListener(callback) } - //Common methods fun acceptCall(userInfo: Map) { currentSession?.acceptCall(userInfo) } @@ -428,28 +431,12 @@ class CallService : Service() { } private fun removeVideoTrack(userId: Int) { + val videoTrack = getVideoTrack(userId) + val renderer =videoTrack?.renderer + videoTrack?.removeRenderer(renderer) videoTrackMap.remove(userId) } - private fun removeVideoTrackRenders() { - Log.d(TAG, "removeVideoTrackRenders") - if (videoTrackMap.isNotEmpty()) { - val entryIterator = videoTrackMap.entries.iterator() - while (entryIterator.hasNext()) { - val entry = entryIterator.next() - val userId = entry.key - val videoTrack = entry.value - val qbUser = QBChatService.getInstance().user - val remoteVideoTrack = userId != qbUser.id - if (remoteVideoTrack) { - videoTrack.renderer?.let { - videoTrack.removeRenderer(it) - } - } - } - } - } - fun setCallTimerCallback(callback: CallTimerListener) { callTimerListener = callback } @@ -535,21 +522,23 @@ class CallService : Service() { } override fun onSessionStartClose(session: QBRTCSession?) { - if (session == WebRtcSessionManager.getCurrentSession()) { - CallService.stop(applicationContext) - } + // empty } override fun onReceiveHangUpFromUser(session: QBRTCSession?, userID: Int?, p2: MutableMap?) { stopRingtone() if (session == WebRtcSessionManager.getCurrentSession()) { - if (userID == session?.callerID) { + val numberOpponents = session?.opponents?.size + if (numberOpponents == ONE_OPPONENT) { currentSession?.let { it.hangUp(HashMap()) CallService.stop(this@CallService) } + }else{ + userID?.let { + removeVideoTrack(it) + } } - Log.d(TAG, "initiator hung up the call") } else { CallService.stop(this@CallService) } @@ -615,9 +604,6 @@ class CallService : Service() { override fun onConnectionClosedForUser(session: QBRTCSession?, userID: Int?) { Log.d(TAG, "Connection closed for user: $userID") shortToast("The user: " + userID + "has left the call") - userID?.let { - removeVideoTrack(it) - } } override fun onStateChanged(session: QBRTCSession?, sessionState: BaseSession.QBRTCSessionState?) { @@ -627,7 +613,7 @@ class CallService : Service() { private inner class QBRTCSignalingListener : QBRTCSignalingCallback { override fun onSuccessSendingPacket(p0: QBSignalingSpec.QBSignalCMD?, p1: Int?) { - + // empty } override fun onErrorSendingPacket(p0: QBSignalingSpec.QBSignalCMD?, p1: Int?, p2: QBRTCSignalException?) { diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/util/ChatPingAlarmManager.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/util/ChatPingAlarmManager.kt index 701b3459d..6adffca8f 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/util/ChatPingAlarmManager.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/util/ChatPingAlarmManager.kt @@ -6,6 +6,7 @@ import android.content.BroadcastReceiver import android.content.Context import android.content.Intent import android.content.IntentFilter +import android.os.Build import android.os.Bundle import android.os.SystemClock import android.util.Log @@ -53,10 +54,17 @@ object ChatPingAlarmManager { * @param context */ fun onCreate(context: Context) { + var intentFlag = 0 + this.context = WeakReference(context) context.registerReceiver(alarmBroadcastReceiver, IntentFilter(PING_ALARM_ACTION)) alarmManager = context.getSystemService(Context.ALARM_SERVICE) as AlarmManager - pendingIntent = PendingIntent.getBroadcast(context, 0, Intent(PING_ALARM_ACTION), 0) + + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.M) { + intentFlag = PendingIntent.FLAG_IMMUTABLE + } + + pendingIntent = PendingIntent.getBroadcast(context, 0, Intent(PING_ALARM_ACTION), intentFlag) val trigger = SystemClock.elapsedRealtime() + PING_INTERVAL alarmManager.setInexactRepeating(AlarmManager.ELAPSED_REALTIME_WAKEUP, trigger, PING_INTERVAL, pendingIntent) } diff --git a/sample-videochat-kotlin/app/src/main/res/values/strings.xml b/sample-videochat-kotlin/app/src/main/res/values/strings.xml index 96ab7e661..bc9947e70 100644 --- a/sample-videochat-kotlin/app/src/main/res/values/strings.xml +++ b/sample-videochat-kotlin/app/src/main/res/values/strings.xml @@ -160,7 +160,7 @@ answer_time_interval - 60 + 30 disconnect_time_interval 10 Dialing_time_interval @@ -215,5 +215,6 @@ You can select up to %d opponents You should allow Camera permission You should allow Microphone permission + Error getting permissions \ No newline at end of file diff --git a/sample-videochat-kotlin/build.gradle b/sample-videochat-kotlin/build.gradle index d2e7db27a..cbe1ad7e0 100644 --- a/sample-videochat-kotlin/build.gradle +++ b/sample-videochat-kotlin/build.gradle @@ -1,22 +1,19 @@ buildscript { repositories { google() - jcenter() + mavenCentral() } dependencies { - classpath 'com.android.tools.build:gradle:3.2.0' - classpath 'com.google.gms:google-services:4.1.0' + classpath 'com.android.tools.build:gradle:4.2.2' + classpath 'com.google.gms:google-services:4.3.10' } } allprojects { repositories { google() - jcenter() - maven { - url "https://github.com/QuickBlox/quickblox-android-sdk-releases/raw/master/" - } + mavenCentral() } } @@ -26,32 +23,22 @@ ext { lintAbortOnError = false // QuickBlox SDK version - qbSdkVersion = '3.9.2' + qbSdkVersion = '3.9.15' //Firebase - firebaseCoreVersion = '16.0.8' - - //Glide - glideVersion = '3.6.1' + firebaseCoreVersion = '20.1.2' //Material - materialVersion = '1.0.0' + materialVersion = '1.5.0' //RobotoTextView robotoTextViewVersion = '4.0.0' //Android X - fragmentAndroidXVersion = '1.0.0' - lifecycleViewmodelAndroidXVersion = '1.0.0' - coreKtxVersion = '1.0.1' - - //Crashlytics - crashlyticsVersion = '2.9.5' - - //Fabric - fabricToolsVersion = '1.27.0' + fragmentAndroidXVersion = '1.4.1' + lifecycleViewmodelAndroidXVersion = '2.4.1' + coreKtxVersion = '1.7.0' //Kotlin - kotlinGradlePluginVersion = '1.2.71' - kotlinReflectVersion = '1.2.71' + kotlinGradlePluginVersion = '1.5.31' } \ No newline at end of file diff --git a/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.jar b/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.jar index f6b961fd5..758de960e 100644 Binary files a/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.jar and b/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.jar differ diff --git a/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.properties b/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.properties index 9a4163a4f..4d9ca1649 100644 --- a/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.properties +++ b/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-4.6-all.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists