diff --git a/sample-videochat-kotlin/app/build.gradle b/sample-videochat-kotlin/app/build.gradle index 3d08a2c63..310338da7 100644 --- a/sample-videochat-kotlin/app/build.gradle +++ b/sample-videochat-kotlin/app/build.gradle @@ -4,7 +4,7 @@ buildscript { mavenCentral() } dependencies { - classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinGradlePluginVersion" + classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.5.31" } } @@ -29,26 +29,26 @@ android { compileSdkVersion 31 buildToolsVersion "31.0.0" - flavorDimensions dimensionDefault + flavorDimensions "default" defaultConfig { applicationId "com.quickblox.sample.videochat.kotlin" minSdkVersion 21 targetSdkVersion 31 - versionCode 405000 - versionName '4.0.5' + versionCode 410000 + versionName '4.1.0' multiDexEnabled true } productFlavors { dev { - dimension dimensionDefault + dimension "default" buildConfigField('boolean', "IS_QA", "false") buildConfigField("int", "VERSION_QA_CODE", versionQACode.toString()) } qa { - dimension dimensionDefault + dimension "default" buildConfigField("boolean", "IS_QA", "true") buildConfigField("int", "VERSION_QA_CODE", versionQACode.toString()) } @@ -92,14 +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.google.android.material:material:$materialVersion" - implementation "com.github.johnkil.android-robototextview:robototextview:$robotoTextViewVersion" - implementation "androidx.fragment:fragment-ktx:$fragmentAndroidXVersion" - implementation "androidx.lifecycle:lifecycle-viewmodel-ktx:$lifecycleViewmodelAndroidXVersion" - implementation "androidx.core:core-ktx:$coreKtxVersion" + implementation 'com.quickblox:quickblox-android-sdk-videochat-webrtc:3.10.1' + implementation 'com.quickblox:quickblox-android-sdk-messages:3.10.1' + 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' + implementation 'androidx.fragment:fragment-ktx:1.5.2' + implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.5.1' + implementation 'androidx.core:core-ktx:1.8.0' } apply from: "../artifacts.gradle" diff --git a/sample-videochat-kotlin/app/proguard-rules.pro b/sample-videochat-kotlin/app/proguard-rules.pro index 7c835c9ff..6eb13999d 100644 --- a/sample-videochat-kotlin/app/proguard-rules.pro +++ b/sample-videochat-kotlin/app/proguard-rules.pro @@ -41,4 +41,7 @@ -keep class org.webrtc.** { *; } #google gms --keep class com.google.android.gms.** { *; } \ No newline at end of file +-keep class com.google.android.gms.** { *; } + +#json +-keep class org.json.** { *; } \ 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 1f9e4169f..4ca7e0b71 100644 --- a/sample-videochat-kotlin/app/src/main/AndroidManifest.xml +++ b/sample-videochat-kotlin/app/src/main/AndroidManifest.xml @@ -37,11 +37,6 @@ android:name=".activities.LoginActivity" android:screenOrientation="portrait" /> - - 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 81bd0a966..11b0d0e27 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 @@ -4,10 +4,10 @@ import android.app.Application import com.quickblox.auth.session.QBSettings import com.quickblox.sample.videochat.kotlin.db.DbHelper -//User default credentials +// user default credentials const val DEFAULT_USER_PASSWORD = "quickblox" -//App credentials +// app credentials private const val APPLICATION_ID = "" private const val AUTH_KEY = "" private const val AUTH_SECRET = "" @@ -42,7 +42,7 @@ class App : Application() { QBSettings.getInstance().init(applicationContext, APPLICATION_ID, AUTH_KEY, AUTH_SECRET) QBSettings.getInstance().accountKey = ACCOUNT_KEY - // Uncomment and put your Api and Chat servers endpoints if you want to point the sample + // uncomment and put your Api and Chat servers endpoints if you want to point the sample // against your own server. // // QBSettings.getInstance().setEndpoints("https://your_api_endpoint.com", "your_chat_endpoint", ServiceZone.PRODUCTION); 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 626995549..b70a3ddbe 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 @@ -71,7 +71,7 @@ class AppInfoActivity : AppCompatActivity() { appQAVersionTextView.setText(spannable, TextView.BufferType.SPANNABLE) appQAVersionTextView.visibility = View.VISIBLE - findViewById(R.id.text_qa_version_title).visibility = View.VISIBLE + findViewById(R.id.text_qa_version_title).visibility = View.VISIBLE } } 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 a6a88b1a3..2ad1c561f 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 @@ -11,23 +11,19 @@ import androidx.core.content.ContextCompat import com.quickblox.sample.videochat.kotlin.R import com.quickblox.sample.videochat.kotlin.utils.showErrorSnackbar - abstract class BaseActivity : AppCompatActivity() { private var progressDialog: ProgressDialog? = null - internal fun showProgressDialog(@StringRes messageId: Int) { if (progressDialog == null) { progressDialog = ProgressDialog(this) - progressDialog!!.isIndeterminate = true - progressDialog!!.setCancelable(false) - progressDialog!!.setCanceledOnTouchOutside(false) - - // Disable the back button - progressDialog!!.setOnKeyListener(KeyEventListener()) + progressDialog?.isIndeterminate = true + progressDialog?.setCancelable(false) + progressDialog?.setCanceledOnTouchOutside(false) + progressDialog?.setOnKeyListener(KeyEventListener()) } - progressDialog!!.setMessage(getString(messageId)) - progressDialog!!.show() + progressDialog?.setMessage(getString(messageId)) + progressDialog?.show() } override fun onPause() { @@ -36,16 +32,14 @@ abstract class BaseActivity : AppCompatActivity() { } internal fun hideProgressDialog() { - if (progressDialog != null && progressDialog?.isShowing == true) { - progressDialog!!.dismiss() + if (progressDialog?.isShowing == true) { + progressDialog?.dismiss() } } - protected fun showErrorSnackbar(@StringRes resId: Int, e: Exception, clickListener: View.OnClickListener) { + protected fun showErrorSnackbar(@StringRes resId: Int, e: Exception?, clickListener: View.OnClickListener) { val rootView = window.decorView.findViewById(android.R.id.content) - if (rootView != null) { - showErrorSnackbar(rootView, resId, e, R.string.dlg_retry, clickListener) - } + showErrorSnackbar(rootView, resId, e, R.string.dlg_retry, clickListener) } protected fun checkPermissions(permissions: Array): Boolean { 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 54197eaae..0a12484df 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 @@ -2,6 +2,7 @@ package com.quickblox.sample.videochat.kotlin.activities import android.annotation.TargetApi import android.app.Activity +import android.app.KeyguardManager import android.content.* import android.net.Uri import android.os.* @@ -9,19 +10,18 @@ import android.preference.PreferenceManager import android.provider.Settings import android.util.Log import android.view.View -import android.view.ViewGroup +import android.view.WindowManager +import android.widget.FrameLayout import android.widget.LinearLayout import android.widget.TextView -import com.quickblox.chat.QBChatService import com.quickblox.core.QBEntityCallbackImpl import com.quickblox.sample.videochat.kotlin.R 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.services.MIN_OPPONENT_SIZE 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.callbacks.QBRTCClientSessionCallbacks @@ -43,15 +43,16 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe private var TAG = CallActivity::class.java.simpleName - private val currentCallStateCallbackList = ArrayList() + private val callStateListeners = hashSetOf() + private val updateOpponentsListeners = hashSetOf() + private val callTimeUpdateListeners = hashSetOf() private lateinit var showIncomingCallWindowTaskHandler: Handler private var connectionListener: ConnectionListenerImpl? = null private lateinit var callServiceConnection: ServiceConnection private lateinit var showIncomingCallWindowTask: Runnable - private lateinit var sharedPref: SharedPreferences - private var opponentsIdsList: List? = null + private var opponentIds: List? = null private lateinit var callService: CallService - + private var connectionView: LinearLayout? = null private var isInComingCall: Boolean = false private var isVideoCall: Boolean = false @@ -69,21 +70,38 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_main) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O_MR1) { + setShowWhenLocked(true) + setTurnScreenOn(true) + val keyguardManager = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager + keyguardManager.requestDismissKeyguard(this, null) + } else { + this.window.addFlags( + WindowManager.LayoutParams.FLAG_DISMISS_KEYGUARD or + WindowManager.LayoutParams.FLAG_SHOW_WHEN_LOCKED or + WindowManager.LayoutParams.FLAG_TURN_SCREEN_ON + ) + } + connectionView = View.inflate(this, R.layout.connection_popup, null) as LinearLayout } private fun initScreen() { callService.setCallTimerCallback(CallTimerCallback()) isVideoCall = callService.isVideoCall() - opponentsIdsList = callService.getOpponents() + opponentIds = callService.getOpponents() - PreferenceManager.setDefaultValues(this, R.xml.preferences, false) - sharedPref = PreferenceManager.getDefaultSharedPreferences(this) + applyMediaSettings() - initSettingsStrategy() addListeners() - if (callService.isCallMode()) { + isInComingCall = if (intent != null && intent.extras != null) { + intent?.extras?.getBoolean(EXTRA_IS_INCOMING_CALL) ?: false + } else { + SharedPrefsHelper.get(EXTRA_IS_INCOMING_CALL, false) + } + + if (callService.isConnectedCall()) { checkPermission() if (callService.isSharingScreenState()) { startScreenSharing(null) @@ -91,12 +109,6 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } addConversationFragment(isInComingCall) } else { - if (intent != null && intent.extras != null) { - isInComingCall = intent?.extras?.getBoolean(EXTRA_IS_INCOMING_CALL) ?: true - } else { - isInComingCall = SharedPrefsHelper.get(EXTRA_IS_INCOMING_CALL, false) - } - if (!isInComingCall) { callService.playRingtone() } @@ -128,6 +140,7 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { + super.onActivityResult(requestCode, resultCode, data) Log.i(TAG, "onActivityResult requestCode=$requestCode, resultCode= $resultCode") if (resultCode == EXTRA_LOGIN_RESULT_CODE) { data?.let { @@ -163,9 +176,9 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe private fun startSuitableFragment(isInComingCall: Boolean) { val session = WebRtcSessionManager.getCurrentSession() if (session != null) { + loadAbsentUsers() if (isInComingCall) { initIncomingCallTask() - startLoadAbsentUsers() addIncomeCallFragment() checkPermission() } else { @@ -224,11 +237,11 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe startActivityForResult(intent, REQUEST_PERMISSION_SETTING) } - private fun startLoadAbsentUsers() { + private fun loadAbsentUsers() { val usersFromDb = QbUsersDbManager.allUsers val allParticipantsOfCall = ArrayList() - opponentsIdsList?.let { + opponentIds?.let { allParticipantsOfCall.addAll(it) } @@ -248,20 +261,14 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe idsNotLoadedUsers.add(userId) } } - - if (!idsNotLoadedUsers.isEmpty()) { - loadUsersByIds(idsNotLoadedUsers, object : QBEntityCallbackImpl>() { - override fun onSuccess(users: ArrayList, params: Bundle) { - QbUsersDbManager.saveAllUsers(users, false) - notifyCallStateListenersNeedUpdateOpponentsList(users) - } - }) - } - } - - private fun initSettingsStrategy() { - opponentsIdsList?.let { - setSettingsStrategy(it, sharedPref, this) + if (idsNotLoadedUsers.isNotEmpty()) { + QBUsers.getUsersByIDs(idsNotLoadedUsers, null) + .performAsync(object : QBEntityCallbackImpl>() { + override fun onSuccess(users: ArrayList, params: Bundle) { + QbUsersDbManager.saveAllUsers(users, false) + notifyOpponentsUpdated(users) + } + }) } } @@ -345,27 +352,31 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe ) } - private fun showNotificationPopUp(text: Int, show: Boolean) { + private fun showConnectionPopUp() { runOnUiThread { - val connectionView = View.inflate(this, R.layout.connection_popup, null) as LinearLayout - if (show) { - (connectionView.findViewById(R.id.notification) as TextView).setText(text) - if (connectionView.parent == null) { - (this@CallActivity.findViewById(R.id.fragment_container) as ViewGroup).addView(connectionView) - } - } else { - (this@CallActivity.findViewById(R.id.fragment_container) as ViewGroup).removeView(connectionView) + val fragmentContainer: FrameLayout = findViewById(R.id.fragment_container) + val connectionNotificationView: TextView? = connectionView?.findViewById(R.id.notification) + connectionNotificationView?.setText(R.string.connection_was_lost) + if (connectionView?.parent == null) { + fragmentContainer.addView(connectionView) } } } + private fun hideConnectionPopUp() { + runOnUiThread { + val fragmentContainer: FrameLayout = findViewById(R.id.fragment_container) + fragmentContainer.removeView(connectionView) + } + } + private inner class ConnectionListenerImpl : AbstractConnectionListener() { override fun connectionClosedOnError(e: Exception?) { - showNotificationPopUp(R.string.connection_was_lost, true) + showConnectionPopUp() } override fun reconnectionSuccessful() { - showNotificationPopUp(R.string.connection_was_lost, false) + hideConnectionPopUp() } } @@ -374,7 +385,7 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } override fun onConnectedToUser(session: QBRTCSession?, userId: Int?) { - notifyCallStateListenersCallStarted() + notifyCallStarted() if (isInComingCall) { stopIncomeCallTimer() } @@ -398,14 +409,14 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe override fun onSessionStartClose(session: QBRTCSession?) { if (callService.isCurrentSession(session)) { callService.removeSessionStateListener(this) - notifyCallStateListenersCallStopped() + notifyCallStopped() } } override fun onReceiveHangUpFromUser(session: QBRTCSession?, userId: Int?, map: MutableMap?) { if (callService.isCurrentSession(session)) { val numberOpponents = session?.opponents?.size - if (numberOpponents == ONE_OPPONENT) { + if (numberOpponents == MIN_OPPONENT_SIZE) { hangUpCurrentSession() } val participant = QbUsersDbManager.getUserById(userId) @@ -436,9 +447,7 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe } override fun onCallRejectByUser(session: QBRTCSession?, userId: Int?, map: MutableMap?) { - if (callService.isCurrentSession(session)) { - callService.stopRingtone() - } + // empty } override fun onAcceptCurrentSession() { @@ -505,14 +514,28 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe callService.removeSessionEventsListener(eventsCallback) } - override fun addCurrentCallStateListener(currentCallStateCallback: CurrentCallStateCallback?) { - currentCallStateCallback?.let { - currentCallStateCallbackList.add(it) - } + override fun addCallStateListener(callStateListener: CallStateListener) { + callStateListeners.add(callStateListener) + } + + override fun removeCallStateListener(callStateListener: CallStateListener) { + callStateListeners.remove(callStateListener) + } + + override fun addUpdateOpponentsListener(updateOpponentsListener: UpdateOpponentsListener) { + updateOpponentsListeners.add(updateOpponentsListener) + } + + override fun removeUpdateOpponentsListener(updateOpponentsListener: UpdateOpponentsListener) { + updateOpponentsListeners.remove(updateOpponentsListener) + } + + override fun addCallTimeUpdateListener(callTimeUpdateListener: CallTimeUpdateListener) { + callTimeUpdateListeners.add(callTimeUpdateListener) } - override fun removeCurrentCallStateListener(currentCallStateCallback: CurrentCallStateCallback?) { - currentCallStateCallbackList.remove(currentCallStateCallback) + override fun removeCallTimeUpdateListener(callTimeUpdateListener: CallTimeUpdateListener) { + callTimeUpdateListeners.remove(callTimeUpdateListener) } override fun addOnChangeAudioDeviceListener(onChangeDynamicCallback: OnChangeAudioDevice?) { @@ -563,8 +586,8 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe return callService.isMediaStreamManagerExist() } - override fun isCallState(): Boolean { - return callService.isCallMode() + override fun isConnectedCall(): Boolean { + return callService.isConnectedCall() } override fun getVideoTrackMap(): MutableMap { @@ -577,30 +600,30 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe override fun onStopPreview() { callService.stopScreenSharing() - addConversationFragment(false) + addConversationFragment(isInComingCall) } - private fun notifyCallStateListenersCallStarted() { - for (callback in currentCallStateCallbackList) { - callback.onCallStarted() + private fun notifyCallStarted() { + for (listener in callStateListeners) { + listener.startedCall() } } - private fun notifyCallStateListenersCallStopped() { - for (callback in currentCallStateCallbackList) { - callback.onCallStopped() + private fun notifyCallStopped() { + for (listener in callStateListeners) { + listener.stoppedCall() } } - private fun notifyCallStateListenersNeedUpdateOpponentsList(newUsers: ArrayList) { - for (callback in currentCallStateCallbackList) { - callback.onOpponentsListUpdated(newUsers) + private fun notifyOpponentsUpdated(opponents: ArrayList) { + for (listener in updateOpponentsListeners) { + listener.updatedOpponents(opponents) } } - private fun notifyCallStateListenersCallTime(callTime: String) { - for (callback in currentCallStateCallbackList) { - callback.onCallTimeUpdate(callTime) + private fun notifyCallTimeUpdated(callTime: String) { + for (listener in callTimeUpdateListeners) { + listener.updatedCallTime(callTime) } } @@ -613,29 +636,17 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe 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 - if (QBChatService.getInstance().isLoggedIn) { - initScreen() - } else { - login() - } + initScreen(); } else { finish() } } - - private fun login() { - val qbUser = SharedPrefsHelper.getQbUser() - val tempIntent = Intent(this@CallActivity, LoginService::class.java) - val pendingIntent = createPendingResult(EXTRA_LOGIN_RESULT_CODE, tempIntent, 0) - LoginService.start(this@CallActivity, qbUser, pendingIntent) - } } private inner class CallTimerCallback : CallService.CallTimerListener { override fun onCallTimeUpdate(time: String) { runOnUiThread { - notifyCallStateListenersCallTime(time) + notifyCallTimeUpdated(time) } } } @@ -644,13 +655,17 @@ class CallActivity : BaseActivity(), IncomeCallFragmentCallbackListener, QBRTCSe fun audioDeviceChanged(newAudioDevice: AppRTCAudioManager.AudioDevice) } - interface CurrentCallStateCallback { - fun onCallStarted() + interface CallStateListener { + fun startedCall() - fun onCallStopped() + fun stoppedCall() + } - fun onOpponentsListUpdated(newUsers: ArrayList) + interface UpdateOpponentsListener { + fun updatedOpponents(updatedOpponents: ArrayList) + } - fun onCallTimeUpdate(time: String) + interface CallTimeUpdateListener { + fun updatedCallTime(time: String) } } \ No newline at end of file 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 15f9d8a4b..b98285241 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 @@ -13,8 +13,6 @@ import com.quickblox.core.exception.QBResponseException import com.quickblox.sample.videochat.kotlin.DEFAULT_USER_PASSWORD import com.quickblox.sample.videochat.kotlin.R import com.quickblox.sample.videochat.kotlin.services.LoginService -import com.quickblox.sample.videochat.kotlin.util.signInUser -import com.quickblox.sample.videochat.kotlin.util.signUp import com.quickblox.sample.videochat.kotlin.utils.* import com.quickblox.users.QBUsers import com.quickblox.users.model.QBUser @@ -22,9 +20,8 @@ import com.quickblox.users.model.QBUser const val ERROR_LOGIN_ALREADY_TAKEN_HTTP_STATUS = 422 class LoginActivity : BaseActivity() { - private lateinit var userLoginEditText: EditText - private lateinit var userFullNameEditText: EditText + private lateinit var userDisplayNameEditText: EditText private lateinit var user: QBUser @@ -45,11 +42,11 @@ class LoginActivity : BaseActivity() { userLoginEditText = findViewById(R.id.userLoginEditText) userLoginEditText.addTextChangedListener(LoginEditTextWatcher(userLoginEditText)) - userFullNameEditText = findViewById(R.id.userFullNameEditText) - userFullNameEditText.addTextChangedListener(LoginEditTextWatcher(userFullNameEditText)) + userDisplayNameEditText = findViewById(R.id.userDisplayNameEditText) + userDisplayNameEditText.addTextChangedListener(LoginEditTextWatcher(userDisplayNameEditText)) } - override fun onCreateOptionsMenu(menu: Menu?): Boolean { + override fun onCreateOptionsMenu(menu: Menu): Boolean { menuInflater.inflate(R.menu.activity_login, menu) return true } @@ -57,41 +54,37 @@ class LoginActivity : BaseActivity() { override fun onOptionsItemSelected(item: MenuItem): Boolean { return when (item.itemId) { R.id.menu_login_user_done -> { - if (isEnteredUserNameValid() && isEnteredRoomNameValid()) { - hideKeyboard() - val user = createUserWithEnteredData() - signUpNewUser(user) + val isNotValidLogin = !isLoginValid(userLoginEditText.text.toString()) + if (isNotValidLogin) { + userLoginEditText.error = getString(R.string.error_login) + return false + } + + val isNotValidDisplayName = !isDisplayNameValid(userDisplayNameEditText.text.toString()) + if (isNotValidDisplayName) { + userDisplayNameEditText.error = getString(R.string.error_display_name) + return false } + hideKeyboard(userDisplayNameEditText) + val user = createUser() + signUp(user) return true } else -> super.onOptionsItemSelected(item) } } - private fun isEnteredUserNameValid(): Boolean { - return isLoginValid(this, userLoginEditText) - } - - private fun isEnteredRoomNameValid(): Boolean { - return isFoolNameValid(this, userFullNameEditText) - } - - private fun hideKeyboard() { - hideKeyboard(userLoginEditText) - hideKeyboard(userFullNameEditText) - } - - private fun signUpNewUser(newUser: QBUser) { + private fun signUp(user: QBUser) { showProgressDialog(R.string.dlg_creating_new_user) - signUp(newUser, object : QBEntityCallback { + QBUsers.signUp(user).performAsync(object : QBEntityCallback { override fun onSuccess(result: QBUser, params: Bundle) { - SharedPrefsHelper.saveQbUser(newUser) + SharedPrefsHelper.saveCurrentUser(user) loginToChat(result) } override fun onError(e: QBResponseException) { if (e.httpStatusCode == ERROR_LOGIN_ALREADY_TAKEN_HTTP_STATUS) { - signInCreatedUser(newUser) + loginToRest(user) } else { hideProgressDialog() longToast(R.string.sign_up_error) @@ -100,20 +93,20 @@ class LoginActivity : BaseActivity() { }) } - private fun loginToChat(qbUser: QBUser) { - qbUser.password = DEFAULT_USER_PASSWORD - user = qbUser - startLoginService(qbUser) + private fun loginToChat(user: QBUser) { + user.password = DEFAULT_USER_PASSWORD + this.user = user + startLoginService(user) } - private fun createUserWithEnteredData(): QBUser { - val qbUser = QBUser() + private fun createUser(): QBUser { + val user = QBUser() val userLogin = userLoginEditText.text.toString() - val userFullName = userFullNameEditText.text.toString() - qbUser.login = userLogin - qbUser.fullName = userFullName - qbUser.password = DEFAULT_USER_PASSWORD - return qbUser + val userFullName = userDisplayNameEditText.text.toString() + user.login = userLogin + user.fullName = userFullName + user.password = DEFAULT_USER_PASSWORD + return user } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -121,31 +114,30 @@ class LoginActivity : BaseActivity() { if (resultCode == EXTRA_LOGIN_RESULT_CODE) { hideProgressDialog() - var isLoginSuccess = false - data?.let { - isLoginSuccess = it.getBooleanExtra(EXTRA_LOGIN_RESULT, false) - } - - var errorMessage: String? = getString(R.string.unknown_error) + var isLoginSuccessInChat = false data?.let { - errorMessage = it.getStringExtra(EXTRA_LOGIN_ERROR_MESSAGE) + isLoginSuccessInChat = it.getBooleanExtra(EXTRA_LOGIN_RESULT, false) } - if (isLoginSuccess) { - SharedPrefsHelper.saveQbUser(user) - signInCreatedUser(user) + if (isLoginSuccessInChat) { + SharedPrefsHelper.saveCurrentUser(user) + loginToRest(user) } else { + var errorMessage: String? = getString(R.string.unknown_error) + data?.let { + errorMessage = it.getStringExtra(EXTRA_LOGIN_ERROR_MESSAGE) + } longToast(getString(R.string.login_chat_login_error) + errorMessage) userLoginEditText.setText(user.login) - userFullNameEditText.setText(user.fullName) + userDisplayNameEditText.setText(user.fullName) } } } - private fun signInCreatedUser(user: QBUser) { - signInUser(user, object : QBEntityCallback { + private fun loginToRest(user: QBUser) { + QBUsers.signIn(user).performAsync(object : QBEntityCallback { override fun onSuccess(result: QBUser, params: Bundle) { - SharedPrefsHelper.saveQbUser(user) + SharedPrefsHelper.saveCurrentUser(user) updateUserOnServer(user) } @@ -159,7 +151,7 @@ class LoginActivity : BaseActivity() { private fun updateUserOnServer(user: QBUser) { user.password = null QBUsers.updateUser(user).performAsync(object : QBEntityCallback { - override fun onSuccess(updUser: QBUser?, params: Bundle?) { + override fun onSuccess(user: QBUser?, params: Bundle?) { hideProgressDialog() OpponentsActivity.start(this@LoginActivity) finish() @@ -179,7 +171,7 @@ class LoginActivity : BaseActivity() { private fun startLoginService(qbUser: QBUser) { val tempIntent = Intent(this, LoginService::class.java) val pendingIntent = createPendingResult(EXTRA_LOGIN_RESULT_CODE, tempIntent, 0) - LoginService.start(this, qbUser, pendingIntent) + LoginService.loginToChatAndInitRTCClient(this, qbUser, pendingIntent) } private inner class LoginEditTextWatcher internal constructor(private val editText: EditText) : diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/OpponentsActivity.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/OpponentsActivity.kt index 356ad0ef2..2aadaa5bc 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/OpponentsActivity.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/OpponentsActivity.kt @@ -9,7 +9,6 @@ import android.text.TextUtils import android.util.Log import android.view.Menu import android.view.MenuItem -import android.view.View import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.RecyclerView import com.quickblox.chat.QBChatService @@ -22,16 +21,15 @@ import com.quickblox.messages.services.SubscribeService import com.quickblox.sample.videochat.kotlin.R import com.quickblox.sample.videochat.kotlin.adapters.UsersAdapter import com.quickblox.sample.videochat.kotlin.db.QbUsersDbManager +import com.quickblox.sample.videochat.kotlin.executor.Executor +import com.quickblox.sample.videochat.kotlin.executor.ExecutorTask import com.quickblox.sample.videochat.kotlin.services.CallService import com.quickblox.sample.videochat.kotlin.services.LoginService -import com.quickblox.sample.videochat.kotlin.util.signOut import com.quickblox.sample.videochat.kotlin.utils.* import com.quickblox.users.QBUsers import com.quickblox.users.model.QBUser import com.quickblox.videochat.webrtc.QBRTCClient import com.quickblox.videochat.webrtc.QBRTCTypes -import kotlin.Exception - private const val PER_PAGE_SIZE_100 = 100 private const val ORDER_RULE = "order" @@ -61,7 +59,7 @@ class OpponentsActivity : BaseActivity() { override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) setContentView(R.layout.activity_select_users) - currentUser = SharedPrefsHelper.getQbUser() + currentUser = SharedPrefsHelper.getCurrentUser() initDefaultActionBar() initUI() startLoginService() @@ -121,7 +119,7 @@ class OpponentsActivity : BaseActivity() { } if (currentPage == 1) { - initUsersList() + initOpponents() } else { usersAdapter?.addUsers(qbUsers) } @@ -136,7 +134,9 @@ class OpponentsActivity : BaseActivity() { hideProgressDialog() isLoading = false currentPage -= 1 - showErrorSnackbar(R.string.loading_users_error, e, View.OnClickListener { loadUsers() }) + showErrorSnackbar(R.string.loading_users_error, e) { + loadUsers() + } } } }) @@ -146,12 +146,13 @@ class OpponentsActivity : BaseActivity() { usersRecyclerView = findViewById(R.id.list_select_users) } - private fun initUsersList() { - val currentOpponentsList = QbUsersDbManager.allUsers - currentOpponentsList.remove(SharedPrefsHelper.getQbUser()) + private fun initOpponents() { + val opponents = QbUsersDbManager.allUsers + opponents.remove(SharedPrefsHelper.getCurrentUser()) if (usersAdapter == null) { - usersAdapter = UsersAdapter(this, currentOpponentsList) - usersAdapter!!.setSelectedItemsCountsChangedListener(object : UsersAdapter.SelectedItemsCountsChangedListener { + usersAdapter = UsersAdapter(this, opponents) + usersAdapter?.setSelectedItemsCountsChangedListener(object : + UsersAdapter.SelectedItemsCountsChangedListener { override fun onCountSelectedItemsChanged(count: Int) { updateActionBar(count) } @@ -161,7 +162,7 @@ class OpponentsActivity : BaseActivity() { usersRecyclerView.adapter = usersAdapter usersRecyclerView.addOnScrollListener(ScrollListener(usersRecyclerView.layoutManager as LinearLayoutManager)) } else { - usersAdapter!!.updateUsersList(currentOpponentsList) + usersAdapter?.updateUsersList(opponents) } } @@ -183,12 +184,10 @@ class OpponentsActivity : BaseActivity() { loadUsers() return true } - R.id.settings -> { - SettingsActivity.start(this) - return true - } R.id.log_out -> { - unsubscribeFromPushesAndLogout() + unsubscribeFromPushes(callback = { + logout() + }) return true } R.id.start_video_call -> { @@ -227,67 +226,82 @@ class OpponentsActivity : BaseActivity() { } private fun startLoginService() { - if (SharedPrefsHelper.hasQbUser()) { - LoginService.start(this, SharedPrefsHelper.getQbUser()) + if (SharedPrefsHelper.hasCurrentUser()) { + LoginService.loginToChatAndInitRTCClient(this, SharedPrefsHelper.getCurrentUser()) } } private fun startCall(isVideoCall: Boolean) { - val usersCount = usersAdapter!!.selectedUsers.size - if (usersCount > MAX_OPPONENTS_COUNT) { + val usersCount = usersAdapter?.selectedUsers?.size + if (usersCount != null && usersCount > MAX_OPPONENTS_COUNT) { longToast(String.format(getString(R.string.error_max_opponents_count), MAX_OPPONENTS_COUNT)) return } - val opponentsList = getIdsSelectedOpponents(usersAdapter!!.selectedUsers) + val userIds = usersAdapter?.selectedUsers?.let { getOpponentIds(it) } val conferenceType = if (isVideoCall) { QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_VIDEO } else { QBRTCTypes.QBConferenceType.QB_CONFERENCE_TYPE_AUDIO } - val qbrtcClient = QBRTCClient.getInstance(applicationContext) - val newQbRtcSession = qbrtcClient.createNewSessionWithOpponents(opponentsList, conferenceType) - WebRtcSessionManager.setCurrentSession(newQbRtcSession) + val rtcClient = QBRTCClient.getInstance(applicationContext) + val session = rtcClient.createNewSessionWithOpponents(userIds, conferenceType) + WebRtcSessionManager.setCurrentSession(session) - // Make Users FullName Strings and ID's list for iOS VOIP push - val newSessionID = newQbRtcSession.sessionID - val opponentsIDsList = java.util.ArrayList() - val opponentsNamesList = java.util.ArrayList() - val usersInCall: ArrayList = usersAdapter!!.selectedUsers as ArrayList + // make Users FullName Strings and id's list for iOS VOIP push + val sessionId = session.sessionID + val opponentIds = ArrayList() + val opponentNames = ArrayList() + val usersInCall = usersAdapter?.selectedUsers as ArrayList // the Caller in exactly first position is needed regarding to iOS 13 functionality usersInCall.add(0, currentUser) for (user in usersInCall) { - val userId = user.id!!.toString() - var userName = "" - if (TextUtils.isEmpty(user.fullName)) { - userName = user.login - } else { - userName = user.fullName - } + val userId = user.id.toString() + val name = user.fullName ?: user.login - opponentsIDsList.add(userId) - opponentsNamesList.add(userName) + opponentIds.add(userId) + opponentNames.add(name) } - val opponentsIDsString = TextUtils.join(",", opponentsIDsList) - val opponentNamesString = TextUtils.join(",", opponentsNamesList) + val idsInLine = TextUtils.join(",", opponentIds) + val namesInLine = TextUtils.join(",", opponentNames) - Log.d(TAG, "New Session with ID: $newSessionID\n Users in Call: \n$opponentsIDsString\n$opponentNamesString") - sendPushMessage(opponentsList, currentUser.fullName, newSessionID, opponentsIDsString, opponentNamesString, isVideoCall) + Log.d(TAG, "New Session with id: $sessionId\n Users in Call: \n$idsInLine\n$namesInLine") + + userIds?.forEach { userId -> + Executor.addTask(object : ExecutorTask { + override fun onBackground(): Boolean { + val timeout3Seconds = 3000L + return QBChatService.getInstance().pingManager.pingUser(userId, timeout3Seconds) + } + + override fun onForeground(result: Boolean) { + if (result) { + val message = + "Participant with id: $userId is online. There is no need to send a VoIP notification." + Log.d(TAG, message) + } else { + sendPushMessage(userId, currentUser.fullName, sessionId, idsInLine, namesInLine, isVideoCall) + } + } + + override fun onError(exception: Exception) { + shortToast(exception.message.toString()) + } + }) + } CallActivity.start(this, false) } - private fun getIdsSelectedOpponents(selectedUsers: Collection): ArrayList { - val opponentsIds = ArrayList() - if (!selectedUsers.isEmpty()) { - for (qbUser in selectedUsers) { - opponentsIds.add(qbUser.id) - } + private fun getOpponentIds(opponents: Collection): ArrayList { + val opponentIds = ArrayList() + for (qbUser in opponents) { + opponentIds.add(qbUser.id) } - return opponentsIds + return opponentIds } private fun updateActionBar(countSelectedUsers: Int) { @@ -306,49 +320,44 @@ class OpponentsActivity : BaseActivity() { } private fun initDefaultActionBar() { - val currentUserFullName = SharedPrefsHelper.getQbUser().fullName + val currentUserFullName = SharedPrefsHelper.getCurrentUser().fullName supportActionBar?.title = "" supportActionBar?.subtitle = getString(R.string.subtitle_text_logged_in_as, currentUserFullName) } private fun logout() { Log.d(TAG, "Removing User data, and Logout") - LoginService.logout(this) - QBUsers.signOut().performAsync(object : QBEntityCallback{ + LoginService.logoutFromChat(this) + LoginService.destroyRTCClient(this) + QBUsers.signOut().performAsync(object : QBEntityCallback { override fun onSuccess(v: Void?, b: Bundle?) { removeAllUserData() startLoginActivity() } - override fun onError(e: QBResponseException?) { - showErrorSnackbar(R.string.dlg_error, e as Exception, object : View.OnClickListener{ - override fun onClick(v: View?) { + override fun onError(e: QBResponseException) { + showErrorSnackbar(R.string.dlg_error, e) { + unsubscribeFromPushes { logout() } - }) + } } }) } - private fun unsubscribeFromPushesAndLogout() { + private fun unsubscribeFromPushes(callback: () -> Unit) { if (QBPushManager.getInstance().isSubscribedToPushes) { - QBPushManager.getInstance().addListener(object : QBPushSubscribeListenerImpl(){ - override fun onSubscriptionDeleted(success: Boolean) { - Log.d(TAG, "Subscription Deleted") - QBPushManager.getInstance().removeListener(this) - logout() - } - }) + QBPushManager.getInstance().addListener(SubscribeListener(TAG, callback)) SubscribeService.unSubscribeFromPushes(this@OpponentsActivity) } else { - logout() + callback() } } private fun removeAllUserData() { SharedPrefsHelper.clearAllData() QbUsersDbManager.clearDB() - signOut() + QBUsers.signOut().performAsync(null) } private fun startLoginActivity() { @@ -357,7 +366,6 @@ class OpponentsActivity : BaseActivity() { } private inner class ScrollListener(val layoutManager: LinearLayoutManager) : RecyclerView.OnScrollListener() { - override fun onScrolled(recyclerView: RecyclerView, dx: Int, dy: Int) { if (!isLoading && hasNextPage && dy > 0) { val visibleItemCount = layoutManager.childCount @@ -372,17 +380,32 @@ class OpponentsActivity : BaseActivity() { } } - private open inner class QBPushSubscribeListenerImpl : QBPushManager.QBSubscribeListener { - override fun onSubscriptionCreated() { - + private inner class SubscribeListener(val tag: String?, val callback: () -> Unit) : + QBPushManager.QBSubscribeListener { + override fun onSubscriptionDeleted(success: Boolean) { + QBPushManager.getInstance().removeListener(this) + callback() } - override fun onSubscriptionError(e: Exception?, i: Int) { + override fun onSubscriptionCreated() { + // empty + } + override fun onSubscriptionError(p0: Exception?, p1: Int) { + // empty } - override fun onSubscriptionDeleted(b: Boolean) { + override fun equals(other: Any?): Boolean { + if (other is SubscribeListener) { + return tag == other.tag + } + return false + } + override fun hashCode(): Int { + var hash = 1 + hash = 31 * hash + tag.hashCode() + return hash } } } \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/SettingsActivity.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/SettingsActivity.kt deleted file mode 100644 index 0b502ade2..000000000 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/SettingsActivity.kt +++ /dev/null @@ -1,81 +0,0 @@ -package com.quickblox.sample.videochat.kotlin.activities - -import android.content.Context -import android.content.Intent -import android.content.SharedPreferences -import android.os.Bundle -import android.preference.EditTextPreference -import com.quickblox.sample.videochat.kotlin.R -import com.quickblox.sample.videochat.kotlin.fragments.SettingsFragment -import com.quickblox.sample.videochat.kotlin.utils.longToast -import com.quickblox.sample.videochat.kotlin.view.SeekBarPreference - -private const val MAX_VIDEO_START_BITRATE = 2000 - -class SettingsActivity : BaseActivity(), SharedPreferences.OnSharedPreferenceChangeListener { - - private lateinit var bitrateStringKey: String - private lateinit var settingsFragment: SettingsFragment - - companion object { - fun start(context: Context) = context.startActivity(Intent(context, SettingsActivity::class.java)) - } - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - supportActionBar?.title = getString(R.string.actionbar_title_settings) - - // Display the fragment as the main content. - settingsFragment = SettingsFragment() - fragmentManager.beginTransaction() - .replace(android.R.id.content, settingsFragment) - .commit() - bitrateStringKey = getString(R.string.pref_startbitratevalue_key) - } - - override fun onResume() { - super.onResume() - val sharedPreferences = settingsFragment.preferenceScreen.sharedPreferences - sharedPreferences.registerOnSharedPreferenceChangeListener(this) - } - - override fun onPause() { - super.onPause() - val sharedPreferences = settingsFragment.preferenceScreen.sharedPreferences - sharedPreferences.unregisterOnSharedPreferenceChangeListener(this) - } - - override fun onSharedPreferenceChanged(sharedPreferences: SharedPreferences?, key: String?) { - if (key == bitrateStringKey) { - val bitrateValue = sharedPreferences?.getInt(bitrateStringKey, Integer.parseInt(getString(R.string.pref_startbitratevalue_default)))!! - if (bitrateValue == 0) { - setDefaultstartingBitrate(sharedPreferences) - return - } - if (bitrateValue > MAX_VIDEO_START_BITRATE) { - longToast("Max value is:$MAX_VIDEO_START_BITRATE") - setDefaultstartingBitrate(sharedPreferences) - } - } - } - - private fun setDefaultstartingBitrate(sharedPreferences: SharedPreferences) { - val editor = sharedPreferences.edit() - editor.putInt(bitrateStringKey, - Integer.parseInt(getString(R.string.pref_startbitratevalue_default))) - editor.apply() - updateSummary(sharedPreferences, bitrateStringKey) - } - - private fun updateSummary(sharedPreferences: SharedPreferences, key: String) { - val updatedPref = settingsFragment.findPreference(key) - // Set summary to be the user-description for the selected value - if (updatedPref is EditTextPreference) { - updatedPref.text = sharedPreferences.getString(key, "") - } else if (updatedPref is SeekBarPreference) { - updatedPref.setSummary(sharedPreferences.getInt(key, 0).toString()) - } else { - updatedPref?.summary = sharedPreferences.getString(key, "") - } - } -} \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/SplashActivity.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/SplashActivity.kt index 8d45c239e..1388034fd 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/SplashActivity.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/activities/SplashActivity.kt @@ -1,6 +1,5 @@ package com.quickblox.sample.videochat.kotlin.activities -import android.content.DialogInterface import android.content.Intent import android.net.Uri import android.os.Build @@ -38,8 +37,8 @@ class SplashActivity : BaseActivity() { } private fun runNextScreen() { - if (SharedPrefsHelper.hasQbUser()) { - LoginService.start(this, SharedPrefsHelper.getQbUser()) + if (SharedPrefsHelper.hasCurrentUser()) { + LoginService.loginToChatAndInitRTCClient(this, SharedPrefsHelper.getCurrentUser()) OpponentsActivity.start(this) } else { Handler().postDelayed({ @@ -80,7 +79,7 @@ class SplashActivity : BaseActivity() { return false } else if (isMiUi() && !miOverlayChecked) { Log.e(TAG, "Xiaomi Device. Need additional Overlay Permissions") - buildMIUIOverlayPermissionAlertDialog() + showMIUIOverlayPermissionDialog() return false } } @@ -123,7 +122,7 @@ class SplashActivity : BaseActivity() { } } - fun buildMIUIOverlayPermissionAlertDialog() { + private fun showMIUIOverlayPermissionDialog() { val builder = AlertDialog.Builder(this) builder.setTitle("Additional Overlay Permission Required") builder.setIcon(R.drawable.ic_error_outline_orange_24dp) diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/adapters/OpponentsFromCallAdapter.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/adapters/OpponentsFromCallAdapter.kt index bcddd30d0..c721b10f8 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/adapters/OpponentsFromCallAdapter.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/adapters/OpponentsFromCallAdapter.kt @@ -18,12 +18,11 @@ import com.quickblox.users.model.QBUser import com.quickblox.videochat.webrtc.QBRTCTypes import com.quickblox.videochat.webrtc.view.QBRTCSurfaceView - class OpponentsFromCallAdapter(val context: Context, - val baseConversationFragment: BaseConversationFragment, - val users: List, - val width: Int, - val height: Int) : RecyclerView.Adapter() { + private val baseConversationFragment: BaseConversationFragment, + users: List, + private val width: Int, + private val height: Int) : RecyclerView.Adapter() { private val TAG = OpponentsFromCallAdapter::class.java.simpleName @@ -75,7 +74,8 @@ class OpponentsFromCallAdapter(val context: Context, override fun onBindViewHolder(holder: ViewHolder, position: Int) { val user = _opponents[position] val userID = user.id - holder.opponentsName.text = user.fullName + val name = user.fullName ?: user.login + holder.opponentsName.text = name holder.getOpponentView().id = user.id holder.setUserId(userID) @@ -103,10 +103,10 @@ class OpponentsFromCallAdapter(val context: Context, init { itemView.setOnClickListener(this) - opponentsName = itemView.findViewById(R.id.opponent_name) as TextView - connectionStatus = itemView.findViewById(R.id.connection_status) as TextView - opponentView = itemView.findViewById(R.id.opponent_view) as QBRTCSurfaceView - progressBar = itemView.findViewById(R.id.progress_bar_adapter) as ProgressBar + opponentsName = itemView.findViewById(R.id.opponent_name) + connectionStatus = itemView.findViewById(R.id.connection_status) + opponentView = itemView.findViewById(R.id.opponent_view) + progressBar = itemView.findViewById(R.id.progress_bar_adapter) } fun setListener(viewHolderClickListener: ViewHolderClickListener) { diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/adapters/UsersAdapter.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/adapters/UsersAdapter.kt index 4f3742c25..ed53827a5 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/adapters/UsersAdapter.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/adapters/UsersAdapter.kt @@ -12,25 +12,22 @@ import com.quickblox.sample.videochat.kotlin.utils.getColoredCircleDrawable import com.quickblox.users.model.QBUser import kotlinx.android.synthetic.main.item_opponents_list.view.* +class UsersAdapter(private val context: Context, private var usersList: ArrayList) : + RecyclerView.Adapter() { -class UsersAdapter : RecyclerView.Adapter { - - val context: Context - private var usersList: ArrayList private val _selectedUsers: MutableList val selectedUsers: List get() = _selectedUsers private lateinit var selectedItemsCountsChangedListener: SelectedItemsCountsChangedListener - constructor(context: Context, usersList: ArrayList) : super() { - this.context = context - this.usersList = usersList + init { this._selectedUsers = ArrayList() } override fun onBindViewHolder(holder: ViewHolder, position: Int) { val user = usersList[position] - holder.opponentName.text = user.fullName + val name = user.fullName ?: user.login + holder.opponentName.text = name if (_selectedUsers.contains(user)) { holder.rootLayout.setBackgroundResource(R.color.background_color_selected_user_item) holder.opponentIcon.setBackgroundDrawable( diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/executor/Executor.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/executor/Executor.kt new file mode 100644 index 000000000..ca0942fef --- /dev/null +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/executor/Executor.kt @@ -0,0 +1,44 @@ +package com.quickblox.sample.videochat.kotlin.executor + +import android.os.Handler +import android.os.Looper +import java.util.concurrent.BlockingQueue +import java.util.concurrent.LinkedBlockingQueue +import java.util.concurrent.ThreadPoolExecutor +import java.util.concurrent.TimeUnit + +private const val THREAD_POOL_SIZE = 3 +private const val MAX_POOL_SIZE = 3 +private const val KEEP_ALIVE_TIME = 1L + +object Executor { + private var threadPoolExecutor: ThreadPoolExecutor? = null + private val threadQueue: BlockingQueue = LinkedBlockingQueue() + + init { + threadPoolExecutor = ThreadPoolExecutor( + THREAD_POOL_SIZE, + MAX_POOL_SIZE, + KEEP_ALIVE_TIME, + TimeUnit.SECONDS, + threadQueue + ) + } + + fun addTask(executorTask: ExecutorTask) { + threadPoolExecutor?.execute { + val mainHandler = Handler(Looper.getMainLooper()) + + try { + val result = executorTask.onBackground() + mainHandler.post { + executorTask.onForeground(result) + } + } catch (exception: Exception) { + mainHandler.post { + executorTask.onError(exception) + } + } + } + } +} \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/executor/ExecutorCallback.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/executor/ExecutorCallback.kt new file mode 100644 index 000000000..7883f70dd --- /dev/null +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/executor/ExecutorCallback.kt @@ -0,0 +1,10 @@ +package com.quickblox.sample.videochat.kotlin.executor + +interface ExecutorTask { + @Throws(Exception::class) + fun onBackground(): T + + fun onForeground(result: T) + + fun onError(exception: Exception) +} \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/AudioConversationFragment.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/AudioConversationFragment.kt index fb9b653f1..809773578 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/AudioConversationFragment.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/AudioConversationFragment.kt @@ -18,6 +18,7 @@ import java.util.* const val SPEAKER_ENABLED = "is_speaker_enabled" class AudioConversationFragment : BaseConversationFragment(), CallActivity.OnChangeAudioDevice { + private val TAG = AudioConversationFragment::class.simpleName private lateinit var audioSwitchToggleButton: ToggleButton private lateinit var alsoOnCallText: TextView @@ -29,6 +30,18 @@ class AudioConversationFragment : BaseConversationFragment(), CallActivity.OnCha conversationFragmentCallback?.addOnChangeAudioDeviceListener(this) } + override fun onResume() { + super.onResume() + conversationFragmentCallback?.addCallTimeUpdateListener(CallTimeUpdateListenerImpl(TAG)) + conversationFragmentCallback?.addUpdateOpponentsListener(UpdateOpponentsListenerImpl(TAG)) + } + + override fun onPause() { + super.onPause() + conversationFragmentCallback?.removeCallTimeUpdateListener(CallTimeUpdateListenerImpl(TAG)) + conversationFragmentCallback?.removeUpdateOpponentsListener(UpdateOpponentsListenerImpl(TAG)) + } + override fun configureOutgoingScreen() { val context: Context = activity as Context outgoingOpponentsRelativeLayout.setBackgroundColor(ContextCompat.getColor(context, R.color.white)) @@ -37,11 +50,10 @@ class AudioConversationFragment : BaseConversationFragment(), CallActivity.OnCha } override fun configureToolbar() { - val context: Context = activity as Context - toolbar.visibility = View.VISIBLE - toolbar.setBackgroundColor(ContextCompat.getColor(context, R.color.white)) - toolbar.setTitleTextColor(ContextCompat.getColor(context, R.color.toolbar_title_color)) - toolbar.setSubtitleTextColor(ContextCompat.getColor(context, R.color.toolbar_subtitle_color)) + toolbar?.visibility = View.VISIBLE + toolbar?.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.white)) + toolbar?.setTitleTextColor(ContextCompat.getColor(requireContext(), R.color.toolbar_title_color)) + toolbar?.setSubtitleTextColor(ContextCompat.getColor(requireContext(), R.color.toolbar_subtitle_color)) } override fun configureActionBar() { @@ -62,18 +74,19 @@ class AudioConversationFragment : BaseConversationFragment(), CallActivity.OnCha setVisibilityAlsoOnCallTextView() firstOpponentNameTextView = view.findViewById(R.id.text_caller_name) - firstOpponentNameTextView.text = opponents[0].fullName + val name = opponents[0].fullName ?: opponents[0].login + firstOpponentNameTextView.text =name - otherOpponentsTextView = view.findViewById(R.id.text_other_inc_users) - otherOpponentsTextView.text = getOtherOpponentsNames() + otherOpponentsTextView = view.findViewById(R.id.text_other_users) + otherOpponentsTextView.text = getOtherOpponentNames() audioSwitchToggleButton = view.findViewById(R.id.toggle_speaker) audioSwitchToggleButton.visibility = View.VISIBLE audioSwitchToggleButton.isChecked = SharedPrefsHelper.get(SPEAKER_ENABLED, true) actionButtonsEnabled(false) - if (conversationFragmentCallback?.isCallState() == true) { - onCallStarted() + if (conversationFragmentCallback?.isConnectedCall() == true) { + startedCall() } } @@ -83,7 +96,7 @@ class AudioConversationFragment : BaseConversationFragment(), CallActivity.OnCha } } - private fun getOtherOpponentsNames(): String { + private fun getOtherOpponentNames(): String { val otherOpponents = ArrayList() otherOpponents.addAll(opponents) otherOpponents.removeAt(0) @@ -124,17 +137,47 @@ class AudioConversationFragment : BaseConversationFragment(), CallActivity.OnCha return R.layout.fragment_audio_conversation } - override fun onOpponentsListUpdated(newUsers: ArrayList) { - super.onOpponentsListUpdated(newUsers) - firstOpponentNameTextView.text = opponents[0].fullName - otherOpponentsTextView.text = getOtherOpponentsNames() + override fun audioDeviceChanged(newAudioDevice: AppRTCAudioManager.AudioDevice) { + audioSwitchToggleButton.isChecked = newAudioDevice != AppRTCAudioManager.AudioDevice.SPEAKER_PHONE } - override fun onCallTimeUpdate(time: String) { - timerCallText.text = time + private inner class UpdateOpponentsListenerImpl(val tag: String?) : CallActivity.UpdateOpponentsListener { + override fun updatedOpponents(updatedOpponents: ArrayList) { + val name = opponents[0].fullName ?: opponents[0].login + firstOpponentNameTextView.text = name + otherOpponentsTextView.text = getOtherOpponentNames() + } + + override fun equals(other: Any?): Boolean { + if (other is UpdateOpponentsListenerImpl) { + return tag == other.tag + } + return false + } + + override fun hashCode(): Int { + var hash = 1 + hash = 31 * hash + tag.hashCode() + return hash + } } - override fun audioDeviceChanged(newAudioDevice: AppRTCAudioManager.AudioDevice) { - audioSwitchToggleButton.isChecked = newAudioDevice != AppRTCAudioManager.AudioDevice.SPEAKER_PHONE + private inner class CallTimeUpdateListenerImpl(val tag: String?) : CallActivity.CallTimeUpdateListener { + override fun updatedCallTime(time: String) { + timerCallText.text = time + } + + override fun equals(other: Any?): Boolean { + if (other is CallTimeUpdateListenerImpl) { + return tag == other.tag + } + return false + } + + override fun hashCode(): Int { + var hash = 1 + hash = 31 * hash + tag.hashCode() + return hash + } } } \ No newline at end of file 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 9707db697..144ea342f 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 @@ -10,6 +10,7 @@ import android.view.ViewGroup import android.widget.ImageButton import android.widget.TextView import android.widget.ToggleButton +import androidx.collection.arrayMapOf import com.quickblox.chat.QBChatService import com.quickblox.core.helper.StringifyArrayList import com.quickblox.sample.videochat.kotlin.R @@ -26,8 +27,8 @@ import kotlin.collections.HashMap private val TAG = BaseConversationFragment::class.java.simpleName const val MIC_ENABLED = "is_microphone_enabled" -abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.CurrentCallStateCallback { - +abstract class BaseConversationFragment : BaseToolBarFragment() { + private val TAG = BaseConversationFragment::class.simpleName private var isIncomingCall: Boolean = false protected lateinit var timerCallText: TextView protected var conversationFragmentCallback: ConversationFragmentCallback? = null @@ -40,6 +41,7 @@ abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.Cu protected lateinit var outgoingOpponentsRelativeLayout: View protected lateinit var allOpponentsTextView: TextView protected lateinit var ringingTextView: TextView + private val callStateListener = CallStateListenerImpl(TAG) companion object { fun newInstance(baseConversationFragment: BaseConversationFragment, isIncomingCall: Boolean): BaseConversationFragment { @@ -62,7 +64,8 @@ abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.Cu override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) - conversationFragmentCallback?.addCurrentCallStateListener(this) + conversationFragmentCallback?.addCallStateListener(callStateListener) + conversationFragmentCallback?.addUpdateOpponentsListener(UpdateOpponentsListenerImpl(TAG)) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -88,26 +91,26 @@ abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.Cu private fun prepareAndShowOutgoingScreen() { configureOutgoingScreen() - allOpponentsTextView.text = getUserNamesFromUsersFullNames(opponents) + allOpponentsTextView.text = getNamesFromOpponents(opponents) } - private fun getUserNamesFromUsersFullNames(allUsers: ArrayList): String { - val usersNames = StringifyArrayList() - for (user in allUsers) { - if (user.fullName != null) { - usersNames.add(user.fullName) - } else if (user.id != null) { - usersNames.add(user.id.toString()) + private fun getNamesFromOpponents(allOpponents: ArrayList): String { + val opponentNames = StringifyArrayList() + for (opponent in allOpponents) { + if (opponent.fullName != null) { + opponentNames.add(opponent.fullName) + } else if (opponent.id != null) { + opponentNames.add(opponent.id.toString()) } } - return usersNames.itemsAsString.replace(",", ", ") + return opponentNames.itemsAsString.replace(",", ", ") } protected abstract fun configureOutgoingScreen() protected open fun initFields() { if (QBChatService.getInstance().user == null) { - currentUser = SharedPrefsHelper.getQbUser() + currentUser = SharedPrefsHelper.getCurrentUser() } else { currentUser = QBChatService.getInstance().user } @@ -115,7 +118,7 @@ abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.Cu arguments?.let { isIncomingCall = it.getBoolean(EXTRA_IS_INCOMING_CALL, false) } - initOpponentsList() + initOpponents() Log.d(TAG, "opponents: $opponents") } @@ -124,22 +127,27 @@ abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.Cu if (isIncomingCall) { conversationFragmentCallback?.acceptCall(HashMap()) } else { - conversationFragmentCallback?.startCall(HashMap()) + val userInfo = arrayMapOf() + userInfo["timestamp"] = System.currentTimeMillis().toString() + conversationFragmentCallback?.startCall(userInfo) } } override fun onDestroy() { - conversationFragmentCallback?.removeCurrentCallStateListener(this) + conversationFragmentCallback?.removeCallStateListener(callStateListener) + conversationFragmentCallback?.removeUpdateOpponentsListener(UpdateOpponentsListenerImpl(TAG)) super.onDestroy() } protected open fun initViews(view: View?) { - micToggleVideoCall = view?.findViewById(R.id.toggle_mic) as ToggleButton - micToggleVideoCall.isChecked = SharedPrefsHelper.get(MIC_ENABLED, true) - handUpVideoCall = view.findViewById(R.id.button_hangup_call) as ImageButton - outgoingOpponentsRelativeLayout = view.findViewById(R.id.layout_background_outgoing_screen) - allOpponentsTextView = view.findViewById(R.id.text_outgoing_opponents_names) as TextView - ringingTextView = view.findViewById(R.id.text_ringing) as TextView + view?.let { + micToggleVideoCall = it.findViewById(R.id.toggle_mic) + micToggleVideoCall.isChecked = SharedPrefsHelper[MIC_ENABLED, true] + handUpVideoCall = it.findViewById(R.id.button_hangup_call) + outgoingOpponentsRelativeLayout = it.findViewById(R.id.layout_background_outgoing_screen) + allOpponentsTextView = it.findViewById(R.id.text_outgoing_opponents_names) + ringingTextView = it.findViewById(R.id.text_ringing) + } if (isIncomingCall) { hideOutgoingScreen() @@ -170,7 +178,6 @@ abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.Cu protected open fun actionButtonsEnabled(inability: Boolean) { micToggleVideoCall.isEnabled = inability - // inactivate toggle buttons micToggleVideoCall.isActivated = inability } @@ -185,43 +192,26 @@ abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.Cu outgoingOpponentsRelativeLayout.visibility = View.GONE } - override fun onCallStarted() { - hideOutgoingScreen() - startTimer() - actionButtonsEnabled(true) - } - - override fun onCallStopped() { - isStarted = false - clearButtonsState() - actionButtonsEnabled(false) - } - - override fun onOpponentsListUpdated(newUsers: ArrayList) { - initOpponentsList() - } - - private fun initOpponentsList() { - Log.v("UPDATE_USERS", "super initOpponentsList()") - val opponnentsIds = conversationFragmentCallback?.getOpponents() - opponnentsIds?.let { + private fun initOpponents() { + val opponentIds = conversationFragmentCallback?.getOpponents() + opponentIds?.let { val usersFromDb = QbUsersDbManager.getUsersByIds(it) - opponents = getListAllUsersFromIds(usersFromDb, it) - } - - var caller = QbUsersDbManager.getUserById(conversationFragmentCallback?.getCallerId()) - if (caller == null) { - caller = QBUser(conversationFragmentCallback?.getCallerId()) - caller.fullName = conversationFragmentCallback?.getCallerId().toString() + opponents = checkAndModifyOpponents(usersFromDb, it) } if (isIncomingCall) { + var caller = QbUsersDbManager.getUserById(conversationFragmentCallback?.getCallerId()) + if (caller == null) { + caller = QBUser(conversationFragmentCallback?.getCallerId()) + caller.fullName = conversationFragmentCallback?.getCallerId().toString() + } + opponents.add(caller) opponents.remove(QBChatService.getInstance().user) } } - private fun getListAllUsersFromIds(existedUsers: ArrayList, allIds: List): ArrayList { + private fun checkAndModifyOpponents(existedUsers: ArrayList, allIds: List): ArrayList { val qbUsers = ArrayList() for (userId in allIds) { val stubUser = createStubUserById(userId) @@ -242,4 +232,55 @@ abstract class BaseConversationFragment : BaseToolBarFragment(), CallActivity.Cu fun getConnectionState(userId: Int): QBRTCTypes.QBRTCConnectionState? { return conversationFragmentCallback?.getPeerChannel(userId) } + + protected fun startedCall() { + callStateListener.startedCall() + } + + private inner class CallStateListenerImpl(val tag: String?) : CallActivity.CallStateListener { + override fun startedCall() { + hideOutgoingScreen() + startTimer() + actionButtonsEnabled(true) + } + + override fun stoppedCall() { + isStarted = false + clearButtonsState() + actionButtonsEnabled(false) + } + + override fun equals(other: Any?): Boolean { + if (other is CallStateListenerImpl) { + return tag == other.tag + } + return false + } + + override fun hashCode(): Int { + var hash = 1 + hash = 31 * hash + tag.hashCode() + return hash + } + } + + private inner class UpdateOpponentsListenerImpl(val tag: String?) : CallActivity.UpdateOpponentsListener { + override fun updatedOpponents(updatedOpponents: ArrayList) { + initOpponents() + allOpponentsTextView.text = getNamesFromOpponents(opponents) + } + + override fun equals(other: Any?): Boolean { + if (other is UpdateOpponentsListenerImpl) { + return tag == other.tag + } + return false + } + + override fun hashCode(): Int { + var hash = 1 + hash = 31 * hash + tag.hashCode() + return hash + } + } } \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/BaseToolBarFragment.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/BaseToolBarFragment.kt index 83954fcdf..0f8663b5c 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/BaseToolBarFragment.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/BaseToolBarFragment.kt @@ -13,23 +13,18 @@ import androidx.fragment.app.Fragment import com.quickblox.sample.videochat.kotlin.R import java.lang.ref.WeakReference - abstract class BaseToolBarFragment : Fragment() { - protected lateinit var actionBar: ActionBar - protected lateinit var toolbar: Toolbar - - protected var mainHandler: Handler + protected var toolbar: Toolbar? = null - init { - mainHandler = FragmentLifeCycleHandler(this) - } + protected var mainHandler: Handler? = null internal abstract fun getFragmentLayout(): Int override fun onCreate(savedInstanceState: Bundle?) { setHasOptionsMenu(true) super.onCreate(savedInstanceState) + mainHandler = FragmentLifeCycleHandler(this) } override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { @@ -39,7 +34,7 @@ abstract class BaseToolBarFragment : Fragment() { } private fun initActionBar() { - toolbar = activity?.findViewById(R.id.toolbar_call) as Toolbar + toolbar = activity?.findViewById(R.id.toolbar_call) (activity as AppCompatActivity).setSupportActionBar(toolbar) actionBar = (activity as AppCompatActivity).delegate.supportActionBar as ActionBar diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/ConversationFragmentCallback.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/ConversationFragmentCallback.kt index e8cd55dda..65fb0ab00 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/ConversationFragmentCallback.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/ConversationFragmentCallback.kt @@ -11,7 +11,6 @@ import com.quickblox.videochat.webrtc.view.QBRTCVideoTrack import org.jivesoftware.smack.ConnectionListener import org.webrtc.CameraVideoCapturer - interface ConversationFragmentCallback { fun addConnectionListener(connectionCallback: ConnectionListener?) @@ -30,9 +29,17 @@ interface ConversationFragmentCallback { fun removeSessionEventsListener(eventsCallback: QBRTCSessionEventsCallback?) - fun addCurrentCallStateListener(currentCallStateCallback: CallActivity.CurrentCallStateCallback?) + fun addCallStateListener(callStateListener: CallActivity.CallStateListener) + + fun removeCallStateListener(callStateListener: CallActivity.CallStateListener) + + fun addUpdateOpponentsListener(updateOpponentsListener: CallActivity.UpdateOpponentsListener) + + fun removeUpdateOpponentsListener(updateOpponentsListener: CallActivity.UpdateOpponentsListener) + + fun addCallTimeUpdateListener(callTimeUpdateListener: CallActivity.CallTimeUpdateListener) - fun removeCurrentCallStateListener(currentCallStateCallback: CallActivity.CurrentCallStateCallback?) + fun removeCallTimeUpdateListener(callTimeUpdateListener: CallActivity.CallTimeUpdateListener) fun addOnChangeAudioDeviceListener(onChangeDynamicCallback: CallActivity.OnChangeAudioDevice?) @@ -66,7 +73,7 @@ interface ConversationFragmentCallback { fun isMediaStreamManagerExist(): Boolean - fun isCallState(): Boolean + fun isConnectedCall(): Boolean fun getVideoTrackMap(): MutableMap 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 e12090bc9..33df13e9b 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 @@ -1,6 +1,5 @@ package com.quickblox.sample.videochat.kotlin.fragments -import android.app.Activity import android.content.Context import android.graphics.drawable.Drawable import android.os.Bundle @@ -15,6 +14,7 @@ import android.widget.ImageButton import android.widget.ImageView import android.widget.ProgressBar import android.widget.TextView +import androidx.appcompat.widget.Toolbar import androidx.fragment.app.Fragment import com.quickblox.chat.QBChatService import com.quickblox.core.QBEntityCallback @@ -23,8 +23,8 @@ import com.quickblox.core.helper.StringifyArrayList import com.quickblox.core.request.GenericQueryRule import com.quickblox.core.request.QBPagedRequestBuilder import com.quickblox.sample.videochat.kotlin.R +import com.quickblox.sample.videochat.kotlin.activities.CallActivity import com.quickblox.sample.videochat.kotlin.db.QbUsersDbManager -import com.quickblox.sample.videochat.kotlin.util.loadUsersByPagedRequestBuilder import com.quickblox.sample.videochat.kotlin.utils.RingtonePlayer import com.quickblox.sample.videochat.kotlin.utils.WebRtcSessionManager import com.quickblox.sample.videochat.kotlin.utils.getColorCircleDrawable @@ -33,7 +33,6 @@ import com.quickblox.users.model.QBUser import com.quickblox.videochat.webrtc.QBRTCSession import com.quickblox.videochat.webrtc.QBRTCTypes import java.io.Serializable -import java.util.* import java.util.concurrent.TimeUnit private const val PER_PAGE_SIZE_100 = 100 @@ -51,13 +50,14 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { private lateinit var alsoOnCallText: TextView private lateinit var progressUserName: ProgressBar private lateinit var callerNameTextView: TextView - - private var opponentsIds: List? = null + private var otherUsersTextView: TextView? = null + private var opponentIds: List? = null private var vibrator: Vibrator? = null private var conferenceType: QBRTCTypes.QBConferenceType? = null private var lastClickTime = 0L private lateinit var ringtonePlayer: RingtonePlayer private lateinit var incomeCallFragmentCallbackListener: IncomeCallFragmentCallbackListener + private var currentSession: QBRTCSession? = null override fun onAttach(context: Context) { @@ -76,6 +76,16 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { super.onCreate(savedInstanceState) } + override fun onResume() { + super.onResume() + (requireActivity() as CallActivity).addUpdateOpponentsListener(UpdateOpponentsListenerImpl(TAG)) + } + + override fun onPause() { + super.onPause() + (requireActivity() as CallActivity).removeUpdateOpponentsListener(UpdateOpponentsListenerImpl(TAG)) + } + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = inflater.inflate(R.layout.fragment_income_call, container, false) @@ -97,14 +107,14 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { currentSession = WebRtcSessionManager.getCurrentSession() currentSession?.let { - opponentsIds = it.opponents + opponentIds = it.opponents conferenceType = it.conferenceType Log.d(TAG, conferenceType.toString() + "From onCreateView()") } } private fun hideToolBar() { - val toolbar = activity?.findViewById(R.id.toolbar_call) + val toolbar: Toolbar? = activity?.findViewById(R.id.toolbar_call) toolbar?.visibility = View.GONE } @@ -120,9 +130,9 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { private fun initUI(view: View) { callTypeTextView = view.findViewById(R.id.call_type) - val callerAvatarImageView = view.findViewById(R.id.image_caller_avatar) + val callerAvatarImageView: ImageView = view.findViewById(R.id.image_caller_avatar) callerNameTextView = view.findViewById(R.id.text_caller_name) - val otherIncUsersTextView = view.findViewById(R.id.text_other_inc_users) + otherUsersTextView = view.findViewById(R.id.text_other_users) progressUserName = view.findViewById(R.id.progress_bar_opponent_name) alsoOnCallText = view.findViewById(R.id.text_also_on_call) rejectButton = view.findViewById(R.id.image_button_reject_call) @@ -134,14 +144,15 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { val callerUser = QbUsersDbManager.getUserById(currentSession?.callerID) - if (callerUser != null && !TextUtils.isEmpty(callerUser.fullName)) { - callerNameTextView.text = callerUser.fullName + if (callerUser != null) { + val name = callerUser.fullName ?: callerUser.login + callerNameTextView.text = name } else { callerNameTextView.text = currentSession?.callerID.toString() updateUserFromServer() } - otherIncUsersTextView.text = getOtherIncUsersNames() + otherUsersTextView?.text = getOtherIncUsersNames() setVisibilityAlsoOnCallTextView() } @@ -149,22 +160,23 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { private fun updateUserFromServer() { progressUserName.visibility = View.VISIBLE - val callerID = currentSession?.callerID!! - QBUsers.getUser(callerID).performAsync(object : QBEntityCallback { - override fun onSuccess(qbUser: QBUser?, b: Bundle?) { - if (qbUser != null) { - QbUsersDbManager.saveUser(qbUser) - val callerName = if (TextUtils.isEmpty(qbUser.fullName)) qbUser.login else qbUser.fullName - callerNameTextView.text = callerName + currentSession?.let { + QBUsers.getUser(it.callerID).performAsync(object : QBEntityCallback { + override fun onSuccess(qbUser: QBUser?, bundle: Bundle?) { + if (qbUser != null) { + QbUsersDbManager.saveUser(qbUser) + val callerName = if (TextUtils.isEmpty(qbUser.fullName)) qbUser.login else qbUser.fullName + callerNameTextView.text = callerName + } + progressUserName.visibility = View.GONE } - progressUserName.visibility = View.GONE - } - override fun onError(e: QBResponseException?) { - progressUserName.visibility = View.GONE - e?.printStackTrace() - } - }) + override fun onError(e: QBResponseException?) { + progressUserName.visibility = View.GONE + e?.printStackTrace() + } + }) + } val rules = ArrayList() rules.add(GenericQueryRule(ORDER_RULE, ORDER_DESC_UPDATED)) @@ -172,12 +184,13 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { requestBuilder.rules = rules requestBuilder.perPage = PER_PAGE_SIZE_100 - loadUsersByPagedRequestBuilder(object : QBEntityCallback> { + QBUsers.getUsers(requestBuilder).performAsync(object : QBEntityCallback> { override fun onSuccess(users: ArrayList, params: Bundle?) { QbUsersDbManager.saveAllUsers(users, true) - var callerUser: QBUser? = QbUsersDbManager.getUserById(currentSession?.callerID) - if (callerUser != null && !TextUtils.isEmpty(callerUser.fullName)) { - callerNameTextView.text = callerUser.fullName + val callerUser: QBUser? = QbUsersDbManager.getUserById(currentSession?.callerID) + if (callerUser != null) { + val name = callerUser.fullName ?: callerUser.login + callerNameTextView.text = name } progressUserName.visibility = View.GONE } @@ -185,11 +198,11 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { override fun onError(e: QBResponseException?) { progressUserName.visibility = View.GONE } - }, requestBuilder) + }) } private fun setVisibilityAlsoOnCallTextView() { - opponentsIds?.let { + opponentIds?.let { if (it.size < 2) { alsoOnCallText.visibility = View.INVISIBLE } @@ -222,13 +235,13 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { private fun getOtherIncUsersNames(): String { var result = "" - opponentsIds?.let { + opponentIds?.let { val usersFromDb = QbUsersDbManager.getUsersByIds(it) val opponents = ArrayList() - opponents.addAll(getListAllUsersFromIds(usersFromDb, it)) + opponents.addAll(getAllUsersFromIds(usersFromDb, it)) opponents.remove(QBChatService.getInstance().user) - Log.d(TAG, "opponentsIds = $opponentsIds") + Log.d(TAG, "opponentIds = $opponentIds") result = makeStringFromUsersFullNames(opponents) } return result @@ -247,7 +260,7 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { return usersNames.itemsAsString.replace(",", ", ") } - fun getListAllUsersFromIds(existedUsers: ArrayList, allIds: List): ArrayList { + private fun getAllUsersFromIds(existedUsers: ArrayList, allIds: List): ArrayList { val qbUsers = ArrayList() for (userId in allIds) { @@ -325,4 +338,23 @@ class IncomeCallFragment : Fragment(), Serializable, View.OnClickListener { takeButton.isEnabled = enable rejectButton.isEnabled = enable } + + private inner class UpdateOpponentsListenerImpl(val tag: String?) : CallActivity.UpdateOpponentsListener { + override fun updatedOpponents(updatedOpponents: ArrayList) { + otherUsersTextView?.text = getOtherIncUsersNames() + } + + override fun equals(other: Any?): Boolean { + if (other is UpdateOpponentsListenerImpl) { + return tag == other.tag + } + return false + } + + override fun hashCode(): Int { + var hash = 1 + hash = 31 * hash + tag.hashCode() + return hash + } + } } \ No newline at end of file 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 66d56f8ff..ef592f3e1 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 @@ -26,7 +26,7 @@ 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) - val ivPreview = view.findViewById(R.id.image_preview) + val ivPreview: ImageView = view.findViewById(R.id.image_preview) val imageDrawable = arguments?.getInt(PREVIEW_IMAGE) imageDrawable?.let { 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 dfa07de19..16863142d 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 @@ -12,13 +12,10 @@ import androidx.fragment.app.FragmentPagerAdapter import androidx.viewpager.widget.ViewPager import com.quickblox.sample.videochat.kotlin.R import com.quickblox.sample.videochat.kotlin.activities.CallActivity -import com.quickblox.users.model.QBUser - class ScreenShareFragment : BaseToolBarFragment() { private val TAG = ScreenShareFragment::class.simpleName private var onSharingEvents: OnSharingEvents? = null - private var currentCallStateCallback: CallActivity.CurrentCallStateCallback? = null companion object { fun newInstance(): ScreenShareFragment = ScreenShareFragment() @@ -31,21 +28,20 @@ class ScreenShareFragment : BaseToolBarFragment() { override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { val view = super.onCreateView(inflater, container, savedInstanceState) - val adapter = ImagesAdapter(childFragmentManager) - - val pager = view?.findViewById(R.id.pager) as ViewPager - pager.adapter = adapter + view?.let { + val adapter = ImagesAdapter(childFragmentManager) + val pager: ViewPager = it.findViewById(R.id.pager) + pager.adapter = adapter + } - val context = activity as Context - toolbar.setBackgroundColor(ContextCompat.getColor(context, R.color.white)) + toolbar?.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.white)) return view } override fun onResume() { super.onResume() - currentCallStateCallback = CurrentCallStateCallbackImpl() - (activity as CallActivity).addCurrentCallStateListener(currentCallStateCallback!!) + (activity as CallActivity).addCallTimeUpdateListener(CallTimeUpdateListenerImpl(TAG)) } override fun onCreateOptionsMenu(menu: Menu, inflater: MenuInflater) { @@ -80,14 +76,13 @@ class ScreenShareFragment : BaseToolBarFragment() { override fun onPause() { super.onPause() - currentCallStateCallback?.let { - (activity as CallActivity).removeCurrentCallStateListener(it) - } + (activity as CallActivity).removeCallTimeUpdateListener(CallTimeUpdateListenerImpl(TAG)) } class ImagesAdapter(fm: FragmentManager) : FragmentPagerAdapter(fm) { - private val images = intArrayOf(R.drawable.pres_img, R.drawable.p2p, R.drawable.group_call, R.drawable.opponents) + private val images = + intArrayOf(R.drawable.pres_img, R.drawable.p2p, R.drawable.group_call, R.drawable.opponents) override fun getCount(): Int { return images.size @@ -98,23 +93,25 @@ class ScreenShareFragment : BaseToolBarFragment() { } } - private inner class CurrentCallStateCallbackImpl : CallActivity.CurrentCallStateCallback { - override fun onCallStarted() { - + private inner class CallTimeUpdateListenerImpl(val tag: String?) : CallActivity.CallTimeUpdateListener { + override fun updatedCallTime(time: String) { + toolbar?.title = "" + val timerTextView: TextView? = toolbar?.findViewById(R.id.timer_call) + timerTextView?.visibility = View.VISIBLE + timerTextView?.text = time } - override fun onCallStopped() { - - } - - override fun onOpponentsListUpdated(newUsers: ArrayList) { + override fun equals(other: Any?): Boolean { + if (other is CallTimeUpdateListenerImpl) { + return tag == other.tag + } + return false } - override fun onCallTimeUpdate(time: String) { - toolbar.title = "" - val timerTextView = toolbar.findViewById(R.id.timer_call) - timerTextView.visibility = View.VISIBLE - timerTextView.text = time + override fun hashCode(): Int { + var hash = 1 + hash = 31 * hash + tag.hashCode() + return hash } } diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/SettingsFragment.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/SettingsFragment.kt deleted file mode 100644 index 5a8565c3b..000000000 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/fragments/SettingsFragment.kt +++ /dev/null @@ -1,27 +0,0 @@ -package com.quickblox.sample.videochat.kotlin.fragments - -import android.os.Bundle -import android.preference.PreferenceFragment -import android.view.LayoutInflater -import android.view.View -import android.view.ViewGroup -import android.widget.ListView -import com.quickblox.sample.videochat.kotlin.R - - -class SettingsFragment : PreferenceFragment() { - - override fun onCreate(savedInstanceState: Bundle?) { - super.onCreate(savedInstanceState) - addPreferencesFromResource(R.xml.preferences) - } - - override fun onCreateView(inflater: LayoutInflater?, container: ViewGroup?, savedInstanceState: Bundle?): View? { - val view = super.onCreateView(inflater, container, savedInstanceState) - if (view != null) { - val listView = view.findViewById(android.R.id.list) as ListView - listView.setPadding(0, 0, 0, 0) - } - return view - } -} \ No newline at end of file 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 f5514ac0b..9ffa4643b 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 @@ -8,10 +8,7 @@ import android.os.SystemClock import android.util.Log import android.util.SparseArray import android.view.* -import android.widget.LinearLayout -import android.widget.RelativeLayout -import android.widget.TextView -import android.widget.ToggleButton +import android.widget.* import androidx.annotation.DimenRes import androidx.core.content.ContextCompat import androidx.recyclerview.widget.LinearLayoutManager @@ -53,11 +50,11 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, private lateinit var cameraToggle: ToggleButton private var parentView: View? = null private lateinit var actionVideoButtonsLayout: LinearLayout - private lateinit var connectionStatusLocal: TextView + private lateinit var tvFullNameUser: TextView private lateinit var recyclerView: RecyclerView private var localVideoView: QBRTCSurfaceView? = null + private var containerLocalVideoView: FrameLayout? = null private var remoteFullScreenVideoView: QBRTCSurfaceView? = null - private lateinit var opponentViewHolders: SparseArray private lateinit var opponentsAdapter: OpponentsFromCallAdapter private lateinit var allOpponents: MutableList @@ -67,7 +64,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, private var optionsMenu: Menu? = null private var isRemoteShown: Boolean = false private var amountOpponents: Int = 0 - private var userIDFullScreen: Int = 0 + private var userIdFullScreen: Int = 0 private var connectionEstablished: Boolean = false private var allCallbacksInit: Boolean = false private var isCurrentCameraFront: Boolean = false @@ -98,6 +95,8 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, override fun onResume() { super.onResume() toggleCamera(cameraToggle.isChecked) + conversationFragmentCallback?.addUpdateOpponentsListener(UpdateOpponentsListenerImpl(TAG)) + conversationFragmentCallback?.addCallTimeUpdateListener(CallTimeUpdateListenerImpl(TAG)) } override fun onPause() { @@ -128,6 +127,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, isRemoteShown = false isCurrentCameraFront = true localVideoView = view.findViewById(R.id.local_video_view) + containerLocalVideoView = view.findViewById(R.id.container_local_video_view) initCorrectSizeForLocalView() localVideoView?.setZOrderMediaOverlay(true) @@ -137,8 +137,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, if (!isPeerToPeerCall) { recyclerView = view.findViewById(R.id.grid_opponents) - val context = activity!! - recyclerView.addItemDecoration(DividerItemDecoration(context, R.dimen.grid_item_divider)) + recyclerView.addItemDecoration(DividerItemDecoration(requireContext(), R.dimen.grid_item_divider)) recyclerView.setHasFixedSize(true) val columnsCount = defineColumnsCount() val layoutManager = LinearLayoutManager(activity, RecyclerView.HORIZONTAL, false) @@ -153,7 +152,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } }) } - connectionStatusLocal = view.findViewById(R.id.connection_status_local) + tvFullNameUser = view.findViewById(R.id.username_full_view) cameraToggle = view.findViewById(R.id.toggle_camera) cameraToggle.visibility = View.VISIBLE @@ -177,16 +176,15 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, allOpponents = Collections.synchronizedList(ArrayList(opponents.size)) allOpponents.addAll(opponents) - timerCallText = activity!!.findViewById(R.id.timer_call) + timerCallText = requireActivity().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)) + outgoingOpponentsRelativeLayout.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.grey_transparent_50)) + allOpponentsTextView.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) + ringingTextView.setTextColor(ContextCompat.getColor(requireContext(), R.color.white)) } override fun configureActionBar() { @@ -194,23 +192,25 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } 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)) + toolbar?.visibility = View.VISIBLE + toolbar?.setBackgroundColor(ContextCompat.getColor(requireContext(), R.color.black_transparent_50)) + toolbar?.setTitleTextColor(ContextCompat.getColor(requireContext(), R.color.white)) + toolbar?.setSubtitleTextColor(ContextCompat.getColor(requireContext(), R.color.white)) } private fun setDuringCallActionBar() { - actionBar.setDisplayShowTitleEnabled(true) - actionBar.title = currentUser.fullName - if (isPeerToPeerCall) { - actionBar.subtitle = getString(R.string.opponent, opponents[0].fullName) + actionBar.setDisplayShowTitleEnabled(false) + val user: QBUser? = if (isPeerToPeerCall) { + opponents[0] } else { - actionBar.subtitle = getString(R.string.opponents, amountOpponents.toString()) + QbUsersDbManager.getUserById(userIdFullScreen) } - actionButtonsEnabled(true) + user?.let { + val name = it.fullName ?: it.login + tvFullNameUser.text = name + actionButtonsEnabled(true) + } } private fun addListeners() { @@ -223,6 +223,8 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, conversationFragmentCallback?.removeSessionStateListener(this) conversationFragmentCallback?.removeSessionEventsListener(this) conversationFragmentCallback?.removeVideoTrackListener(this) + conversationFragmentCallback?.removeCallTimeUpdateListener(CallTimeUpdateListenerImpl(TAG)) + conversationFragmentCallback?.removeUpdateOpponentsListener(UpdateOpponentsListenerImpl(TAG)) } override fun actionButtonsEnabled(inability: Boolean) { @@ -234,10 +236,10 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, private fun restoreSession() { Log.d(TAG, "restoreSession ") - if (conversationFragmentCallback?.isCallState() == false) { + if (conversationFragmentCallback?.isConnectedCall() == false) { return } - onCallStarted() + startedCall() val videoTrackMap = conversationFragmentCallback?.getVideoTrackMap() ?: return if (videoTrackMap.isNotEmpty()) { val entryIterator = videoTrackMap.entries.iterator() @@ -249,12 +251,12 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, if (userId == currentUser.id) { Log.d(TAG, "execute restoreSession for user:$userId") - mainHandler.postDelayed({ + mainHandler?.postDelayed({ onLocalVideoTrackReceive(null, videoTrack) }, LOCAL_TRACK_INITIALIZE_DELAY) } else if (conversationFragmentCallback?.getPeerChannel(userId) != QBRTCTypes.QBRTCConnectionState.QB_RTC_CONNECTION_CLOSED) { Log.d(TAG, "execute restoreSession for user:$userId") - mainHandler.postDelayed({ + mainHandler?.postDelayed({ onConnectedToUser(null, userId) onRemoteVideoTrackReceive(null, videoTrack, userId) }, LOCAL_TRACK_INITIALIZE_DELAY) @@ -287,8 +289,10 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, gridWidth?.let { val cellSizeWidth = defineSize(it, columnsCount, itemMargin) Log.i(TAG, "onGlobalLayout : cellSize=$cellSizeWidth") - opponentsAdapter = OpponentsFromCallAdapter(context!!, this, opponents, cellSizeWidth, - resources.getDimension(R.dimen.item_height).toInt()) + opponentsAdapter = OpponentsFromCallAdapter( + requireContext(), this, opponents, cellSizeWidth, + resources.getDimension(R.dimen.item_height).toInt() + ) opponentsAdapter.setAdapterListener(this) recyclerView.adapter = opponentsAdapter } @@ -323,11 +327,6 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } } - override fun onCallStopped() { - super.onCallStopped() - Log.i(TAG, "onCallStopped") - } - override fun initButtonsListener() { super.initButtonsListener() @@ -376,7 +375,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, return } val surfaceViewRenderer = if (isLocalVideoFullScreen) { - remoteFullScreenVideoView!! + remoteFullScreenVideoView } else { localVideoView } @@ -411,11 +410,11 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, if (isPeerToPeerCall) { setDuringCallActionBar() remoteFullScreenVideoView?.let { - fillVideoView(remoteFullScreenVideoView!!, videoTrack, true) - updateVideoView(remoteFullScreenVideoView!!, false) + fillVideoView(remoteFullScreenVideoView, videoTrack, true) + updateVideoView(remoteFullScreenVideoView, false) } } else { - mainHandler.postDelayed({ setRemoteViewMultiCall(it, videoTrack) }, LOCAL_TRACK_INITIALIZE_DELAY) + mainHandler?.postDelayed({ setRemoteViewMultiCall(it, videoTrack) }, LOCAL_TRACK_INITIALIZE_DELAY) } } } @@ -430,20 +429,23 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, val connectionState = conversationFragmentCallback?.getPeerChannel(userId) val videoTrackMap = conversationFragmentCallback?.getVideoTrackMap() - if (videoTrackMap != null && !videoTrackMap.containsKey(userId) - || connectionState?.ordinal == QBRTCTypes.QBRTCConnectionState.QB_RTC_CONNECTION_CLOSED.ordinal) { + val isNotExistVideoTrack = videoTrackMap != null && !videoTrackMap.containsKey(userId) + val isConnectionStateClosed = + connectionState?.ordinal == QBRTCTypes.QBRTCConnectionState.QB_RTC_CONNECTION_CLOSED.ordinal + val holder = findHolder(userId) + if (isNotExistVideoTrack || isConnectionStateClosed || holder == null) { return } replaceUsersInAdapter(position) updateViewHolders(position) - swapUsersFullscreenToPreview(userId) + swapUsersFullscreenToPreview(holder, userId) } private fun replaceUsersInAdapter(position: Int) { val opponents = allOpponents for (qbUser in opponents) { - if (qbUser.id == userIDFullScreen) { + if (qbUser.id == userIdFullScreen) { opponentsAdapter.replaceUsers(position, qbUser) break } @@ -456,25 +458,24 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, opponentViewHolders.put(position, childViewHolder) } - private fun swapUsersFullscreenToPreview(userId: Int) { - // get opponentVideoTrack - opponent's video track from recyclerView - val videoTrackMap = conversationFragmentCallback?.getVideoTrackMap() - - val opponentVideoTrack = videoTrackMap?.get(userId) + private fun swapUsersFullscreenToPreview(holder: OpponentsFromCallAdapter.ViewHolder, userId: Int) { + val videoTrack = conversationFragmentCallback?.getVideoTrackMap()?.get(userId) + val videoTrackFullScreen = conversationFragmentCallback?.getVideoTrackMap()?.get(userIdFullScreen) - // get mainVideoTrack - opponent's video track from full screen - val mainVideoTrack = videoTrackMap?.get(userIDFullScreen) + val videoView = holder.getOpponentView() - val remoteVideoView = findHolder(userId)?.getOpponentView() - - mainVideoTrack?.let { - fillVideoView(0, remoteVideoView!!, it) - Log.d(TAG, "_remoteVideoView enabled") + videoTrack?.let { + fillVideoView(userId, remoteFullScreenVideoView, videoTrack); + val user: QBUser? = QbUsersDbManager.getUserById(userIdFullScreen) + val name = user?.fullName ?: user?.login + tvFullNameUser.text = name } - opponentVideoTrack?.let { - fillVideoView(userId, remoteFullScreenVideoView!!, it) - Log.d(TAG, "fullscreen enabled") + if (videoTrackFullScreen != null) { + fillVideoView(0, videoView, videoTrackFullScreen) + } else { + holder.getOpponentView().setBackgroundColor(Color.BLACK) + remoteFullScreenVideoView?.setBackgroundColor(Color.TRANSPARENT) } } @@ -493,20 +494,20 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, if (isRemoteShown) { Log.d(TAG, "onRemoteVideoTrackReceive User = $userID") fillVideoView(remoteVideoView, videoTrack, true) + showRecyclerView(); } else { isRemoteShown = true itemHolder.getOpponentView().release() opponentsAdapter.removeItem(itemHolder.adapterPosition) - setDuringCallActionBar() - setRecyclerViewVisibleState() remoteFullScreenVideoView?.let { fillVideoView(userID, it, videoTrack) - updateVideoView(remoteFullScreenVideoView!!, false) + updateVideoView(remoteFullScreenVideoView, false) } + setDuringCallActionBar() } } - private fun setRecyclerViewVisibleState() { + private fun showRecyclerView() { val params = recyclerView.layoutParams params.height = resources.getDimension(R.dimen.item_height).toInt() recyclerView.layoutParams = params @@ -555,10 +556,9 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, /** * @param userId set userId if it from fullscreen videoTrack */ - private fun fillVideoView(videoView: QBRTCSurfaceView?, videoTrack: QBRTCVideoTrack, - remoteRenderer: Boolean) { - videoTrack.removeRenderer(videoTrack.renderer) - videoTrack.addRenderer(videoView) + private fun fillVideoView(videoView: QBRTCSurfaceView?, videoTrack: QBRTCVideoTrack?, remoteRenderer: Boolean) { + videoTrack?.removeRenderer(videoTrack.renderer) + videoTrack?.addRenderer(videoView) if (!remoteRenderer) { updateVideoView(videoView, isCurrentCameraFront) } @@ -576,16 +576,15 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, /** * @param userId set userId if it from fullscreen videoTrack */ - private fun fillVideoView(userId: Int, videoView: QBRTCSurfaceView, videoTrack: QBRTCVideoTrack) { + private fun fillVideoView(userId: Int, videoView: QBRTCSurfaceView?, videoTrack: QBRTCVideoTrack) { if (userId != 0) { - userIDFullScreen = userId + userIdFullScreen = userId } fillVideoView(videoView, videoTrack, true) } private fun setStatusForOpponent(userId: Int?, status: String) { if (isPeerToPeerCall) { - connectionStatusLocal.text = status return } @@ -609,7 +608,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } } - private fun setProgressBarForOpponentGone(userId: Int) { + private fun hideProgressForOpponent(userId: Int) { if (isPeerToPeerCall) { return } @@ -619,10 +618,11 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } private fun setBackgroundOpponentView(userId: Int?) { - val holder = findHolder(userId) ?: return - - if (userId != userIDFullScreen) { - holder.getOpponentView().setBackgroundColor(Color.parseColor("#000000")) + if (userId != userIdFullScreen) { + val holder = findHolder(userId) ?: return + holder.getOpponentView().setBackgroundColor(Color.BLACK) + } else { + remoteFullScreenVideoView?.setBackgroundColor(Color.BLACK) } } @@ -633,7 +633,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, override fun onConnectedToUser(qbrtcSession: QBRTCSession?, userId: Int) { connectionEstablished = true setStatusForOpponent(userId, getString(R.string.text_status_connected)) - setProgressBarForOpponentGone(userId) + hideProgressForOpponent(userId) } override fun onConnectionClosedForUser(qbrtcSession: QBRTCSession, userId: Int?) { @@ -642,6 +642,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, if (!isPeerToPeerCall) { Log.d(TAG, "onConnectionClosedForUser videoTrackMap.remove(userId)= $userId") setBackgroundOpponentView(it) + hideProgressForOpponent(userId); } } } @@ -651,7 +652,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, } override fun onUserNotAnswer(session: QBRTCSession, userId: Int) { - setProgressBarForOpponentGone(userId) + hideProgressForOpponent(userId) setStatusForOpponent(userId, getString(R.string.text_status_no_answer)) } @@ -667,7 +668,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, setStatusForOpponent(userId, getString(R.string.text_status_hang_up)) Log.d(TAG, "onReceiveHangUpFromUser userId= $userId") if (!isPeerToPeerCall) { - if (userId == userIDFullScreen) { + if (userId == userIdFullScreen) { Log.d(TAG, "setAnotherUserToFullScreen call userId= $userId") setAnotherUserToFullScreen() } @@ -682,19 +683,24 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, if (opponentsAdapter.opponents.isEmpty()) { return } - for (user in opponents){ + for (user in opponents) { val videoTrack = conversationFragmentCallback?.getVideoTrack(user.id) - if (videoTrack != null){ - val userFullScreen = QbUsersDbManager.getUserById(userIDFullScreen) + videoTrack?.let { track -> + val userFullScreen = QbUsersDbManager.getUserById(userIdFullScreen) val itemHolder = findHolder(user.id) - itemHolder?.setUserId(userIDFullScreen) + + itemHolder?.setUserId(userIdFullScreen) itemHolder?.setUserName(userFullScreen?.fullName.toString()) itemHolder?.setStatus(getString(R.string.text_status_closed)) itemHolder?.getOpponentView()?.release() - + itemHolder?.adapterPosition?.let { position -> + replaceUsersInAdapter(position) + } remoteFullScreenVideoView?.let { - fillVideoView(user.id, it, videoTrack) + fillVideoView(user.id, it, track) + val name = user.fullName ?: user.login + tvFullNameUser.text = name Log.d(TAG, "fullscreen enabled") } return @@ -727,17 +733,6 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, conversationFragmentCallback?.onStartScreenSharing() } - override fun onOpponentsListUpdated(newUsers: ArrayList) { - super.onOpponentsListUpdated(newUsers) - updateAllOpponentsList(newUsers) - Log.d(TAG, "updateOpponentsList(), newUsers = $newUsers") - runUpdateUsersNames(newUsers) - } - - override fun onCallTimeUpdate(time: String) { - timerCallText.text = time - } - private fun updateAllOpponentsList(newUsers: ArrayList) { val indexList = allOpponents.indices for (index in indexList) { @@ -751,7 +746,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, private fun runUpdateUsersNames(newUsers: ArrayList) { // need delayed for synchronization with recycler parentView initialization - mainHandler.postDelayed({ + mainHandler?.postDelayed({ for (user in newUsers) { Log.d(TAG, "runUpdateUsersNames. foreach, user = " + user.fullName) updateNameForOpponent(user.id, user.fullName) @@ -792,7 +787,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, private fun hideToolBarAndButtons() { actionBar.hide() - localVideoView?.visibility = View.INVISIBLE + containerLocalVideoView?.visibility = View.INVISIBLE actionVideoButtonsLayout.visibility = View.GONE if (!isPeerToPeerCall) { shiftBottomListOpponents() @@ -801,7 +796,7 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, private fun showToolBarAndButtons() { actionBar.show() - localVideoView?.visibility = View.VISIBLE + containerLocalVideoView?.visibility = View.VISIBLE actionVideoButtonsLayout.visibility = View.VISIBLE if (!isPeerToPeerCall) { shiftMarginListOpponents() @@ -824,4 +819,44 @@ class VideoConversationFragment : BaseConversationFragment(), Serializable, recyclerView.layoutParams = params } } + + private inner class UpdateOpponentsListenerImpl(val tag: String?) : CallActivity.UpdateOpponentsListener { + override fun updatedOpponents(updatedOpponents: ArrayList) { + updateAllOpponentsList(updatedOpponents) + Log.d(TAG, "updateOpponentsList(), opponents = $updatedOpponents") + runUpdateUsersNames(updatedOpponents) + } + + override fun equals(other: Any?): Boolean { + if (other is UpdateOpponentsListenerImpl) { + return tag == other.tag + } + return false + } + + override fun hashCode(): Int { + var hash = 1 + hash = 31 * hash + tag.hashCode() + return hash + } + } + + private inner class CallTimeUpdateListenerImpl(val tag: String?) : CallActivity.CallTimeUpdateListener { + override fun updatedCallTime(time: String) { + timerCallText.text = time + } + + override fun equals(other: Any?): Boolean { + if (other is CallTimeUpdateListenerImpl) { + return tag == other.tag + } + return false + } + + override fun hashCode(): Int { + var hash = 1 + hash = 31 * hash + tag.hashCode() + return hash + } + } } \ No newline at end of file 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 5c3595b69..424770b78 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 @@ -4,7 +4,6 @@ import android.app.* import android.content.Context import android.content.Intent import android.graphics.BitmapFactory -import android.media.MediaPlayer import android.os.Binder import android.os.Build import android.os.IBinder @@ -14,7 +13,6 @@ import android.util.Log import androidx.annotation.RequiresApi import androidx.core.app.NotificationCompat import com.quickblox.chat.QBChatService -import com.quickblox.core.QBEntityCallback import com.quickblox.sample.videochat.kotlin.R import com.quickblox.sample.videochat.kotlin.activities.CallActivity import com.quickblox.sample.videochat.kotlin.db.QbUsersDbManager @@ -23,10 +21,9 @@ import com.quickblox.sample.videochat.kotlin.fragments.IS_CURRENT_CAMERA_FRONT import com.quickblox.sample.videochat.kotlin.fragments.MIC_ENABLED import com.quickblox.sample.videochat.kotlin.fragments.SPEAKER_ENABLED import com.quickblox.sample.videochat.kotlin.util.NetworkConnectionChecker -import com.quickblox.sample.videochat.kotlin.util.loadUsersByIds import com.quickblox.sample.videochat.kotlin.utils.* -import com.quickblox.users.QBUsers import com.quickblox.videochat.webrtc.* +import com.quickblox.videochat.webrtc.BaseSession.QBRTCSessionState import com.quickblox.videochat.webrtc.callbacks.* import com.quickblox.videochat.webrtc.exception.QBRTCSignalException import com.quickblox.videochat.webrtc.view.QBRTCVideoTrack @@ -39,7 +36,7 @@ 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 +const val MIN_OPPONENT_SIZE = 1 class CallService : Service() { private var TAG = CallService::class.java.simpleName @@ -60,7 +57,7 @@ class CallService : Service() { private var currentSession: QBRTCSession? = null private var expirationReconnectionTime: Long = 0 private var sharingScreenState: Boolean = false - private var isCallState: Boolean = false + private var isConnectedCall: Boolean = false private lateinit var rtcClient: QBRTCClient private val callTimerTask: CallTimerTask = CallTimerTask() @@ -153,7 +150,7 @@ class CallService : Service() { builder.setContentTitle(notificationTitle) builder.setContentText(notificationText) builder.setWhen(System.currentTimeMillis()) - builder.setSmallIcon(R.mipmap.ic_launcher) + builder.setSmallIcon(R.drawable.ic_qb_logo) val bitmapIcon = BitmapFactory.decodeResource(resources, R.mipmap.ic_launcher) builder.setLargeIcon(bitmapIcon) @@ -212,10 +209,7 @@ class CallService : Service() { rtcClient = QBRTCClient.getInstance(this) rtcClient.setCameraErrorHandler(CameraEventsListener()) - QBRTCConfig.setMaxOpponentsCount(MAX_OPPONENTS_COUNT) - QBRTCConfig.setDebugEnabled(true) - - configRTCTimers(this) + applyRTCSettings() rtcClient.prepareToProcessCalls() } @@ -414,8 +408,8 @@ class CallService : Service() { return sharingScreenState } - fun isCallMode(): Boolean { - return isCallState + fun isConnectedCall(): Boolean { + return isConnectedCall } fun getVideoTrackMap(): MutableMap { @@ -426,13 +420,13 @@ class CallService : Service() { videoTrackMap[userId] = videoTrack } - fun getVideoTrack(userId: Int): QBRTCVideoTrack? { + fun getVideoTrack(userId: Int?): QBRTCVideoTrack? { return videoTrackMap[userId] } - private fun removeVideoTrack(userId: Int) { + private fun removeVideoTrack(userId: Int?) { val videoTrack = getVideoTrack(userId) - val renderer =videoTrack?.renderer + val renderer = videoTrack?.renderer videoTrack?.removeRenderer(renderer) videoTrackMap.remove(userId) } @@ -498,11 +492,8 @@ class CallService : Service() { private inner class ConnectionListenerImpl : AbstractConnectionListener() { override fun connectionClosedOnError(e: Exception?) { - val sharedPref = PreferenceManager.getDefaultSharedPreferences(applicationContext) - val reconnectHangUpTimeMillis = getPreferenceInt(sharedPref, applicationContext, - R.string.pref_disconnect_time_interval_key, - R.string.pref_disconnect_time_interval_default_value) * 1000 - expirationReconnectionTime = System.currentTimeMillis() + reconnectHangUpTimeMillis + val HANG_UP_TIME_10_SECONDS = 10 * 1000 + expirationReconnectionTime = System.currentTimeMillis() + HANG_UP_TIME_10_SECONDS } override fun reconnectionSuccessful() { @@ -510,7 +501,7 @@ class CallService : Service() { override fun reconnectingIn(seconds: Int) { Log.i(TAG, "reconnectingIn $seconds") - if (!isCallState && expirationReconnectionTime < System.currentTimeMillis()) { + if (!isConnectedCall && expirationReconnectionTime < System.currentTimeMillis()) { hangUpCurrentSession(HashMap()) } } @@ -529,18 +520,15 @@ class CallService : Service() { stopRingtone() if (session == WebRtcSessionManager.getCurrentSession()) { val numberOpponents = session?.opponents?.size - if (numberOpponents == ONE_OPPONENT) { - currentSession?.let { - it.hangUp(HashMap()) - CallService.stop(this@CallService) + if (numberOpponents == MIN_OPPONENT_SIZE || session?.state == QBRTCSessionState.QB_RTC_SESSION_PENDING) { + if (userID?.equals(session.callerID) == true){ + currentSession?.hangUp(HashMap()) } - }else{ + } else { userID?.let { removeVideoTrack(it) } } - } else { - CallService.stop(this@CallService) } val participant = QbUsersDbManager.getUserById(userID) @@ -574,8 +562,8 @@ class CallService : Service() { override fun onSessionClosed(session: QBRTCSession?) { Log.d(TAG, "Session " + session?.sessionID + " start stop session") - stopRingtone() if (session == currentSession) { + stopRingtone() Log.d(TAG, "Stop session") CallService.stop(this@CallService) } @@ -596,7 +584,7 @@ class CallService : Service() { override fun onConnectedToUser(session: QBRTCSession?, userId: Int?) { stopRingtone() - isCallState = true + isConnectedCall = true Log.d(TAG, "onConnectedToUser() is started") startCallTimer() } @@ -604,6 +592,7 @@ 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") + removeVideoTrack(userID) } override fun onStateChanged(session: QBRTCSession?, sessionState: BaseSession.QBRTCSessionState?) { diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/LoginService.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/LoginService.kt index bbddac507..3edcd7b67 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/LoginService.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/LoginService.kt @@ -7,19 +7,17 @@ import android.content.Context import android.content.Intent import android.os.Bundle import android.os.IBinder +import android.text.TextUtils import android.util.Log import com.quickblox.chat.QBChatService import com.quickblox.chat.connections.tcp.QBTcpChatConnectionFabric import com.quickblox.chat.connections.tcp.QBTcpConfigurationBuilder import com.quickblox.core.QBEntityCallback import com.quickblox.core.exception.QBResponseException -import com.quickblox.sample.videochat.kotlin.util.ChatPingAlarmManager import com.quickblox.sample.videochat.kotlin.utils.* import com.quickblox.users.model.QBUser import com.quickblox.videochat.webrtc.QBRTCClient import com.quickblox.videochat.webrtc.QBRTCConfig -import org.jivesoftware.smackx.ping.PingFailedListener - private const val EXTRA_COMMAND_TO_SERVICE = "command_for_service" private const val EXTRA_QB_USER = "qb_user" @@ -27,6 +25,7 @@ private const val EXTRA_QB_USER = "qb_user" private const val COMMAND_NOT_FOUND = 0 private const val COMMAND_LOGIN = 1 private const val COMMAND_LOGOUT = 2 +private const val COMMAND_DESTROY_RTC_CLIENT = 3 private const val EXTRA_PENDING_INTENT = "pending_Intent" @@ -39,7 +38,7 @@ class LoginService : Service() { private var currentUser: QBUser? = null companion object { - fun start(context: Context, qbUser: QBUser, pendingIntent: PendingIntent? = null) { + fun loginToChatAndInitRTCClient(context: Context, qbUser: QBUser, pendingIntent: PendingIntent? = null) { val intent = Intent(context, LoginService::class.java) intent.putExtra(EXTRA_COMMAND_TO_SERVICE, COMMAND_LOGIN) intent.putExtra(EXTRA_QB_USER, qbUser) @@ -48,12 +47,13 @@ class LoginService : Service() { context.startService(intent) } - fun stop(context: Context) { + fun logoutFromChat(context: Context) { val intent = Intent(context, LoginService::class.java) - context.stopService(intent) + intent.putExtra(EXTRA_COMMAND_TO_SERVICE, COMMAND_LOGOUT) + context.startService(intent) } - fun logout(context: Context) { + fun destroyRTCClient(context: Context) { val intent = Intent(context, LoginService::class.java) intent.putExtra(EXTRA_COMMAND_TO_SERVICE, COMMAND_LOGOUT) context.startService(intent) @@ -71,7 +71,7 @@ class LoginService : Service() { parseIntentExtras(intent) startSuitableActions() - return Service.START_REDELIVER_INTENT + return START_REDELIVER_INTENT } private fun parseIntentExtras(intent: Intent?) { @@ -86,10 +86,10 @@ class LoginService : Service() { } private fun startSuitableActions() { - if (currentCommand == COMMAND_LOGIN) { - startLoginToChat() - } else if (currentCommand == COMMAND_LOGOUT) { - logout() + when (currentCommand) { + COMMAND_LOGIN -> loginToChatAndInitRTCClient() + COMMAND_LOGOUT -> logoutFomChat() + COMMAND_DESTROY_RTC_CLIENT -> destroyRtcClient() } } @@ -102,60 +102,46 @@ class LoginService : Service() { chatService = QBChatService.getInstance() } - private fun startLoginToChat() { + private fun loginToChatAndInitRTCClient() { if (chatService.isLoggedIn) { sendResultToActivity(true, null) } else { currentUser?.let { - loginToChat(it) + loginToChatAndInitRTCClient(it) } } } - private fun loginToChat(qbUser: QBUser) { - chatService.login(qbUser, object : QBEntityCallback { - override fun onSuccess(qbUser: QBUser?, bundle: Bundle) { + private fun loginToChatAndInitRTCClient(user: QBUser) { + chatService.login(user, object : QBEntityCallback { + override fun onSuccess(user: QBUser?, bundle: Bundle) { Log.d(TAG, "login onSuccess") - startActionsOnSuccessLogin() + initQBRTCClient() + sendResultToActivity(true, null) } override fun onError(e: QBResponseException) { Log.d(TAG, "login onError " + e.message) - val errorMessage = if (e.message != null) { - e.message - } else { - "Login error" + var errorMessage: String? = e.message + if (TextUtils.isEmpty(errorMessage)) { + errorMessage = "Login error" } sendResultToActivity(false, errorMessage) } }) } - private fun startActionsOnSuccessLogin() { - initPingListener() - initQBRTCClient() - sendResultToActivity(true, null) - } - - private fun initPingListener() { - ChatPingAlarmManager.onCreate(this) - ChatPingAlarmManager.addPingListener(PingFailedListener { Log.d(TAG, "Ping chat server failed") }) - } - private fun initQBRTCClient() { rtcClient = QBRTCClient.getInstance(applicationContext) - // Add signalling manager - chatService.videoChatWebRTCSignalingManager?.addSignalingManagerListener { qbSignaling, createdLocally -> - if (!createdLocally) { - rtcClient.addSignaling(qbSignaling) + chatService.videoChatWebRTCSignalingManager?.addSignalingManagerListener { signaling, createdLocally -> + val needAddSignaling = !createdLocally + if (needAddSignaling) { + rtcClient.addSignaling(signaling) } } - // Configure - QBRTCConfig.setDebugEnabled(true) - configRTCTimers(this) + applyRTCSettings() - // Add service as callback to RTCClient rtcClient.addSessionCallbacksListener(WebRtcSessionManager) rtcClient.prepareToProcessCalls() } @@ -175,15 +161,14 @@ class LoginService : Service() { } } - private fun logout() { - destroyRtcClientAndChat() - } - - private fun destroyRtcClientAndChat() { + private fun destroyRtcClient() { if (::rtcClient.isInitialized) { rtcClient.destroy() } - ChatPingAlarmManager.onDestroy() + stopSelf() + } + + private fun logoutFomChat() { chatService.logout(object : QBEntityCallback { override fun onSuccess(aVoid: Void?, bundle: Bundle) { chatService.destroy() @@ -197,11 +182,6 @@ class LoginService : Service() { stopSelf() } - override fun onDestroy() { - Log.d(TAG, "Service onDestroy()") - super.onDestroy() - } - override fun onBind(intent: Intent): IBinder? { Log.d(TAG, "Service onBind)") return null @@ -210,19 +190,20 @@ class LoginService : Service() { override fun onTaskRemoved(rootIntent: Intent) { Log.d(TAG, "Service onTaskRemoved()") super.onTaskRemoved(rootIntent) - if (!isCallServiceRunning()) { - logout() + if (isCallServiceNotRunning()) { + logoutFomChat() + destroyRtcClient() } } - private fun isCallServiceRunning(): Boolean { + private fun isCallServiceNotRunning(): Boolean { val manager = getSystemService(Context.ACTIVITY_SERVICE) as ActivityManager - var running = false + var notRunning = true for (service in manager.getRunningServices(Integer.MAX_VALUE)) { if (CallService::class.java.name == service.service.className) { - running = true + notRunning = false } } - return running + return notRunning } } \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/fcm/PushListenerService.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/fcm/PushListenerService.kt index aaced2995..63e281b74 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/fcm/PushListenerService.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/services/fcm/PushListenerService.kt @@ -12,10 +12,10 @@ class PushListenerService : QBFcmPushListenerService() { override fun onMessageReceived(remoteMessage: RemoteMessage?) { super.onMessageReceived(remoteMessage) - if (SharedPrefsHelper.hasQbUser()) { - val qbUser: QBUser = SharedPrefsHelper.getQbUser() - Log.d(TAG, "App has logged user" + qbUser.id) - LoginService.start(this, qbUser) + if (SharedPrefsHelper.hasCurrentUser()) { + val user: QBUser = SharedPrefsHelper.getCurrentUser() + Log.d(TAG, "App has logged user" + user.id) + LoginService.loginToChatAndInitRTCClient(this, user) } } 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 deleted file mode 100644 index 6adffca8f..000000000 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/util/ChatPingAlarmManager.kt +++ /dev/null @@ -1,88 +0,0 @@ -package com.quickblox.sample.videochat.kotlin.util - -import android.app.AlarmManager -import android.app.PendingIntent -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 -import com.quickblox.chat.QBChatService -import com.quickblox.core.QBEntityCallback -import com.quickblox.core.exception.QBResponseException -import org.jivesoftware.smackx.ping.PingFailedListener -import java.lang.ref.WeakReference -import java.util.concurrent.TimeUnit - - -private const val PING_ALARM_ACTION = "com.quickblox.chat.ping.ACTION" - -object ChatPingAlarmManager { - private val TAG = ChatPingAlarmManager::class.java.simpleName - private val PING_INTERVAL = TimeUnit.SECONDS.toMillis(60) - private lateinit var pendingIntent: PendingIntent - private lateinit var alarmManager: AlarmManager - private lateinit var context: WeakReference - private var pingFailedListener: PingFailedListener? = null - - private val alarmBroadcastReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - Log.v(TAG, "Ping Alarm broadcast received") - Log.d(TAG, "Calling pingServer for connection ") - val pingManager = QBChatService.getInstance().pingManager - pingManager?.pingServer(object : QBEntityCallback { - override fun onSuccess(result: Void?, params: Bundle) { - - } - - override fun onError(responseException: QBResponseException) { - pingFailedListener?.pingFailed() - } - }) - } - } - - /** - * Register a pending intent with the AlarmManager to be broadcasted every - * half hour and register the alarm broadcast receiver to receive this - * intent. The receiver will check all known questions if a ping is - * Necessary when invoked by the alarm intent. - * - * @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 - - 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) - } - - /** - * Unregister the alarm broadcast receiver and cancel the alarm. - */ - fun onDestroy() { - try { - context.get()?.unregisterReceiver(alarmBroadcastReceiver) - } catch (e: IllegalArgumentException) { - e.printStackTrace() - } - alarmManager.cancel(pendingIntent) - pingFailedListener = null - } - - fun addPingListener(listener: PingFailedListener) { - pingFailedListener = listener - } -} \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/util/QBResRequestExecutor.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/util/QBResRequestExecutor.kt deleted file mode 100644 index 4633da893..000000000 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/util/QBResRequestExecutor.kt +++ /dev/null @@ -1,39 +0,0 @@ -package com.quickblox.sample.videochat.kotlin.util - -import com.quickblox.core.QBEntityCallback -import com.quickblox.core.request.QBPagedRequestBuilder -import com.quickblox.users.QBUsers -import com.quickblox.users.model.QBUser -import java.util.* - -fun signUp(newQbUser: QBUser, callback: QBEntityCallback) { - QBUsers.signUp(newQbUser).performAsync(callback) -} - -fun signInUser(currentQbUser: QBUser, callback: QBEntityCallback) { - QBUsers.signIn(currentQbUser).performAsync(callback) -} - -fun signOut() { - QBUsers.signOut().performAsync(null) -} - -fun deleteCurrentUser(currentQbUserID: Int, callback: QBEntityCallback) { - QBUsers.deleteUser(currentQbUserID).performAsync(callback) -} - -fun loadUsersByTag(tag: String, callback: QBEntityCallback>) { - val requestBuilder = QBPagedRequestBuilder() - requestBuilder.perPage = 50 - val tags = LinkedList() - tags.add(tag) - QBUsers.getUsersByTags(tags, requestBuilder).performAsync(callback) -} - -fun loadUsersByPagedRequestBuilder(callback: QBEntityCallback>, requestBuilder: QBPagedRequestBuilder) { - QBUsers.getUsers(requestBuilder).performAsync(callback) -} - -fun loadUsersByIds(usersIDs: Collection, callback: QBEntityCallback>) { - QBUsers.getUsersByIDs(usersIDs, null).performAsync(callback) -} \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/Consts.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/Consts.kt index d6e571649..9a35b9c0b 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/Consts.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/Consts.kt @@ -4,7 +4,7 @@ import android.Manifest val PERMISSIONS = arrayOf(Manifest.permission.CAMERA, Manifest.permission.RECORD_AUDIO) -const val MAX_OPPONENTS_COUNT = 6 +const val MAX_OPPONENTS_COUNT = 3 const val EXTRA_LOGIN_RESULT = "login_result" @@ -12,8 +12,4 @@ const val EXTRA_LOGIN_ERROR_MESSAGE = "login_error_message" const val EXTRA_LOGIN_RESULT_CODE = 1002 -const val EXTRA_IS_INCOMING_CALL = "conversation_reason" - -const val MAX_LOGIN_LENGTH = 15 - -const val MAX_FULLNAME_LENGTH = 20 \ No newline at end of file +const val EXTRA_IS_INCOMING_CALL = "conversation_reason" \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/KeyboardUtils.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/KeyboardUtils.kt index 9d7285d05..86b21c013 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/KeyboardUtils.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/KeyboardUtils.kt @@ -1,6 +1,7 @@ package com.quickblox.sample.videochat.kotlin.utils import android.content.Context +import android.view.View import android.view.inputmethod.InputMethodManager import android.widget.EditText import com.quickblox.sample.videochat.kotlin.App @@ -10,7 +11,7 @@ fun showKeyboard(editText: EditText) { imm.showSoftInput(editText, InputMethodManager.SHOW_IMPLICIT) } -fun hideKeyboard(editText: EditText) { +fun hideKeyboard(view: View) { val imm = App.getInstance().getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager - imm.hideSoftInputFromWindow(editText.windowToken, 0) + imm.hideSoftInputFromWindow(view.windowToken, 0) } \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/PushNotificationSender.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/PushNotificationSender.kt index 46ca2caf9..2d0824e13 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/PushNotificationSender.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/PushNotificationSender.kt @@ -8,37 +8,35 @@ import com.quickblox.messages.model.QBNotificationType import com.quickblox.sample.videochat.kotlin.R import org.json.JSONException import org.json.JSONObject -import java.util.* -fun sendPushMessage(recipients: ArrayList, senderName: String, newSessionID: String, - opponentsIDs: String, opponentsNames: String, isVideoCall: Boolean) { +fun sendPushMessage(userId: Int, senderName: String, sessionId: String, + opponentIds: String, opponentNames: String, isVideoCall: Boolean) { val outMessage = String.format(R.string.text_push_notification_message.toString(), senderName) - val timeStamp = System.currentTimeMillis() - - // Send Push: create QuickBlox Push Notification Event + // send Push: create QuickBlox Push Notification Event val qbEvent = QBEvent() qbEvent.notificationType = QBNotificationType.PUSH qbEvent.environment = QBEnvironment.DEVELOPMENT - // Generic push - will be delivered to all platforms (Android, iOS, WP, Blackberry..) + // generic push - will be delivered to all platforms (Android, iOS, WP, Blackberry..) val json = JSONObject() try { json.put("message", outMessage) json.put("ios_voip", "1") json.put("VOIPCall", "1") - json.put("sessionID", newSessionID) - json.put("opponentsIDs", opponentsIDs) - json.put("contactIdentifier", opponentsNames) + json.put("sessionID", sessionId) + json.put("opponentsIDs", opponentIds) + json.put("contactIdentifier", opponentNames) json.put("conferenceType", if (isVideoCall) "1" else "2") - json.put("timestamp", timeStamp.toString()) + json.put("timestamp", System.currentTimeMillis().toString()) } catch (e: JSONException) { e.printStackTrace() } qbEvent.message = json.toString() - val userIds = StringifyArrayList(recipients) + val userIds = StringifyArrayList() + userIds.add(userId) qbEvent.userIds = userIds QBPushNotifications.createEvent(qbEvent).performAsync(null) diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/SettingsManager.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/SettingsManager.kt new file mode 100644 index 000000000..f7f5683fe --- /dev/null +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/SettingsManager.kt @@ -0,0 +1,37 @@ +package com.quickblox.sample.videochat.kotlin.utils + +import com.quickblox.videochat.webrtc.QBRTCConfig +import com.quickblox.videochat.webrtc.QBRTCMediaConfig + +fun applyMediaSettings() { + applyAudioSettings() + applyVideoSettings() +} + +private fun applyAudioSettings() { + QBRTCMediaConfig.setAudioCodec(QBRTCMediaConfig.AudioCodec.ISAC) + QBRTCMediaConfig.setUseBuildInAEC(true) + QBRTCMediaConfig.setAudioProcessingEnabled(true) + QBRTCMediaConfig.setUseOpenSLES(false) +} + +private fun applyVideoSettings() { + QBRTCMediaConfig.setVideoHWAcceleration(true) + QBRTCMediaConfig.setVideoWidth(QBRTCMediaConfig.VideoQuality.VGA_VIDEO.width) + QBRTCMediaConfig.setVideoHeight(QBRTCMediaConfig.VideoQuality.VGA_VIDEO.height) + QBRTCMediaConfig.setVideoCodec(QBRTCMediaConfig.VideoCodec.VP8) +} + +fun applyRTCSettings() { + QBRTCConfig.setMaxOpponentsCount(MAX_OPPONENTS_COUNT) + QBRTCConfig.setDebugEnabled(true) + + val ANSWER_TIME_INTERVAL_IN_SECONDS = 30L + QBRTCConfig.setAnswerTimeInterval(ANSWER_TIME_INTERVAL_IN_SECONDS) + + val DISCONNECT_TIME_IN_SECONDS = 10 + QBRTCConfig.setDisconnectTime(DISCONNECT_TIME_IN_SECONDS) + + val DIALING_TIME_INTERVAL_IN_SECONDS = 5L + QBRTCConfig.setDialingTimeInterval(DIALING_TIME_INTERVAL_IN_SECONDS) +} \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/SettingsUtil.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/SettingsUtil.kt deleted file mode 100644 index e85bb0b05..000000000 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/SettingsUtil.kt +++ /dev/null @@ -1,178 +0,0 @@ -package com.quickblox.sample.videochat.kotlin.utils - -import android.content.Context -import android.content.SharedPreferences -import android.preference.PreferenceManager -import android.util.Log -import com.quickblox.sample.videochat.kotlin.R -import com.quickblox.videochat.webrtc.QBRTCConfig -import com.quickblox.videochat.webrtc.QBRTCMediaConfig - -private val TAG = "SettingsUtil" -private const val LIMIT_MEMBERS = 4 - -private fun setSettingsForMultiCall(users: List) { - if (users.size <= LIMIT_MEMBERS) { - setDefaultVideoQuality() - } else { - //set to minimum settings - QBRTCMediaConfig.setVideoWidth(QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.width) - QBRTCMediaConfig.setVideoHeight(QBRTCMediaConfig.VideoQuality.QBGA_VIDEO.height) - QBRTCMediaConfig.setVideoHWAcceleration(false) - QBRTCMediaConfig.setVideoCodec(null) - } -} - -fun setSettingsStrategy(users: List, sharedPref: SharedPreferences, context: Context) { - setCommonSettings(sharedPref, context) - if (users.size == 1) { - setSettingsFromPreferences(sharedPref, context) - } else { - setSettingsForMultiCall(users) - } -} - -fun configRTCTimers(context: Context) { - val sharedPref = PreferenceManager.getDefaultSharedPreferences(context) - - val answerTimeInterval = getPreferenceInt(sharedPref, context, - R.string.pref_answer_time_interval_key, - R.string.pref_answer_time_interval_default_value).toLong() - QBRTCConfig.setAnswerTimeInterval(answerTimeInterval) - Log.e(TAG, "answerTimeInterval = $answerTimeInterval") - - val disconnectTimeInterval = getPreferenceInt(sharedPref, context, - R.string.pref_disconnect_time_interval_key, - R.string.pref_disconnect_time_interval_default_value) - QBRTCConfig.setDisconnectTime(disconnectTimeInterval) - Log.e(TAG, "disconnectTimeInterval = $disconnectTimeInterval") - - val dialingTimeInterval = getPreferenceInt(sharedPref, context, - R.string.pref_dialing_time_interval_key, - R.string.pref_dialing_time_interval_default_value).toLong() - QBRTCConfig.setDialingTimeInterval(dialingTimeInterval) - Log.e(TAG, "dialingTimeInterval = $dialingTimeInterval") -} - -fun isManageSpeakerPhoneByProximity(context: Context): Boolean { - val sharedPref = PreferenceManager.getDefaultSharedPreferences(context) - return sharedPref.getBoolean(context.getString(R.string.pref_manage_speakerphone_by_proximity_key), - java.lang.Boolean.valueOf(context.getString(R.string.pref_manage_speakerphone_by_proximity_default))) -} - -fun getPreferenceInt(sharedPref: SharedPreferences, context: Context, strResKey: Int, strResDefValue: Int): Int { - return sharedPref.getInt(context.getString(strResKey), Integer.valueOf(context.getString(strResDefValue))) -} - -private fun setCommonSettings(sharedPref: SharedPreferences, context: Context) { - val audioCodecDescription = getPreferenceString(sharedPref, context, R.string.pref_audiocodec_key, - R.string.pref_audiocodec_def) - val audioCodec = if (QBRTCMediaConfig.AudioCodec.ISAC.description == audioCodecDescription) { - QBRTCMediaConfig.AudioCodec.ISAC - } else { - QBRTCMediaConfig.AudioCodec.OPUS - } - Log.e(TAG, "audioCodec =: " + audioCodec.description) - QBRTCMediaConfig.setAudioCodec(audioCodec) - Log.v(TAG, "audioCodec = " + QBRTCMediaConfig.getAudioCodec()) - - // Check Disable built-in AEC flag. - val disableBuiltInAEC = getPreferenceBoolean(sharedPref, context, - R.string.pref_disable_built_in_aec_key, - R.string.pref_disable_built_in_aec_default) - QBRTCMediaConfig.setUseBuildInAEC(!disableBuiltInAEC) - Log.v(TAG, "setUseBuildInAEC = " + QBRTCMediaConfig.isUseBuildInAEC()) - - // Check Disable Audio Processing flag. - val noAudioProcessing = getPreferenceBoolean(sharedPref, context, - R.string.pref_noaudioprocessing_key, - R.string.pref_noaudioprocessing_default) - QBRTCMediaConfig.setAudioProcessingEnabled(!noAudioProcessing) - Log.v(TAG, "isAudioProcessingEnabled = " + QBRTCMediaConfig.isAudioProcessingEnabled()) - - // Check OpenSL ES enabled flag. - val useOpenSLES = getPreferenceBoolean(sharedPref, context, - R.string.pref_opensles_key, - R.string.pref_opensles_default) - QBRTCMediaConfig.setUseOpenSLES(useOpenSLES) - Log.v(TAG, "isUseOpenSLES = " + QBRTCMediaConfig.isUseOpenSLES()) -} - -private fun setSettingsFromPreferences(sharedPref: SharedPreferences, context: Context) { - - // Check HW codec flag. - val hwCodec = sharedPref.getBoolean(context.getString(R.string.pref_hwcodec_key), - java.lang.Boolean.valueOf(context.getString(R.string.pref_hwcodec_default))) - - QBRTCMediaConfig.setVideoHWAcceleration(hwCodec) - - // Get video resolution from settings. - val resolutionDefaultValue = "0" - val resolutionItem = Integer.parseInt(sharedPref.getString(context.getString(R.string.pref_resolution_key), resolutionDefaultValue) - ?: resolutionDefaultValue) - Log.e(TAG, "resolutionItem =: $resolutionItem") - setVideoQuality(resolutionItem) - Log.v(TAG, "resolution = " + QBRTCMediaConfig.getVideoHeight() + "x" + QBRTCMediaConfig.getVideoWidth()) - - // Get start bitrate. - val startBitrate = getPreferenceInt(sharedPref, context, - R.string.pref_startbitratevalue_key, - R.string.pref_startbitratevalue_default) - Log.e(TAG, "videoStartBitrate =: $startBitrate") - QBRTCMediaConfig.setVideoStartBitrate(startBitrate) - Log.v(TAG, "videoStartBitrate = " + QBRTCMediaConfig.getVideoStartBitrate()) - - val videoCodecDefaultValue = "0" - val videoCodecItem = Integer.parseInt(getPreferenceString(sharedPref, context, R.string.pref_videocodec_key, videoCodecDefaultValue) - ?: videoCodecDefaultValue) - for (codec in QBRTCMediaConfig.VideoCodec.values()) { - if (codec.ordinal == videoCodecItem) { - Log.e(TAG, "videoCodecItem =: " + codec.description) - QBRTCMediaConfig.setVideoCodec(codec) - Log.v(TAG, "videoCodecItem = " + QBRTCMediaConfig.getVideoCodec()) - break - } - } - - // Get camera fps from settings. - val cameraFps = getPreferenceInt(sharedPref, context, R.string.pref_frame_rate_key, R.string.pref_frame_rate_default) - Log.e(TAG, "cameraFps = $cameraFps") - QBRTCMediaConfig.setVideoFps(cameraFps) - Log.v(TAG, "cameraFps = " + QBRTCMediaConfig.getVideoFps()) -} - -private fun setVideoQuality(resolutionItem: Int) { - if (resolutionItem != -1) { - setVideoFromLibraryPreferences(resolutionItem) - } else { - setDefaultVideoQuality() - } -} - -private fun setDefaultVideoQuality() { - QBRTCMediaConfig.setVideoWidth(QBRTCMediaConfig.VideoQuality.VGA_VIDEO.width) - QBRTCMediaConfig.setVideoHeight(QBRTCMediaConfig.VideoQuality.VGA_VIDEO.height) -} - -private fun setVideoFromLibraryPreferences(resolutionItem: Int) { - for (quality in QBRTCMediaConfig.VideoQuality.values()) { - if (quality.ordinal == resolutionItem) { - Log.e(TAG, "resolution =: " + quality.height + ":" + quality.width) - QBRTCMediaConfig.setVideoHeight(quality.height) - QBRTCMediaConfig.setVideoWidth(quality.width) - } - } -} - -private fun getPreferenceString(sharedPref: SharedPreferences, context: Context, strResKey: Int, strResDefValue: Int): String { - val defaultValue = context.getString(strResDefValue) - return sharedPref.getString(context.getString(strResKey), defaultValue) ?: defaultValue -} - -private fun getPreferenceString(sharedPref: SharedPreferences, context: Context, strResKey: Int, strResDefValue: String): String? { - return sharedPref.getString(context.getString(strResKey), strResDefValue) -} - -private fun getPreferenceBoolean(sharedPref: SharedPreferences, context: Context, StrRes: Int, strResDefValue: Int): Boolean { - return sharedPref.getBoolean(context.getString(StrRes), java.lang.Boolean.valueOf(context.getString(strResDefValue))) -} \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/SharedPrefsHelper.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/SharedPrefsHelper.kt index b31900b20..71b1ac9f5 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/SharedPrefsHelper.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/SharedPrefsHelper.kt @@ -7,7 +7,6 @@ import com.quickblox.core.helper.StringifyArrayList import com.quickblox.sample.videochat.kotlin.App import com.quickblox.users.model.QBUser - private const val SHARED_PREFS_NAME = "qb" private const val QB_USER_ID = "qb_user_id" private const val QB_USER_LOGIN = "qb_user_login" @@ -16,8 +15,8 @@ private const val QB_USER_FULL_NAME = "qb_user_full_name" private const val QB_USER_TAGS = "qb_user_tags" object SharedPrefsHelper { - - private var sharedPreferences: SharedPreferences = App.getInstance().getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) + private var sharedPreferences: SharedPreferences = + App.getInstance().getSharedPreferences(SHARED_PREFS_NAME, Context.MODE_PRIVATE) fun delete(key: String) { if (sharedPreferences.contains(key)) { @@ -39,19 +38,19 @@ object SharedPrefsHelper { editor.apply() } - fun saveQbUser(qbUser: QBUser) { - save(QB_USER_ID, qbUser.id) - save(QB_USER_LOGIN, qbUser.login) - save(QB_USER_PASSWORD, qbUser.password) - save(QB_USER_FULL_NAME, qbUser.fullName) - save(QB_USER_TAGS, qbUser.tags.itemsAsString) + fun saveCurrentUser(user: QBUser) { + save(QB_USER_ID, user.id) + save(QB_USER_LOGIN, user.login) + save(QB_USER_PASSWORD, user.password) + save(QB_USER_FULL_NAME, user.fullName) + save(QB_USER_TAGS, user.tags.itemsAsString) } fun clearAllData() { sharedPreferences.edit().clear().apply() } - fun hasQbUser(): Boolean { + fun hasCurrentUser(): Boolean { return has(QB_USER_LOGIN) && has(QB_USER_PASSWORD) } @@ -70,7 +69,7 @@ object SharedPrefsHelper { return sharedPreferences.contains(key) } - fun getQbUser(): QBUser { + fun getCurrentUser(): QBUser { val id = get(QB_USER_ID) val login = get(QB_USER_LOGIN) val password = get(QB_USER_PASSWORD) diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/ValidationUtils.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/ValidationUtils.kt index 9ad027c1a..a95121678 100644 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/ValidationUtils.kt +++ b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/utils/ValidationUtils.kt @@ -1,42 +1,29 @@ package com.quickblox.sample.videochat.kotlin.utils -import android.content.Context import android.text.TextUtils -import android.widget.EditText -import com.quickblox.sample.videochat.kotlin.R import java.io.BufferedReader import java.io.IOException import java.io.InputStreamReader import java.util.regex.Pattern -private fun isEnteredTextValid(context: Context, editText: EditText, resFieldName: Int, maxLength: Int, checkName: Boolean): Boolean { +private fun checkTextByPattern(text: String, pattern: Pattern): Boolean { var isCorrect = false - val p: Pattern - if (checkName) { - p = Pattern.compile("^[a-zA-Z][a-zA-Z0-9]{2," + (maxLength - 1) + "}+$") - } else { - p = Pattern.compile("^[a-zA-Z][a-zA-Z 0-9]{2," + (maxLength - 1) + "}+$") - } - if (editText.text.toString().isNotBlank()) { - val m = p.matcher(editText.text.toString().trim { it <= ' ' }) - isCorrect = m.matches() - } - if (!isCorrect) { - editText.error = String.format(context.getString(R.string.error_name_must_not_contain_special_characters_from_app), - context.getString(resFieldName), - maxLength) - return false - } else { - return true + + if (text.isNotBlank()) { + val matcher = pattern.matcher(text.trim { it <= ' ' }) + isCorrect = matcher.matches() } + return isCorrect } -fun isLoginValid(context: Context, editText: EditText): Boolean { - return isEnteredTextValid(context, editText, R.string.field_name_user_login, MAX_LOGIN_LENGTH, true) +fun isLoginValid(login: String): Boolean { + val pattern = Pattern.compile("^[a-zA-Z][a-zA-Z0-9]{2,49}+$") + return checkTextByPattern(login, pattern) } -fun isFoolNameValid(context: Context, editText: EditText): Boolean { - return isEnteredTextValid(context, editText, R.string.field_name_user_fullname, MAX_FULLNAME_LENGTH, false) +fun isDisplayNameValid(displayName: String): Boolean { + val pattern = Pattern.compile("^[a-zA-Z][a-zA-Z 0-9]{2,19}+$") + return checkTextByPattern(displayName, pattern) } fun isMiUi(): Boolean { diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/view/ListPreferenceCompat.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/view/ListPreferenceCompat.kt deleted file mode 100644 index 5971b8598..000000000 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/view/ListPreferenceCompat.kt +++ /dev/null @@ -1,34 +0,0 @@ -package com.quickblox.sample.videochat.kotlin.view - -import android.annotation.TargetApi -import android.content.Context -import android.os.Build -import android.preference.ListPreference -import android.text.TextUtils -import android.util.AttributeSet - - -class ListPreferenceCompat : ListPreference { - - constructor(context: Context) : super(context) - - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int, defStyleRes: Int) : super(context, attrs, defStyleAttr, defStyleRes) - - @TargetApi(Build.VERSION_CODES.LOLLIPOP) - constructor(context: Context, attrs: AttributeSet, defStyleAttr: Int) : super(context, attrs, defStyleAttr) - - override fun setValue(value: String) { - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) { - super.setValue(value) - } else { - val oldValue = getValue() - super.setValue(value) - if (!TextUtils.equals(value, oldValue)) { - notifyChanged() - } - } - } -} \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/view/SeekBarPreference.kt b/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/view/SeekBarPreference.kt deleted file mode 100644 index 909227ad1..000000000 --- a/sample-videochat-kotlin/app/src/main/java/com/quickblox/sample/videochat/kotlin/view/SeekBarPreference.kt +++ /dev/null @@ -1,101 +0,0 @@ -package com.quickblox.sample.videochat.kotlin.view - -import android.content.Context -import android.content.res.TypedArray -import android.preference.Preference -import android.util.AttributeSet -import android.util.Log -import android.view.View -import android.widget.SeekBar -import com.quickblox.sample.videochat.kotlin.R - -private const val ANDROID_NS = "http://schemas.android.com/apk/res/android" -private const val SEEKBAR_NS = "http://schemas.android.com/apk/res-auto" - -class SeekBarPreference : Preference, SeekBar.OnSeekBarChangeListener { - - private lateinit var seekBar: SeekBar - private var progress: Int = 0 - private var maxSeekBarValue: Int = 0 - private var minSeekBarValue: Int = 0 - private var seekBarStepSize: Int = 0 - - constructor(context: Context, attrs: AttributeSet) : super(context, attrs) { - layoutResource = R.layout.seekbar_preference - initFields(context, attrs) - } - - constructor(context: Context, attrs: AttributeSet, defStyle: Int) : super(context, attrs, defStyle) { - layoutResource = R.layout.seekbar_preference - initFields(context, attrs) - } - - private fun initFields(context: Context, attrs: AttributeSet) { - val maxValueResourceId = attrs.getAttributeResourceValue(ANDROID_NS, "max", R.integer.pref_default_int_value) - maxSeekBarValue = context.resources.getInteger(maxValueResourceId) - - val minValueResourceId = attrs.getAttributeResourceValue(SEEKBAR_NS, "min", R.integer.pref_default_int_value) - minSeekBarValue = context.resources.getInteger(minValueResourceId) - - val stepSizeValueResourceId = attrs.getAttributeResourceValue(SEEKBAR_NS, "stepSize", R.integer.pref_seekbar_step_default_int_value) - seekBarStepSize = context.resources.getInteger(stepSizeValueResourceId) - - Log.v("Attribute", "max = $maxSeekBarValue") - Log.v("Attribute", "min = $minSeekBarValue") - Log.v("Attribute", "step = $seekBarStepSize") - } - - override fun onBindView(view: View) { - super.onBindView(view) - seekBar = view.findViewById(R.id.seekbar) - seekBar.max = maxSeekBarValue - seekBar.progress = progress - seekBar.setOnSeekBarChangeListener(this) - } - - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - var changedProgress = progress / seekBarStepSize * seekBarStepSize - - if (changedProgress <= minSeekBarValue) { - changedProgress += minSeekBarValue - } - - setValue(changedProgress) - } - } - - override fun onStartTrackingTouch(seekBar: SeekBar) { - - } - - override fun onStopTrackingTouch(seekBar: SeekBar) { - - } - - override fun onSetInitialValue(restoreValue: Boolean, defaultValue: Any?) { - val value = if (restoreValue) { - getPersistedInt(progress) - } else { - defaultValue - } - setValue(value as Int) - } - - private fun setValue(value: Int) { - if (shouldPersist()) { - persistInt(value) - } - - if (value != progress) { - progress = value - notifyChanged() - } - - summary = progress.toString() - } - - override fun onGetDefaultValue(a: TypedArray, index: Int): Any { - return a.getInt(index, 0) - } -} \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/res/drawable/ic_qb_logo.xml b/sample-videochat-kotlin/app/src/main/res/drawable/ic_qb_logo.xml new file mode 100644 index 000000000..e541d52be --- /dev/null +++ b/sample-videochat-kotlin/app/src/main/res/drawable/ic_qb_logo.xml @@ -0,0 +1,12 @@ + + + diff --git a/sample-videochat-kotlin/app/src/main/res/layout/activity_login.xml b/sample-videochat-kotlin/app/src/main/res/layout/activity_login.xml index 78008bbeb..a22ca75f7 100644 --- a/sample-videochat-kotlin/app/src/main/res/layout/activity_login.xml +++ b/sample-videochat-kotlin/app/src/main/res/layout/activity_login.xml @@ -13,15 +13,15 @@ diff --git a/sample-videochat-kotlin/app/src/main/res/layout/activity_settings.xml b/sample-videochat-kotlin/app/src/main/res/layout/activity_settings.xml deleted file mode 100644 index 0e61fce29..000000000 --- a/sample-videochat-kotlin/app/src/main/res/layout/activity_settings.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/res/layout/fragment_audio_conversation.xml b/sample-videochat-kotlin/app/src/main/res/layout/fragment_audio_conversation.xml index 3b6d6afeb..9b5584f56 100644 --- a/sample-videochat-kotlin/app/src/main/res/layout/fragment_audio_conversation.xml +++ b/sample-videochat-kotlin/app/src/main/res/layout/fragment_audio_conversation.xml @@ -46,7 +46,7 @@ android:textSize="@dimen/also_on_call_text_size" /> - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/res/layout/fragment_income_call.xml b/sample-videochat-kotlin/app/src/main/res/layout/fragment_income_call.xml index dc380572a..8898bb141 100755 --- a/sample-videochat-kotlin/app/src/main/res/layout/fragment_income_call.xml +++ b/sample-videochat-kotlin/app/src/main/res/layout/fragment_income_call.xml @@ -61,7 +61,7 @@ android:textSize="@dimen/also_on_call_text_size" /> - + + + + + + + android:textSize="18sp" + android:textStyle="bold" /> \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/res/layout/list_item_opponent_from_call.xml b/sample-videochat-kotlin/app/src/main/res/layout/list_item_opponent_from_call.xml index f7c8bca2d..4dc7405e5 100644 --- a/sample-videochat-kotlin/app/src/main/res/layout/list_item_opponent_from_call.xml +++ b/sample-videochat-kotlin/app/src/main/res/layout/list_item_opponent_from_call.xml @@ -1,62 +1,58 @@ - + android:layout_height="match_parent" + android:layout_gravity="center"> - + android:layout_alignParentTop="true" /> - + - - - + - - + - \ No newline at end of file + \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/res/layout/preference_version.xml b/sample-videochat-kotlin/app/src/main/res/layout/preference_version.xml index f8f846465..24ab5c274 100644 --- a/sample-videochat-kotlin/app/src/main/res/layout/preference_version.xml +++ b/sample-videochat-kotlin/app/src/main/res/layout/preference_version.xml @@ -7,5 +7,4 @@ android:paddingTop="@dimen/margin_common" android:paddingBottom="@dimen/margin_common" android:text="@string/versionName" - android:textSize="@dimen/text_size_ringing" - android:textStyle="normal" /> \ No newline at end of file + android:textSize="@dimen/text_size_ringing" /> \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/res/layout/toolbar_call.xml b/sample-videochat-kotlin/app/src/main/res/layout/toolbar_call.xml index 364130ee7..85e52180a 100644 --- a/sample-videochat-kotlin/app/src/main/res/layout/toolbar_call.xml +++ b/sample-videochat-kotlin/app/src/main/res/layout/toolbar_call.xml @@ -14,7 +14,6 @@ android:id="@+id/timer_call" android:layout_width="wrap_content" android:layout_height="wrap_content" - android:layout_gravity="center" android:layout_margin="@dimen/margin_common" android:textColor="@color/text_color_light_grey" android:textSize="@dimen/text_size" diff --git a/sample-videochat-kotlin/app/src/main/res/layout/view_outgoing_screen.xml b/sample-videochat-kotlin/app/src/main/res/layout/view_outgoing_screen.xml index 72008eaf3..7b5263e61 100644 --- a/sample-videochat-kotlin/app/src/main/res/layout/view_outgoing_screen.xml +++ b/sample-videochat-kotlin/app/src/main/res/layout/view_outgoing_screen.xml @@ -16,7 +16,6 @@ android:paddingLeft="@dimen/padding_outgoing_screen" android:paddingRight="@dimen/padding_outgoing_screen" android:textSize="@dimen/text_size_call" - android:textStyle="normal" tools:text="John, Bob, Merlin" /> + android:textSize="@dimen/text_size_ringing" /> \ No newline at end of file diff --git a/sample-videochat-kotlin/app/src/main/res/menu/activity_opponents.xml b/sample-videochat-kotlin/app/src/main/res/menu/activity_opponents.xml index 9f2765375..ca6aa0edb 100644 --- a/sample-videochat-kotlin/app/src/main/res/menu/activity_opponents.xml +++ b/sample-videochat-kotlin/app/src/main/res/menu/activity_opponents.xml @@ -10,11 +10,6 @@ app:showAsAction="always" tools:ignore="AlwaysShowAction" /> - - Rejected Hung up - disable_built_in_aec_preference - false - - audioprocessing_preference - false - - opensles_preference - false - - hwcodec_preference - true - - videocodec_preference - - %1$s must contain 3 — - %2$s alphanumeric Latin characters without spaces, the first char must be a letter - - User name - User Full Name - - 0 - 1 + Use your email or alphanumeric characters in a range from 3 to 50. First character must be a letter. + Use alphanumeric characters and spaces in a range from 3 to 20. Cannot contain more than one space in a row. v %s - User full name + Display name Enter to chat Please enter username and chat room name. \nYou can join existed chat room. - User login + Login Select dialog you want to join @@ -64,49 +44,6 @@ %s (you) Please, choose at least one participant - - Video - Call Settings - - Video codec hardware acceleration - Disable built-in AEC - Disable built-in AEC - Use OpenSL ES for audio playback - Use OpenSL ES for audio playback - Disable audio processing pipeline - Disable audio processing - - - resolution_preference - Video Format - Enter RTC local video resolution - -1 - - startbitratevalue_preference - Bandwidth (0–2000) - 0 - 0 - 2000 - 100 - - frame_rate - Frame Rate (0–30) - 0 - 0 - 30 - 5 - - pref_audio_codec - Default audio codec - ISAC - Select default audio codec - - 0 - Select default video codec - Default video codec - - Packet loss occurs - "%s is calling you" @@ -154,29 +91,14 @@ %s dialog selected %s dialogs selected - - manage_speakerphone_by_proximity_preference - false - - - answer_time_interval - 30 - disconnect_time_interval - 10 - Dialing_time_interval - 5 - + Also on call: - User 1, User 2, User 3, User 4, User 5, User 6 - Connection was lost - and %s - Accepted - Stop + You Application version: @@ -216,5 +138,4 @@ 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/app/src/main/res/xml/preferences.xml b/sample-videochat-kotlin/app/src/main/res/xml/preferences.xml deleted file mode 100644 index 9fe629e4a..000000000 --- a/sample-videochat-kotlin/app/src/main/res/xml/preferences.xml +++ /dev/null @@ -1,86 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/sample-videochat-kotlin/build.gradle b/sample-videochat-kotlin/build.gradle index cbe1ad7e0..370971703 100644 --- a/sample-videochat-kotlin/build.gradle +++ b/sample-videochat-kotlin/build.gradle @@ -6,7 +6,7 @@ buildscript { dependencies { classpath 'com.android.tools.build:gradle:4.2.2' - classpath 'com.google.gms:google-services:4.3.10' + classpath 'com.google.gms:google-services:4.3.13' } } @@ -15,30 +15,4 @@ allprojects { google() mavenCentral() } -} - -ext { - dimensionDefault = 'default' - - lintAbortOnError = false - - // QuickBlox SDK version - qbSdkVersion = '3.9.15' - - //Firebase - firebaseCoreVersion = '20.1.2' - - //Material - materialVersion = '1.5.0' - - //RobotoTextView - robotoTextViewVersion = '4.0.0' - - //Android X - fragmentAndroidXVersion = '1.4.1' - lifecycleViewmodelAndroidXVersion = '2.4.1' - coreKtxVersion = '1.7.0' - - //Kotlin - kotlinGradlePluginVersion = '1.5.31' } \ No newline at end of file diff --git a/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.properties b/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.properties index 4d9ca1649..8a2b5de27 100644 --- a/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.properties +++ b/sample-videochat-kotlin/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,6 @@ +#Mon Jul 25 15:08:47 CEST 2022 distributionBase=GRADLE_USER_HOME -distributionPath=wrapper/dists distributionUrl=https\://services.gradle.org/distributions/gradle-6.7.1-bin.zip -zipStoreBase=GRADLE_USER_HOME +distributionPath=wrapper/dists zipStorePath=wrapper/dists +zipStoreBase=GRADLE_USER_HOME diff --git a/sample-videochat-kotlin/proguard-rules.pro b/sample-videochat-kotlin/proguard-rules.pro deleted file mode 100755 index f1dd7da4b..000000000 --- a/sample-videochat-kotlin/proguard-rules.pro +++ /dev/null @@ -1,70 +0,0 @@ -# Add project specific ProGuard rules here. -# 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 - -# Add any project specific keep options here: - -# If your project uses WebView with JS, uncomment the following -# and specify the fully qualified class name to the JavaScript interface -# class: -#-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 Signature - -# For using GSON @Expose annotation --keepattributes *Annotation* - -# Gson specific classes --keep class sun.misc.Unsafe { *; } -#-keep class com.google.gson.stream.** { *; } - -# Application classes that will be serialized/deserialized over Gson - -keep class com.quickblox.core.account.model.** { *; } - - -##---------------End: proguard configuration for Gson ---------- -#quickblox sample chat - --keep class com.quickblox.auth.parsers.** { *; } --keep class com.quickblox.auth.model.** { *; } --keep class com.quickblox.core.parser.** { *; } --keep class com.quickblox.core.model.** { *; } --keep class com.quickblox.core.server.** { *; } --keep class com.quickblox.core.rest.** { *; } --keep class com.quickblox.core.error.** { *; } --keep class com.quickblox.core.Query { *; } - --keep class com.quickblox.users.parsers.** { *; } --keep class com.quickblox.users.model.** { *; } - --keep class com.quickblox.chat.parser.** { *; } --keep class com.quickblox.chat.model.** { *; } - --keep class com.quickblox.messages.parsers.** { *; } --keep class com.quickblox.messages.model.** { *; } - --keep class com.quickblox.content.parsers.** { *; } --keep class com.quickblox.content.model.** { *; } - --keep class org.jivesoftware.** { *; } - -#sample chat --keep class android.support.v7.** { *; } --keep class com.bumptech.** { *; } - --dontwarn org.jivesoftware.smackx.** --dontwarn android.support.v4.app.** \ No newline at end of file