diff --git a/.editorconfig b/.editorconfig index 7de780eb349..fbc2f393c53 100644 --- a/.editorconfig +++ b/.editorconfig @@ -38,6 +38,11 @@ indent_size=2 [*.{kt,kts}] # IDE does not follow this Ktlint rule strictly, but the default ordering is pretty good anyway, so let's ditch it +ktlint_code_style = android_studio +insert_final_newline = true +ktlint_function_signature_rule_force_multiline_when_parameter_count_greater_or_equal_than=unset +ktlint_function_signature_body_expression_wrapping=multiline ktlint_standard_import-ordering = disabled +ktlint_standard_wrapping = enabled ij_kotlin_allow_trailing_comma = false ij_kotlin_allow_trailing_comma_on_call_site = false diff --git a/app/src/gplay/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.kt b/app/src/gplay/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.kt index e6d9ca62a66..6c854458169 100644 --- a/app/src/gplay/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.kt +++ b/app/src/gplay/java/com/nextcloud/talk/utils/ClosedInterfaceImpl.kt @@ -55,10 +55,7 @@ class ClosedInterfaceImpl : ClosedInterface, ProviderInstaller.ProviderInstallLi // unused atm } - override fun onProviderInstallFailed( - p0: Int, - p1: Intent? - ) { + override fun onProviderInstallFailed(p0: Int, p1: Intent?) { // unused atm } diff --git a/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt b/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt index 9d8aef034d9..7f1b8400e7b 100644 --- a/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/account/AccountVerificationActivity.kt @@ -148,51 +148,55 @@ class AccountVerificationActivity : BaseActivity() { private fun determineBaseUrlProtocol(checkForcedHttps: Boolean) { cookieManager.cookieStore.removeAll() baseUrl = baseUrl!!.replace("http://", "").replace("https://", "") - val queryUrl: String = if (checkForcedHttps) { - "https://" + baseUrl + ApiUtils.getUrlPostfixForStatus() - } else { - "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus() - } + val queryUrl: String = + if (checkForcedHttps) { + "https://" + baseUrl + ApiUtils.getUrlPostfixForStatus() + } else { + "http://" + baseUrl + ApiUtils.getUrlPostfixForStatus() + } ncApi.getServerStatus(queryUrl) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - disposables.add(d) - } - - override fun onNext(status: Status) { - baseUrl = if (checkForcedHttps) { - "https://$baseUrl" - } else { - "http://$baseUrl" + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + disposables.add(d) } - if (isAccountImport) { - val bundle = Bundle() - bundle.putString(KEY_BASE_URL, baseUrl) - bundle.putString(KEY_USERNAME, username) - bundle.putString(KEY_PASSWORD, "") - - val intent = Intent(context, WebViewLoginActivity::class.java) - intent.putExtras(bundle) - startActivity(intent) - } else { - findServerTalkApp() + + override fun onNext(status: Status) { + baseUrl = + if (checkForcedHttps) { + "https://$baseUrl" + } else { + "http://$baseUrl" + } + if (isAccountImport) { + val bundle = Bundle() + bundle.putString(KEY_BASE_URL, baseUrl) + bundle.putString(KEY_USERNAME, username) + bundle.putString(KEY_PASSWORD, "") + + val intent = Intent(context, WebViewLoginActivity::class.java) + intent.putExtras(bundle) + startActivity(intent) + } else { + findServerTalkApp() + } } - } - override fun onError(e: Throwable) { - if (checkForcedHttps) { - determineBaseUrlProtocol(false) - } else { - abortVerification() + override fun onError(e: Throwable) { + if (checkForcedHttps) { + determineBaseUrlProtocol(false) + } else { + abortVerification() + } } - } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun findServerTalkApp() { @@ -201,52 +205,58 @@ class AccountVerificationActivity : BaseActivity() { ncApi.getCapabilities(credentials, ApiUtils.getUrlForCapabilities(baseUrl)) .subscribeOn(Schedulers.io()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - disposables.add(d) - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + disposables.add(d) + } - override fun onNext(capabilitiesOverall: CapabilitiesOverall) { - val hasTalk = - capabilitiesOverall.ocs!!.data!!.capabilities != null && - capabilitiesOverall.ocs!!.data!!.capabilities!!.spreedCapability != null && - capabilitiesOverall.ocs!!.data!!.capabilities!!.spreedCapability!!.features != null && - !capabilitiesOverall.ocs!!.data!!.capabilities!!.spreedCapability!!.features!!.isEmpty() - if (hasTalk) { - fetchProfile(credentials, capabilitiesOverall) - } else { + override fun onNext(capabilitiesOverall: CapabilitiesOverall) { + val hasTalk = + capabilitiesOverall.ocs!!.data!!.capabilities != null && + capabilitiesOverall.ocs!!.data!!.capabilities!!.spreedCapability != null && + capabilitiesOverall.ocs!!.data!!.capabilities!!.spreedCapability!!.features != null && + !capabilitiesOverall.ocs!!.data!!.capabilities!!.spreedCapability!!.features!!.isEmpty() + if (hasTalk) { + fetchProfile(credentials, capabilitiesOverall) + } else { + if (resources != null) { + runOnUiThread { + binding.progressText.text = + String + .format( + resources!!.getString(R.string.nc_nextcloud_talk_app_not_installed), + resources!!.getString(R.string.nc_app_product_name) + ) + } + } + ApplicationWideMessageHolder.getInstance().messageType = + ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK + abortVerification() + } + } + + override fun onError(e: Throwable) { if (resources != null) { runOnUiThread { - binding.progressText.text = String.format( - resources!!.getString(R.string.nc_nextcloud_talk_app_not_installed), - resources!!.getString(R.string.nc_app_product_name) - ) + binding.progressText.text = + String + .format( + resources!!.getString(R.string.nc_nextcloud_talk_app_not_installed), + resources!!.getString(R.string.nc_app_product_name) + ) } } ApplicationWideMessageHolder.getInstance().messageType = ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK abortVerification() } - } - override fun onError(e: Throwable) { - if (resources != null) { - runOnUiThread { - binding.progressText.text = String.format( - resources!!.getString(R.string.nc_nextcloud_talk_app_not_installed), - resources!!.getString(R.string.nc_app_product_name) - ) - } + override fun onComplete() { + // unused atm } - ApplicationWideMessageHolder.getInstance().messageType = - ApplicationWideMessageHolder.MessageType.SERVER_WITHOUT_TALK - abortVerification() } - - override fun onComplete() { - // unused atm - } - }) + ) } private fun storeProfile(displayName: String?, userId: String, capabilities: Capabilities) { @@ -266,39 +276,41 @@ class AccountVerificationActivity : BaseActivity() { ) ) .subscribeOn(Schedulers.io()) - .subscribe(object : MaybeObserver { - override fun onSubscribe(d: Disposable) { - disposables.add(d) - } + .subscribe( + object : MaybeObserver { + override fun onSubscribe(d: Disposable) { + disposables.add(d) + } - @SuppressLint("SetTextI18n") - override fun onSuccess(user: User) { - internalAccountId = user.id!! - if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) { - ClosedInterfaceImpl().setUpPushTokenRegistration() - } else { - Log.w(TAG, "Skipping push registration.") - runOnUiThread { - binding.progressText.text = - """ ${binding.progressText.text} + @SuppressLint("SetTextI18n") + override fun onSuccess(user: User) { + internalAccountId = user.id!! + if (ClosedInterfaceImpl().isGooglePlayServicesAvailable) { + ClosedInterfaceImpl().setUpPushTokenRegistration() + } else { + Log.w(TAG, "Skipping push registration.") + runOnUiThread { + binding.progressText.text = + """ ${binding.progressText.text} ${resources!!.getString(R.string.nc_push_disabled)} - """.trimIndent() + """.trimIndent() + } + fetchAndStoreCapabilities() } - fetchAndStoreCapabilities() } - } - @SuppressLint("SetTextI18n") - override fun onError(e: Throwable) { - binding.progressText.text = """ ${binding.progressText.text}""".trimIndent() + - resources!!.getString(R.string.nc_display_name_not_stored) - abortVerification() - } + @SuppressLint("SetTextI18n") + override fun onError(e: Throwable) { + binding.progressText.text = """ ${binding.progressText.text}""".trimIndent() + + resources!!.getString(R.string.nc_display_name_not_stored) + abortVerification() + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun fetchProfile(credentials: String, capabilities: CapabilitiesOverall) { @@ -307,53 +319,55 @@ class AccountVerificationActivity : BaseActivity() { ApiUtils.getUrlForUserProfile(baseUrl) ) .subscribeOn(Schedulers.io()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - disposables.add(d) - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + disposables.add(d) + } - @SuppressLint("SetTextI18n") - override fun onNext(userProfileOverall: UserProfileOverall) { - var displayName: String? = null - if (!TextUtils.isEmpty(userProfileOverall.ocs!!.data!!.displayName)) { - displayName = userProfileOverall.ocs!!.data!!.displayName - } else if (!TextUtils.isEmpty(userProfileOverall.ocs!!.data!!.displayNameAlt)) { - displayName = userProfileOverall.ocs!!.data!!.displayNameAlt + @SuppressLint("SetTextI18n") + override fun onNext(userProfileOverall: UserProfileOverall) { + var displayName: String? = null + if (!TextUtils.isEmpty(userProfileOverall.ocs!!.data!!.displayName)) { + displayName = userProfileOverall.ocs!!.data!!.displayName + } else if (!TextUtils.isEmpty(userProfileOverall.ocs!!.data!!.displayNameAlt)) { + displayName = userProfileOverall.ocs!!.data!!.displayNameAlt + } + if (!TextUtils.isEmpty(displayName)) { + storeProfile( + displayName, + userProfileOverall.ocs!!.data!!.userId!!, + capabilities.ocs!!.data!!.capabilities!! + ) + } else { + runOnUiThread { + binding.progressText.text = + """ + ${binding.progressText.text} + ${resources!!.getString(R.string.nc_display_name_not_fetched)} + """.trimIndent() + } + abortVerification() + } } - if (!TextUtils.isEmpty(displayName)) { - storeProfile( - displayName, - userProfileOverall.ocs!!.data!!.userId!!, - capabilities.ocs!!.data!!.capabilities!! - ) - } else { + + @SuppressLint("SetTextI18n") + override fun onError(e: Throwable) { runOnUiThread { binding.progressText.text = """ - ${binding.progressText.text} - ${resources!!.getString(R.string.nc_display_name_not_fetched)} + ${binding.progressText.text} + ${resources!!.getString(R.string.nc_display_name_not_fetched)} """.trimIndent() } abortVerification() } - } - @SuppressLint("SetTextI18n") - override fun onError(e: Throwable) { - runOnUiThread { - binding.progressText.text = - """ - ${binding.progressText.text} - ${resources!!.getString(R.string.nc_display_name_not_fetched)} - """.trimIndent() + override fun onComplete() { + // unused atm } - abortVerification() - } - - override fun onComplete() { - // unused atm } - }) + ) } @SuppressLint("SetTextI18n") @@ -365,8 +379,8 @@ class AccountVerificationActivity : BaseActivity() { runOnUiThread { binding.progressText.text = """ - ${binding.progressText.text} - ${resources!!.getString(R.string.nc_push_disabled)} + ${binding.progressText.text} + ${resources!!.getString(R.string.nc_push_disabled)} """.trimIndent() } } @@ -376,8 +390,8 @@ class AccountVerificationActivity : BaseActivity() { runOnUiThread { binding.progressText.text = """ - ${binding.progressText.text} - ${resources!!.getString(R.string.nc_capabilities_failed)} + ${binding.progressText.text} + ${resources!!.getString(R.string.nc_capabilities_failed)} """.trimIndent() } abortVerification() @@ -389,8 +403,8 @@ class AccountVerificationActivity : BaseActivity() { runOnUiThread { binding.progressText.text = """ - ${binding.progressText.text} - ${resources!!.getString(R.string.nc_external_server_failed)} + ${binding.progressText.text} + ${resources!!.getString(R.string.nc_external_server_failed)} """.trimIndent() } } @@ -415,10 +429,15 @@ class AccountVerificationActivity : BaseActivity() { Data.Builder() .putLong(KEY_INTERNAL_USER_ID, internalAccountId) .build() - val signalingSettingsWorker = OneTimeWorkRequest.Builder(SignalingSettingsWorker::class.java) - .setInputData(userData) - .build() - val websocketConnectionsWorker = OneTimeWorkRequest.Builder(WebsocketConnectionsWorker::class.java).build() + val signalingSettingsWorker = + OneTimeWorkRequest + .Builder(SignalingSettingsWorker::class.java) + .setInputData(userData) + .build() + val websocketConnectionsWorker = + OneTimeWorkRequest + .Builder(WebsocketConnectionsWorker::class.java) + .build() WorkManager.getInstance(applicationContext!!) .beginWith(signalingSettingsWorker) @@ -473,8 +492,10 @@ class AccountVerificationActivity : BaseActivity() { private fun abortVerification() { if (isAccountImport) { - ApplicationWideMessageHolder.getInstance().messageType = ApplicationWideMessageHolder.MessageType - .FAILED_TO_IMPORT_ACCOUNT + ApplicationWideMessageHolder.getInstance().messageType = + ApplicationWideMessageHolder + .MessageType + .FAILED_TO_IMPORT_ACCOUNT runOnUiThread { Handler().postDelayed({ val intent = Intent(this, ServerSelectionActivity::class.java) diff --git a/app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt b/app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt index d6bc6712903..4aa51dcf13a 100644 --- a/app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/account/ServerSelectionActivity.kt @@ -74,15 +74,16 @@ class ServerSelectionActivity : BaseActivity() { private var statusQueryDisposable: Disposable? = null - private val onBackPressedCallback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - if (intent.hasExtra(ADD_ADDITIONAL_ACCOUNT) && intent.getBooleanExtra(ADD_ADDITIONAL_ACCOUNT, false)) { - finish() - } else { - finishAffinity() + private val onBackPressedCallback = + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + if (intent.hasExtra(ADD_ADDITIONAL_ACCOUNT) && intent.getBooleanExtra(ADD_ADDITIONAL_ACCOUNT, false)) { + finish() + } else { + finishAffinity() + } } } - } @SuppressLint("SourceLockedOrientationActivity") override fun onCreate(savedInstanceState: Bundle?) { @@ -100,10 +101,12 @@ class ServerSelectionActivity : BaseActivity() { override fun onResume() { super.onResume() - binding.hostUrlInputHelperText.text = String.format( - resources!!.getString(R.string.nc_server_helper_text), - resources!!.getString(R.string.nc_server_product_name) - ) + binding.hostUrlInputHelperText.text = + String + .format( + resources!!.getString(R.string.nc_server_helper_text), + resources!!.getString(R.string.nc_server_product_name) + ) binding.serverEntryTextInputLayout.setEndIconOnClickListener { checkServerAndProceed() } if (resources!!.getBoolean(R.bool.hide_auth_cert)) { @@ -179,15 +182,21 @@ class ServerSelectionActivity : BaseActivity() { ) ) { if (availableAccounts.size > 1) { - binding.importOrChooseProviderText.text = String.format( - resources!!.getString(R.string.nc_server_import_accounts), - AccountUtils.getAppNameBasedOnPackage(resources!!.getString(R.string.nc_import_accounts_from)) - ) + binding.importOrChooseProviderText.text = + String + .format( + resources!!.getString(R.string.nc_server_import_accounts), + AccountUtils + .getAppNameBasedOnPackage(resources!!.getString(R.string.nc_import_accounts_from)) + ) } else { - binding.importOrChooseProviderText.text = String.format( - resources!!.getString(R.string.nc_server_import_account), - AccountUtils.getAppNameBasedOnPackage(resources!!.getString(R.string.nc_import_accounts_from)) - ) + binding.importOrChooseProviderText.text = + String + .format( + resources!!.getString(R.string.nc_server_import_account), + AccountUtils + .getAppNameBasedOnPackage(resources!!.getString(R.string.nc_import_accounts_from)) + ) } } else { if (availableAccounts.size > 1) { @@ -210,13 +219,14 @@ class ServerSelectionActivity : BaseActivity() { private fun showVisitProvidersInfo() { binding.importOrChooseProviderText.setText(R.string.nc_get_from_provider) binding.importOrChooseProviderText.setOnClickListener { - val browserIntent = Intent( - Intent.ACTION_VIEW, - Uri.parse( - resources!! - .getString(R.string.nc_providers_url) + val browserIntent = + Intent( + Intent.ACTION_VIEW, + Uri.parse( + resources!! + .getString(R.string.nc_providers_url) + ) ) - ) startActivity(browserIntent) } } @@ -249,129 +259,133 @@ class ServerSelectionActivity : BaseActivity() { private fun checkServer(url: String, checkForcedHttps: Boolean) { val queryStatusUrl = url + ApiUtils.getUrlPostfixForStatus() - statusQueryDisposable = ncApi.getServerStatus(queryStatusUrl) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ status: Status -> - val productName = resources!!.getString(R.string.nc_server_product_name) - val versionString: String = status.version!!.substring(0, status.version!!.indexOf(".")) - val version: Int = versionString.toInt() - - if (isServerStatusQueryable(status) && version >= MIN_SERVER_MAJOR_VERSION) { - findServerTalkApp(url) - } else if (!status.installed) { - setErrorText( - String.format( - resources!!.getString(R.string.nc_server_not_installed), - productName + statusQueryDisposable = + ncApi + .getServerStatus(queryStatusUrl) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ status: Status -> + val productName = resources!!.getString(R.string.nc_server_product_name) + val versionString: String = status.version!!.substring(0, status.version!!.indexOf(".")) + val version: Int = versionString.toInt() + + if (isServerStatusQueryable(status) && version >= MIN_SERVER_MAJOR_VERSION) { + findServerTalkApp(url) + } else if (!status.installed) { + setErrorText( + String.format( + resources!!.getString(R.string.nc_server_not_installed), + productName + ) ) - ) - } else if (status.needsUpgrade) { - setErrorText( - String.format( - resources!!.getString(R.string.nc_server_db_upgrade_needed), - productName + } else if (status.needsUpgrade) { + setErrorText( + String.format( + resources!!.getString(R.string.nc_server_db_upgrade_needed), + productName + ) ) - ) - } else if (status.maintenance) { - setErrorText( - String.format( - resources!!.getString(R.string.nc_server_maintenance), - productName + } else if (status.maintenance) { + setErrorText( + String.format( + resources!!.getString(R.string.nc_server_maintenance), + productName + ) ) - ) - } else if (!status.version!!.startsWith("13.")) { - setErrorText( - String.format( - resources!!.getString(R.string.nc_server_version), - resources!!.getString(R.string.nc_app_product_name), - productName + } else if (!status.version!!.startsWith("13.")) { + setErrorText( + String.format( + resources!!.getString(R.string.nc_server_version), + resources!!.getString(R.string.nc_app_product_name), + productName + ) ) - ) - } - }, { throwable: Throwable -> - if (checkForcedHttps) { - checkServer(queryStatusUrl.replace("https://", "http://"), false) - } else { - if (throwable.localizedMessage != null) { - setErrorText(throwable.localizedMessage) - } else if (throwable.cause is CertificateException) { - setErrorText(resources!!.getString(R.string.nc_certificate_error)) - } else { - hideserverEntryProgressBar() } + }, { throwable: Throwable -> + if (checkForcedHttps) { + checkServer(queryStatusUrl.replace("https://", "http://"), false) + } else { + if (throwable.localizedMessage != null) { + setErrorText(throwable.localizedMessage) + } else if (throwable.cause is CertificateException) { + setErrorText(resources!!.getString(R.string.nc_certificate_error)) + } else { + hideserverEntryProgressBar() + } + if (binding.importOrChooseProviderText.visibility != View.INVISIBLE) { + binding.importOrChooseProviderText.visibility = View.VISIBLE + binding.certTextView.visibility = View.VISIBLE + } + dispose() + } + }) { + hideserverEntryProgressBar() if (binding.importOrChooseProviderText.visibility != View.INVISIBLE) { binding.importOrChooseProviderText.visibility = View.VISIBLE binding.certTextView.visibility = View.VISIBLE } dispose() } - }) { - hideserverEntryProgressBar() - if (binding.importOrChooseProviderText.visibility != View.INVISIBLE) { - binding.importOrChooseProviderText.visibility = View.VISIBLE - binding.certTextView.visibility = View.VISIBLE - } - dispose() - } } private fun findServerTalkApp(queryUrl: String) { ncApi.getCapabilities(ApiUtils.getUrlForCapabilities(queryUrl)) .subscribeOn(Schedulers.io()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(capabilitiesOverall: CapabilitiesOverall) { - val capabilities = capabilitiesOverall.ocs?.data?.capabilities + override fun onNext(capabilitiesOverall: CapabilitiesOverall) { + val capabilities = capabilitiesOverall.ocs?.data?.capabilities - val hasTalk = - capabilities?.spreedCapability != null && - capabilities.spreedCapability?.features != null && - capabilities.spreedCapability?.features?.isNotEmpty() == true + val hasTalk = + capabilities?.spreedCapability != null && + capabilities.spreedCapability?.features != null && + capabilities.spreedCapability?.features?.isNotEmpty() == true - if (hasTalk) { - runOnUiThread { - if (CapabilitiesUtilNew.isServerEOL(capabilities)) { - if (resources != null) { - runOnUiThread { - setErrorText(resources!!.getString(R.string.nc_settings_server_eol)) + if (hasTalk) { + runOnUiThread { + if (CapabilitiesUtilNew.isServerEOL(capabilities)) { + if (resources != null) { + runOnUiThread { + setErrorText(resources!!.getString(R.string.nc_settings_server_eol)) + } } - } - } else { - val bundle = Bundle() - bundle.putString(BundleKeys.KEY_BASE_URL, queryUrl.replace("/status.php", "")) + } else { + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_BASE_URL, queryUrl.replace("/status.php", "")) - val intent = Intent(context, WebViewLoginActivity::class.java) - intent.putExtras(bundle) - startActivity(intent) + val intent = Intent(context, WebViewLoginActivity::class.java) + intent.putExtras(bundle) + startActivity(intent) + } + } + } else { + if (resources != null) { + runOnUiThread { + setErrorText(resources!!.getString(R.string.nc_server_unsupported)) + } } } - } else { + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Error while checking capabilities", e) if (resources != null) { runOnUiThread { - setErrorText(resources!!.getString(R.string.nc_server_unsupported)) + setErrorText(resources!!.getString(R.string.nc_common_error_sorry)) } } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "Error while checking capabilities", e) - if (resources != null) { - runOnUiThread { - setErrorText(resources!!.getString(R.string.nc_common_error_sorry)) - } + override fun onComplete() { + // unused atm } } - - override fun onComplete() { - // unused atm - } - }) + ) } private fun isServerStatusQueryable(status: Status): Boolean { diff --git a/app/src/main/java/com/nextcloud/talk/account/SwitchAccountActivity.kt b/app/src/main/java/com/nextcloud/talk/account/SwitchAccountActivity.kt index c19c3223fd2..03748132434 100644 --- a/app/src/main/java/com/nextcloud/talk/account/SwitchAccountActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/account/SwitchAccountActivity.kt @@ -72,25 +72,29 @@ class SwitchAccountActivity : BaseActivity() { private val userItems: MutableList> = ArrayList() private var isAccountImport = false - private val onImportItemClickListener = FlexibleAdapter.OnItemClickListener { _, position -> - if (userItems.size > position) { - val account = (userItems[position] as AdvancedUserItem).account - reauthorizeFromImport(account) - } - true - } + private val onImportItemClickListener = + FlexibleAdapter + .OnItemClickListener { _, position -> + if (userItems.size > position) { + val account = (userItems[position] as AdvancedUserItem).account + reauthorizeFromImport(account) + } + true + } - private val onSwitchItemClickListener = FlexibleAdapter.OnItemClickListener { _, position -> - if (userItems.size > position) { - val user = (userItems[position] as AdvancedUserItem).user + private val onSwitchItemClickListener = + FlexibleAdapter + .OnItemClickListener { _, position -> + if (userItems.size > position) { + val user = (userItems[position] as AdvancedUserItem).user - if (userManager.setUserAsActive(user).blockingGet()) { - cookieManager.cookieStore.removeAll() - finish() + if (userManager.setUserAsActive(user).blockingGet()) { + cookieManager.cookieStore.removeAll() + finish() + } + } + true } - } - true - } @SuppressLint("SourceLockedOrientationActivity") override fun onCreate(savedInstanceState: Bundle?) { @@ -137,11 +141,12 @@ class SwitchAccountActivity : BaseActivity() { if (!isAccountImport) { for (user in userManager.users.blockingGet()) { if (!user.current) { - val userId: String? = if (user.userId != null) { - user.userId - } else { - user.username - } + val userId: String? = + if (user.userId != null) { + user.userId + } else { + user.username + } participant = Participant() participant.actorType = Participant.ActorType.USERS participant.actorId = userId diff --git a/app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt b/app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt index 00c9232137e..746ff926b02 100644 --- a/app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/account/WebViewLoginActivity.kt @@ -99,23 +99,25 @@ class WebViewLoginActivity : BaseActivity() { private var automatedLoginAttempted = false private var webViewFidoBridge: WebViewFidoBridge? = null - private val onBackPressedCallback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - val intent = Intent(context, MainActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - startActivity(intent) + private val onBackPressedCallback = + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val intent = Intent(context, MainActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(intent) + } } - } private val webLoginUserAgent: String - get() = ( - Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) + - Build.MANUFACTURER.substring(1).toLowerCase(Locale.getDefault()) + - " " + - Build.MODEL + - " (" + - resources!!.getString(R.string.nc_app_product_name) + - ")" - ) + get() = + ( + Build.MANUFACTURER.substring(0, 1).toUpperCase(Locale.getDefault()) + + Build.MANUFACTURER.substring(1).toLowerCase(Locale.getDefault()) + + " " + + Build.MODEL + + " (" + + resources!!.getString(R.string.nc_app_product_name) + + ")" + ) @SuppressLint("SourceLockedOrientationActivity") override fun onCreate(savedInstanceState: Bundle?) { @@ -167,155 +169,163 @@ class WebViewLoginActivity : BaseActivity() { android.webkit.CookieManager.getInstance().removeAllCookies(null) val headers: MutableMap = HashMap() headers["OCS-APIRequest"] = "true" - binding.webview.webViewClient = object : WebViewClient() { - private var basePageLoaded = false - override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { - webViewFidoBridge?.delegateShouldInterceptRequest(view, request) - return super.shouldInterceptRequest(view, request) - } + binding.webview.webViewClient = + object : WebViewClient() { + private var basePageLoaded = false - override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { - super.onPageStarted(view, url, favicon) - webViewFidoBridge?.delegateOnPageStarted(view, url, favicon) - } - - @Deprecated("Use shouldOverrideUrlLoading(WebView view, WebResourceRequest request)") - override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { - if (url.startsWith(assembledPrefix!!)) { - parseAndLoginFromWebView(url) - return true + override fun shouldInterceptRequest(view: WebView, request: WebResourceRequest): WebResourceResponse? { + webViewFidoBridge?.delegateShouldInterceptRequest(view, request) + return super.shouldInterceptRequest(view, request) } - return false - } - @Suppress("Detekt.TooGenericExceptionCaught") - override fun onPageFinished(view: WebView, url: String) { - loginStep++ - if (!basePageLoaded) { - binding.progressBar.visibility = View.GONE - binding.webview.visibility = View.VISIBLE - - basePageLoaded = true + override fun onPageStarted(view: WebView, url: String, favicon: Bitmap?) { + super.onPageStarted(view, url, favicon) + webViewFidoBridge?.delegateOnPageStarted(view, url, favicon) } - if (!TextUtils.isEmpty(username)) { - if (loginStep == 1) { - binding.webview.loadUrl( - "javascript: {document.getElementsByClassName('login')[0].click(); };" - ) - } else if (!automatedLoginAttempted) { - automatedLoginAttempted = true - if (TextUtils.isEmpty(password)) { - binding.webview.loadUrl( - "javascript:var justStore = document.getElementById('user').value = '$username';" - ) - } else { - binding.webview.loadUrl( - "javascript: {" + - "document.getElementById('user').value = '" + username + "';" + - "document.getElementById('password').value = '" + password + "';" + - "document.getElementById('submit').click(); };" - ) - } + + @Deprecated("Use shouldOverrideUrlLoading(WebView view, WebResourceRequest request)") + override fun shouldOverrideUrlLoading(view: WebView, url: String): Boolean { + if (url.startsWith(assembledPrefix!!)) { + parseAndLoginFromWebView(url) + return true } + return false } - super.onPageFinished(view, url) - } + @Suppress("Detekt.TooGenericExceptionCaught") + override fun onPageFinished(view: WebView, url: String) { + loginStep++ + if (!basePageLoaded) { + binding.progressBar.visibility = View.GONE + binding.webview.visibility = View.VISIBLE - override fun onReceivedClientCertRequest(view: WebView, request: ClientCertRequest) { - val user = userManager.currentUser.blockingGet() - var alias: String? = null - if (!reauthorizeAccount) { - alias = appPreferences.temporaryClientCertAlias - } - if (TextUtils.isEmpty(alias) && user != null) { - alias = user.clientCertificate - } - if (!TextUtils.isEmpty(alias)) { - val finalAlias = alias - Thread { - try { - val privateKey = KeyChain.getPrivateKey(applicationContext, finalAlias!!) - val certificates = KeyChain.getCertificateChain( - applicationContext, - finalAlias + basePageLoaded = true + } + if (!TextUtils.isEmpty(username)) { + if (loginStep == 1) { + binding.webview.loadUrl( + "javascript: {document.getElementsByClassName('login')[0].click(); };" ) - if (privateKey != null && certificates != null) { - request.proceed(privateKey, certificates) + } else if (!automatedLoginAttempted) { + automatedLoginAttempted = true + if (TextUtils.isEmpty(password)) { + binding.webview.loadUrl( + "javascript:var justStore = document.getElementById('user').value = '$username';" + ) } else { - request.cancel() + binding.webview.loadUrl( + "javascript: {" + + "document.getElementById('user').value = '" + username + "';" + + "document.getElementById('password').value = '" + password + "';" + + "document.getElementById('submit').click(); };" + ) } - } catch (e: KeyChainException) { - request.cancel() - } catch (e: InterruptedException) { - request.cancel() } - }.start() - } else { - KeyChain.choosePrivateKeyAlias( - this@WebViewLoginActivity, - { chosenAlias: String? -> - if (chosenAlias != null) { - appPreferences!!.temporaryClientCertAlias = chosenAlias - Thread { - var privateKey: PrivateKey? = null - try { - privateKey = KeyChain.getPrivateKey(applicationContext, chosenAlias) - val certificates = KeyChain.getCertificateChain( + } + + super.onPageFinished(view, url) + } + + override fun onReceivedClientCertRequest(view: WebView, request: ClientCertRequest) { + val user = userManager.currentUser.blockingGet() + var alias: String? = null + if (!reauthorizeAccount) { + alias = appPreferences.temporaryClientCertAlias + } + if (TextUtils.isEmpty(alias) && user != null) { + alias = user.clientCertificate + } + if (!TextUtils.isEmpty(alias)) { + val finalAlias = alias + Thread { + try { + val privateKey = KeyChain.getPrivateKey(applicationContext, finalAlias!!) + val certificates = + KeyChain + .getCertificateChain( applicationContext, - chosenAlias + finalAlias ) - if (privateKey != null && certificates != null) { - request.proceed(privateKey, certificates) - } else { - request.cancel() - } - } catch (e: KeyChainException) { - request.cancel() - } catch (e: InterruptedException) { - request.cancel() - } - }.start() - } else { + if (privateKey != null && certificates != null) { + request.proceed(privateKey, certificates) + } else { + request.cancel() + } + } catch (e: KeyChainException) { + request.cancel() + } catch (e: InterruptedException) { request.cancel() } - }, - arrayOf("RSA", "EC"), - null, - request.host, - request.port, - null - ) + }.start() + } else { + KeyChain.choosePrivateKeyAlias( + this@WebViewLoginActivity, + { chosenAlias: String? -> + if (chosenAlias != null) { + appPreferences!!.temporaryClientCertAlias = chosenAlias + Thread { + var privateKey: PrivateKey? = null + try { + privateKey = + KeyChain + .getPrivateKey(applicationContext, chosenAlias) + val certificates = + KeyChain + .getCertificateChain( + applicationContext, + chosenAlias + ) + if (privateKey != null && certificates != null) { + request.proceed(privateKey, certificates) + } else { + request.cancel() + } + } catch (e: KeyChainException) { + request.cancel() + } catch (e: InterruptedException) { + request.cancel() + } + }.start() + } else { + request.cancel() + } + }, + arrayOf("RSA", "EC"), + null, + request.host, + request.port, + null + ) + } } - } - @Suppress("Detekt.TooGenericExceptionCaught") - override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { - try { - val sslCertificate = error.certificate - val f: Field = sslCertificate.javaClass.getDeclaredField("mX509Certificate") - f.isAccessible = true - val cert = f[sslCertificate] as X509Certificate - if (cert == null) { - handler.cancel() - } else { - try { - trustManager.checkServerTrusted(arrayOf(cert), "generic") - handler.proceed() - } catch (exception: CertificateException) { - eventBus.post(CertificateEvent(cert, trustManager, handler)) + @Suppress("Detekt.TooGenericExceptionCaught") + override fun onReceivedSslError(view: WebView, handler: SslErrorHandler, error: SslError) { + try { + val sslCertificate = error.certificate + val f: Field = sslCertificate.javaClass.getDeclaredField("mX509Certificate") + f.isAccessible = true + val cert = f[sslCertificate] as X509Certificate + if (cert == null) { + handler.cancel() + } else { + try { + trustManager.checkServerTrusted(arrayOf(cert), "generic") + handler.proceed() + } catch (exception: CertificateException) { + eventBus.post(CertificateEvent(cert, trustManager, handler)) + } } + } catch (exception: Exception) { + handler.cancel() } - } catch (exception: Exception) { - handler.cancel() } - } - @Deprecated("Deprecated in super implementation") - override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) { - super.onReceivedError(view, errorCode, description, failingUrl) + @Deprecated("Deprecated in super implementation") + override fun onReceivedError(view: WebView, errorCode: Int, description: String, failingUrl: String) { + super.onReceivedError(view, errorCode, description, failingUrl) + } } - } binding.webview.loadUrl("$baseUrl/index.php/login/flow", headers) } @@ -417,17 +427,23 @@ class WebViewLoginActivity : BaseActivity() { } for (value in values) { if (value.startsWith("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { - loginData.username = URLDecoder.decode( - value.substring(("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length) - ) + loginData.username = + URLDecoder + .decode( + value.substring(("user" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length) + ) } else if (value.startsWith("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { - loginData.token = URLDecoder.decode( - value.substring(("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length) - ) + loginData.token = + URLDecoder + .decode( + value.substring(("password" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length) + ) } else if (value.startsWith("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR)) { - loginData.serverUrl = URLDecoder.decode( - value.substring(("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length) - ) + loginData.serverUrl = + URLDecoder + .decode( + value.substring(("server" + LOGIN_URL_DATA_KEY_VALUE_SEPARATOR).length) + ) } else { return null } diff --git a/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt index 6efc1e8a16a..7b528fc1eda 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/BaseActivity.kt @@ -61,7 +61,9 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) open class BaseActivity : AppCompatActivity() { enum class AppBarLayoutType { - TOOLBAR, SEARCH_BAR, EMPTY + TOOLBAR, + SEARCH_BAR, + EMPTY } @Inject @@ -196,27 +198,31 @@ open class BaseActivity : AppCompatActivity() { } @SuppressLint("StringFormatMatches") - val dialogText = String.format( - resources.getString(R.string.nc_certificate_dialog_text), - issuedBy, - issuedFor, - validFrom, - validUntil - ) - - val dialogBuilder = MaterialAlertDialogBuilder(this).setIcon( - viewThemeUtils.dialog.colorMaterialAlertDialogIcon( - context, - R.drawable.ic_security_white_24dp - ) - ).setTitle(R.string.nc_certificate_dialog_title) - .setMessage(dialogText) - .setPositiveButton(R.string.nc_yes) { _, _ -> - trustManager.addCertInTrustStore(cert) - sslErrorHandler?.proceed() - }.setNegativeButton(R.string.nc_no) { _, _ -> - sslErrorHandler?.cancel() - } + val dialogText = + String + .format( + resources.getString(R.string.nc_certificate_dialog_text), + issuedBy, + issuedFor, + validFrom, + validUntil + ) + + val dialogBuilder = + MaterialAlertDialogBuilder(this) + .setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_security_white_24dp + ) + ).setTitle(R.string.nc_certificate_dialog_title) + .setMessage(dialogText) + .setPositiveButton(R.string.nc_yes) { _, _ -> + trustManager.addCertInTrustStore(cert) + sslErrorHandler?.proceed() + }.setNegativeButton(R.string.nc_no) { _, _ -> + sslErrorHandler?.cancel() + } viewThemeUtils.dialog.colorMaterialAlertDialogBackground(context, dialogBuilder) diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt index 8a6c92466d7..92d60e779e2 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/CallActivity.kt @@ -268,35 +268,38 @@ class CallActivity : CallBaseActivity() { private val screenParticipantDisplayItemManagersHandler = Handler(Looper.getMainLooper()) private val callParticipantEventDisplayers: MutableMap = HashMap() private val callParticipantEventDisplayersHandler = Handler(Looper.getMainLooper()) - private val callParticipantListObserver: CallParticipantList.Observer = object : CallParticipantList.Observer { - override fun onCallParticipantsChanged( - joined: Collection, - updated: Collection, - left: Collection, - unchanged: Collection - ) { - handleCallParticipantsChanged(joined, updated, left, unchanged) - } + private val callParticipantListObserver: CallParticipantList.Observer = + object : CallParticipantList.Observer { + override fun onCallParticipantsChanged( + joined: Collection, + updated: Collection, + left: Collection, + unchanged: Collection + ) { + handleCallParticipantsChanged(joined, updated, left, unchanged) + } - override fun onCallEndedForAll() { - Log.d(TAG, "A moderator ended the call for all.") - hangup(true) + override fun onCallEndedForAll() { + Log.d(TAG, "A moderator ended the call for all.") + hangup(true) + } } - } private var callParticipantList: CallParticipantList? = null private var switchToRoomToken = "" private var isBreakoutRoom = false - private val localParticipantMessageListener = LocalParticipantMessageListener { token -> - switchToRoomToken = token - hangup(true) - } - private val offerMessageListener = OfferMessageListener { sessionId, roomType, sdp, nick -> - getOrCreatePeerConnectionWrapperForSessionIdAndType( - sessionId, - roomType, - false - ) - } + private val localParticipantMessageListener = + LocalParticipantMessageListener { token -> + switchToRoomToken = token + hangup(true) + } + private val offerMessageListener = + OfferMessageListener { sessionId, roomType, sdp, nick -> + getOrCreatePeerConnectionWrapperForSessionIdAndType( + sessionId, + roomType, + false + ) + } private var externalSignalingServer: ExternalSignalingServer? = null private var webSocketClient: WebSocketInstance? = null private var webSocketConnectionHelper: WebSocketConnectionHelper? = null @@ -314,54 +317,55 @@ class CallActivity : CallBaseActivity() { private var moreCallActionsDialog: MoreCallActionsDialog? = null private var elapsedSeconds: Long = 0 - private var requestPermissionLauncher = registerForActivityResult( - ActivityResultContracts.RequestMultiplePermissions() - ) { permissionMap: Map -> - val rationaleList: MutableList = ArrayList() - val audioPermission = permissionMap[Manifest.permission.RECORD_AUDIO] - if (audioPermission != null) { - if (java.lang.Boolean.TRUE == audioPermission) { - if (!microphoneOn) { - onMicrophoneClick() + private var requestPermissionLauncher = + registerForActivityResult( + ActivityResultContracts.RequestMultiplePermissions() + ) { permissionMap: Map -> + val rationaleList: MutableList = ArrayList() + val audioPermission = permissionMap[Manifest.permission.RECORD_AUDIO] + if (audioPermission != null) { + if (java.lang.Boolean.TRUE == audioPermission) { + if (!microphoneOn) { + onMicrophoneClick() + } + } else { + rationaleList.add(resources.getString(R.string.nc_microphone_permission_hint)) } - } else { - rationaleList.add(resources.getString(R.string.nc_microphone_permission_hint)) } - } - val cameraPermission = permissionMap[Manifest.permission.CAMERA] - if (cameraPermission != null) { - if (java.lang.Boolean.TRUE == cameraPermission) { - if (!videoOn) { - onCameraClick() - } - if (cameraEnumerator!!.deviceNames.isEmpty()) { - binding!!.cameraButton.visibility = View.GONE - } - if (cameraEnumerator!!.deviceNames.size > 1) { - binding!!.switchSelfVideoButton.visibility = View.VISIBLE + val cameraPermission = permissionMap[Manifest.permission.CAMERA] + if (cameraPermission != null) { + if (java.lang.Boolean.TRUE == cameraPermission) { + if (!videoOn) { + onCameraClick() + } + if (cameraEnumerator!!.deviceNames.isEmpty()) { + binding!!.cameraButton.visibility = View.GONE + } + if (cameraEnumerator!!.deviceNames.size > 1) { + binding!!.switchSelfVideoButton.visibility = View.VISIBLE + } + } else { + rationaleList.add(resources.getString(R.string.nc_camera_permission_hint)) } - } else { - rationaleList.add(resources.getString(R.string.nc_camera_permission_hint)) } - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - val bluetoothPermission = permissionMap[Manifest.permission.BLUETOOTH_CONNECT] - if (bluetoothPermission != null) { - if (java.lang.Boolean.TRUE == bluetoothPermission) { - enableBluetoothManager() - } else { - // Only ask for bluetooth when already asking to grant microphone or camera access. Asking - // for bluetooth solely is not important enough here and would most likely annoy the user. - if (rationaleList.isNotEmpty()) { - rationaleList.add(resources.getString(R.string.nc_bluetooth_permission_hint)) + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + val bluetoothPermission = permissionMap[Manifest.permission.BLUETOOTH_CONNECT] + if (bluetoothPermission != null) { + if (java.lang.Boolean.TRUE == bluetoothPermission) { + enableBluetoothManager() + } else { + // Only ask for bluetooth when already asking to grant microphone or camera access. Asking + // for bluetooth solely is not important enough here and would most likely annoy the user. + if (rationaleList.isNotEmpty()) { + rationaleList.add(resources.getString(R.string.nc_bluetooth_permission_hint)) + } } } } + if (rationaleList.isNotEmpty()) { + showRationaleDialogForSettings(rationaleList) + } } - if (rationaleList.isNotEmpty()) { - showRationaleDialogForSettings(rationaleList) - } - } private var canPublishAudioStream = false private var canPublishVideoStream = false private var isModerator = false @@ -371,11 +375,12 @@ class CallActivity : CallBaseActivity() { private lateinit var micInputAudioRecorder: AudioRecord private var micInputAudioRecordThread: Thread? = null private var isMicInputAudioThreadRunning: Boolean = false - private val bufferSize = AudioRecord.getMinBufferSize( - SAMPLE_RATE, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT - ) + private val bufferSize = + AudioRecord.getMinBufferSize( + SAMPLE_RATE, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT + ) private var recordingConsentGiven = false @@ -432,9 +437,10 @@ class CallActivity : CallBaseActivity() { } } } - callRecordingViewModel = ViewModelProvider(this, viewModelFactory).get( - CallRecordingViewModel::class.java - ) + callRecordingViewModel = + ViewModelProvider(this, viewModelFactory).get( + CallRecordingViewModel::class.java + ) callRecordingViewModel!!.setData(roomToken!!) callRecordingViewModel!!.setRecordingState(extras.getInt(KEY_RECORDING_STATE)) callRecordingViewModel!!.viewState.observe(this) { viewState: CallRecordingViewModel.ViewState? -> @@ -458,15 +464,16 @@ class CallActivity : CallBaseActivity() { } } else if (viewState is RecordingConfirmStopState) { if (isAllowedToStartOrStopRecording) { - val dialogBuilder = MaterialAlertDialogBuilder(this) - .setTitle(R.string.record_stop_confirm_title) - .setMessage(R.string.record_stop_confirm_message) - .setPositiveButton(R.string.record_stop_description) { _: DialogInterface?, _: Int -> - callRecordingViewModel!!.stopRecording() - } - .setNegativeButton(R.string.nc_common_dismiss) { _: DialogInterface?, _: Int -> - callRecordingViewModel!!.dismissStopRecording() - } + val dialogBuilder = + MaterialAlertDialogBuilder(this) + .setTitle(R.string.record_stop_confirm_title) + .setMessage(R.string.record_stop_confirm_message) + .setPositiveButton(R.string.record_stop_description) { _: DialogInterface?, _: Int -> + callRecordingViewModel!!.stopRecording() + } + .setNegativeButton(R.string.nc_common_dismiss) { _: DialogInterface?, _: Int -> + callRecordingViewModel!!.dismissStopRecording() + } viewThemeUtils.dialog.colorMaterialAlertDialogBackground(this, dialogBuilder) val dialog = dialogBuilder.show() viewThemeUtils.platform.colorTextButtons( @@ -491,10 +498,13 @@ class CallActivity : CallBaseActivity() { } initClickListeners() binding!!.microphoneButton.setOnTouchListener(MicrophoneButtonTouchListener()) - pulseAnimation = PulseAnimation.create().with(binding!!.microphoneButton) - .setDuration(310) - .setRepeatCount(PulseAnimation.INFINITE) - .setRepeatMode(PulseAnimation.REVERSE) + pulseAnimation = + PulseAnimation + .create() + .with(binding!!.microphoneButton) + .setDuration(310) + .setRepeatCount(PulseAnimation.INFINITE) + .setRepeatMode(PulseAnimation.REVERSE) basicInitialization() callParticipants = HashMap() participantDisplayItems = HashMap() @@ -507,18 +517,19 @@ class CallActivity : CallBaseActivity() { private fun checkRecordingConsentAndInitiateCall() { fun askForRecordingConsent() { - val materialAlertDialogBuilder = MaterialAlertDialogBuilder(this) - .setTitle(R.string.recording_consent_title) - .setMessage(R.string.recording_consent_description) - .setCancelable(false) - .setPositiveButton(R.string.nc_yes) { _, _ -> - recordingConsentGiven = true - initiateCall() - } - .setNegativeButton(R.string.nc_no) { _, _ -> - recordingConsentGiven = false - hangup(true) - } + val materialAlertDialogBuilder = + MaterialAlertDialogBuilder(this) + .setTitle(R.string.recording_consent_title) + .setMessage(R.string.recording_consent_description) + .setCancelable(false) + .setPositiveButton(R.string.nc_yes) { _, _ -> + recordingConsentGiven = true + initiateCall() + } + .setNegativeButton(R.string.nc_no) { _, _ -> + recordingConsentGiven = false + hangup(true) + } viewThemeUtils.dialog.colorMaterialAlertDialogBackground(this, materialAlertDialogBuilder) val dialog = materialAlertDialogBuilder.show() @@ -532,37 +543,42 @@ class CallActivity : CallBaseActivity() { CapabilitiesUtilNew.RECORDING_CONSENT_NOT_REQUIRED -> initiateCall() CapabilitiesUtilNew.RECORDING_CONSENT_REQUIRED -> askForRecordingConsent() CapabilitiesUtilNew.RECORDING_CONSENT_DEPEND_ON_CONVERSATION -> { - val getRoomApiVersion = ApiUtils.getConversationApiVersion( - conversationUser, - intArrayOf(ApiUtils.APIv4, 1) - ) + val getRoomApiVersion = + ApiUtils + .getConversationApiVersion( + conversationUser, + intArrayOf(ApiUtils.APIv4, 1) + ) ncApi!!.getRoom(credentials, ApiUtils.getUrlForRoom(getRoomApiVersion, baseUrl, roomToken)) .retry(API_RETRIES) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(roomOverall: RoomOverall) { - val conversation = roomOverall.ocs!!.data - if (conversation?.recordingConsentRequired == 1) { - askForRecordingConsent() - } else { - initiateCall() + override fun onNext(roomOverall: RoomOverall) { + val conversation = roomOverall.ocs!!.data + if (conversation?.recordingConsentRequired == 1) { + askForRecordingConsent() + } else { + initiateCall() + } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to get room", e) - Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - } + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to get room", e) + Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG) + .show() + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } } } @@ -722,11 +738,12 @@ class CallActivity : CallBaseActivity() { } catch (t: Throwable) { Log.w(TAG, "Camera2Enumerator threw an error", t) } - cameraEnumerator = if (camera2EnumeratorIsSupported) { - Camera2Enumerator(this) - } else { - Camera1Enumerator(MagicWebRTCUtils.shouldEnableVideoHardwareAcceleration()) - } + cameraEnumerator = + if (camera2EnumeratorIsSupported) { + Camera2Enumerator(this) + } else { + Camera1Enumerator(MagicWebRTCUtils.shouldEnableVideoHardwareAcceleration()) + } } private fun basicInitialization() { @@ -735,19 +752,23 @@ class CallActivity : CallBaseActivity() { // Create a new PeerConnectionFactory instance. val options = PeerConnectionFactory.Options() - val defaultVideoEncoderFactory = DefaultVideoEncoderFactory( - rootEglBase!!.eglBaseContext, - true, - true - ) - val defaultVideoDecoderFactory = DefaultVideoDecoderFactory( - rootEglBase!!.eglBaseContext - ) - peerConnectionFactory = PeerConnectionFactory.builder() - .setOptions(options) - .setVideoEncoderFactory(defaultVideoEncoderFactory) - .setVideoDecoderFactory(defaultVideoDecoderFactory) - .createPeerConnectionFactory() + val defaultVideoEncoderFactory = + DefaultVideoEncoderFactory( + rootEglBase!!.eglBaseContext, + true, + true + ) + val defaultVideoDecoderFactory = + DefaultVideoDecoderFactory( + rootEglBase!!.eglBaseContext + ) + peerConnectionFactory = + PeerConnectionFactory + .builder() + .setOptions(options) + .setVideoEncoderFactory(defaultVideoEncoderFactory) + .setVideoDecoderFactory(defaultVideoDecoderFactory) + .createPeerConnectionFactory() // Create MediaConstraints - Will be useful for specifying video and audio constraints. audioConstraints = MediaConstraints() @@ -805,21 +826,25 @@ class CallActivity : CallBaseActivity() { private fun updateAudioOutputButton(activeAudioDevice: AudioDevice) { when (activeAudioDevice) { - AudioDevice.BLUETOOTH -> binding!!.audioOutputButton.setImageResource( - R.drawable.ic_baseline_bluetooth_audio_24 - ) + AudioDevice.BLUETOOTH -> + binding!!.audioOutputButton.setImageResource( + R.drawable.ic_baseline_bluetooth_audio_24 + ) - AudioDevice.SPEAKER_PHONE -> binding!!.audioOutputButton.setImageResource( - R.drawable.ic_volume_up_white_24dp - ) + AudioDevice.SPEAKER_PHONE -> + binding!!.audioOutputButton.setImageResource( + R.drawable.ic_volume_up_white_24dp + ) - AudioDevice.EARPIECE -> binding!!.audioOutputButton.setImageResource( - R.drawable.ic_baseline_phone_in_talk_24 - ) + AudioDevice.EARPIECE -> + binding!!.audioOutputButton.setImageResource( + R.drawable.ic_baseline_phone_in_talk_24 + ) - AudioDevice.WIRED_HEADSET -> binding!!.audioOutputButton.setImageResource( - R.drawable.ic_baseline_headset_mic_24 - ) + AudioDevice.WIRED_HEADSET -> + binding!!.audioOutputButton.setImageResource( + R.drawable.ic_baseline_headset_mic_24 + ) else -> Log.e(TAG, "Icon for audio output not available") } @@ -838,20 +863,22 @@ class CallActivity : CallBaseActivity() { binding!!.switchSelfVideoButton.visibility = View.GONE binding!!.cameraButton.visibility = View.GONE binding!!.selfVideoRenderer.visibility = View.GONE - val params = RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) + val params = + RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) params.addRule(RelativeLayout.BELOW, R.id.callInfosLinearLayout) val callControlsHeight = applicationContext.resources.getDimension(R.dimen.call_controls_height).roundToInt() params.setMargins(0, 0, 0, callControlsHeight) binding!!.gridview.layoutParams = params } else { - val params = RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) + val params = + RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) params.setMargins(0, 0, 0, 0) binding!!.gridview.layoutParams = params if (cameraEnumerator!!.deviceNames.size < 2) { @@ -895,51 +922,57 @@ class CallActivity : CallBaseActivity() { Log.d(TAG, "initGridAdapter") val columns: Int val participantsInGrid = participantDisplayItems!!.size - columns = if (resources != null && - resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT - ) { - if (participantsInGrid > 2) { - 2 - } else { - 1 - } - } else { - if (participantsInGrid > 2) { - 3 - } else if (participantsInGrid > 1) { - 2 + columns = + if (resources != null && + resources.configuration.orientation == Configuration.ORIENTATION_PORTRAIT + ) { + if (participantsInGrid > 2) { + 2 + } else { + 1 + } } else { - 1 + if (participantsInGrid > 2) { + 3 + } else if (participantsInGrid > 1) { + 2 + } else { + 1 + } } - } binding!!.gridview.numColumns = columns binding!!.conversationRelativeLayout .viewTreeObserver - .addOnGlobalLayoutListener(object : OnGlobalLayoutListener { - override fun onGlobalLayout() { - binding!!.conversationRelativeLayout.viewTreeObserver.removeOnGlobalLayoutListener(this) - val height = binding!!.conversationRelativeLayout.measuredHeight - binding!!.gridview.minimumHeight = height + .addOnGlobalLayoutListener( + object : OnGlobalLayoutListener { + override fun onGlobalLayout() { + binding!!.conversationRelativeLayout.viewTreeObserver.removeOnGlobalLayoutListener(this) + val height = binding!!.conversationRelativeLayout.measuredHeight + binding!!.gridview.minimumHeight = height + } } - }) + ) binding!!.callInfosLinearLayout .viewTreeObserver - .addOnGlobalLayoutListener(object : OnGlobalLayoutListener { - override fun onGlobalLayout() { - binding!!.callInfosLinearLayout.viewTreeObserver.removeOnGlobalLayoutListener(this) + .addOnGlobalLayoutListener( + object : OnGlobalLayoutListener { + override fun onGlobalLayout() { + binding!!.callInfosLinearLayout.viewTreeObserver.removeOnGlobalLayoutListener(this) + } } - }) + ) if (participantsAdapter != null) { participantsAdapter!!.destroy() } - participantsAdapter = ParticipantsAdapter( - this, - participantDisplayItems, - binding!!.conversationRelativeLayout, - binding!!.callInfosLinearLayout, - columns, - isVoiceOnlyCall - ) + participantsAdapter = + ParticipantsAdapter( + this, + participantDisplayItems, + binding!!.conversationRelativeLayout, + binding!!.callInfosLinearLayout, + columns, + isVoiceOnlyCall + ) binding!!.gridview.adapter = participantsAdapter if (isInPipMode) { updateUiForPipMode() @@ -1014,13 +1047,14 @@ class CallActivity : CallBaseActivity() { for (rationale in rationaleList) { rationalesWithLineBreaks.append(rationale).append("\n\n") } - val dialogBuilder = MaterialAlertDialogBuilder(this) - .setTitle(R.string.nc_permissions_rationale_dialog_title) - .setMessage(rationalesWithLineBreaks) - .setPositiveButton(R.string.nc_permissions_ask) { _, _ -> - requestPermissionLauncher.launch(permissionsToRequest.toTypedArray()) - } - .setNegativeButton(R.string.nc_common_dismiss, null) + val dialogBuilder = + MaterialAlertDialogBuilder(this) + .setTitle(R.string.nc_permissions_rationale_dialog_title) + .setMessage(rationalesWithLineBreaks) + .setPositiveButton(R.string.nc_permissions_ask) { _, _ -> + requestPermissionLauncher.launch(permissionsToRequest.toTypedArray()) + } + .setNegativeButton(R.string.nc_common_dismiss, null) viewThemeUtils.dialog.colorMaterialAlertDialogBackground(this, dialogBuilder) dialogBuilder.show() } @@ -1034,15 +1068,16 @@ class CallActivity : CallBaseActivity() { for (rationale in rationaleList) { rationalesWithLineBreaks.append(rationale).append("\n\n") } - val dialogBuilder = MaterialAlertDialogBuilder(this) - .setTitle(R.string.nc_permissions_rationale_dialog_title) - .setMessage(rationalesWithLineBreaks) - .setPositiveButton(R.string.nc_permissions_settings) { _, _ -> - val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) - intent.data = Uri.fromParts("package", packageName, null) - startActivity(intent) - } - .setNegativeButton(R.string.nc_common_dismiss, null) + val dialogBuilder = + MaterialAlertDialogBuilder(this) + .setTitle(R.string.nc_permissions_rationale_dialog_title) + .setMessage(rationalesWithLineBreaks) + .setPositiveButton(R.string.nc_permissions_settings) { _, _ -> + val intent = Intent(Settings.ACTION_APPLICATION_DETAILS_SETTINGS) + intent.data = Uri.fromParts("package", packageName, null) + startActivity(intent) + } + .setNegativeButton(R.string.nc_common_dismiss, null) viewThemeUtils.dialog.colorMaterialAlertDialogBackground(this, dialogBuilder) dialogBuilder.show() } @@ -1050,10 +1085,7 @@ class CallActivity : CallBaseActivity() { private val isConnectionEstablished: Boolean get() = currentCallStatus === CallStatus.JOINED || currentCallStatus === CallStatus.IN_CONVERSATION - private fun onAudioManagerDevicesChanged( - currentDevice: AudioDevice, - availableDevices: Set - ) { + private fun onAudioManagerDevicesChanged(currentDevice: AudioDevice, availableDevices: Set) { Log.d(TAG, "onAudioManagerDevicesChanged: $availableDevices, currentDevice: $currentDevice") val shouldDisableProximityLock = currentDevice == AudioDevice.WIRED_HEADSET || @@ -1075,10 +1107,11 @@ class CallActivity : CallBaseActivity() { // Create a VideoSource instance if (videoCapturer != null) { - val surfaceTextureHelper = SurfaceTextureHelper.create( - "CaptureThread", - rootEglBase!!.eglBaseContext - ) + val surfaceTextureHelper = + SurfaceTextureHelper.create( + "CaptureThread", + rootEglBase!!.eglBaseContext + ) videoSource = peerConnectionFactory!!.createVideoSource(false) videoCapturer!!.initialize(surfaceTextureHelper, applicationContext, videoSource!!.capturerObserver) } @@ -1102,33 +1135,35 @@ class CallActivity : CallBaseActivity() { private fun startMicInputDetection() { if (permissionUtil!!.isMicrophonePermissionGranted() && micInputAudioRecordThread == null) { var isSpeakingLongTerm = false - micInputAudioRecorder = AudioRecord( - MediaRecorder.AudioSource.MIC, - SAMPLE_RATE, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT, - bufferSize - ) + micInputAudioRecorder = + AudioRecord( + MediaRecorder.AudioSource.MIC, + SAMPLE_RATE, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT, + bufferSize + ) isMicInputAudioThreadRunning = true micInputAudioRecorder.startRecording() - micInputAudioRecordThread = Thread( - Runnable { - while (isMicInputAudioThreadRunning) { - val byteArr = ByteArray(bufferSize / 2) - micInputAudioRecorder.read(byteArr, 0, byteArr.size) - val isCurrentlySpeaking = abs(byteArr[0].toDouble()) > MICROPHONE_VALUE_THRESHOLD - - if (isCurrentlySpeaking && !isSpeakingLongTerm) { - isSpeakingLongTerm = true - sendIsSpeakingMessage(true) - } else if (!isCurrentlySpeaking && isSpeakingLongTerm) { - isSpeakingLongTerm = false - sendIsSpeakingMessage(false) + micInputAudioRecordThread = + Thread( + Runnable { + while (isMicInputAudioThreadRunning) { + val byteArr = ByteArray(bufferSize / 2) + micInputAudioRecorder.read(byteArr, 0, byteArr.size) + val isCurrentlySpeaking = abs(byteArr[0].toDouble()) > MICROPHONE_VALUE_THRESHOLD + + if (isCurrentlySpeaking && !isSpeakingLongTerm) { + isSpeakingLongTerm = true + sendIsSpeakingMessage(true) + } else if (!isCurrentlySpeaking && isSpeakingLongTerm) { + isSpeakingLongTerm = false + sendIsSpeakingMessage(false) + } + Thread.sleep(MICROPHONE_VALUE_SLEEP) } - Thread.sleep(MICROPHONE_VALUE_SLEEP) } - } - ) + ) micInputAudioRecordThread!!.start() } } @@ -1234,22 +1269,23 @@ class CallActivity : CallBaseActivity() { } private fun getSpotlightView(): SpotlightView? { - val builder = SpotlightView.Builder(this) - .introAnimationDuration(300) - .enableRevealAnimation(true) - .performClick(false) - .fadeinTextDuration(400) - .headingTvSize(20) - .headingTvText(resources.getString(R.string.nc_push_to_talk)) - .subHeadingTvColor(resources.getColor(R.color.bg_default, null)) - .subHeadingTvSize(16) - .subHeadingTvText(resources.getString(R.string.nc_push_to_talk_desc)) - .maskColor(Color.parseColor("#dc000000")) - .target(binding!!.microphoneButton) - .lineAnimDuration(400) - .enableDismissAfterShown(true) - .dismissOnBackPress(true) - .usageId("pushToTalk") + val builder = + SpotlightView.Builder(this) + .introAnimationDuration(300) + .enableRevealAnimation(true) + .performClick(false) + .fadeinTextDuration(400) + .headingTvSize(20) + .headingTvText(resources.getString(R.string.nc_push_to_talk)) + .subHeadingTvColor(resources.getColor(R.color.bg_default, null)) + .subHeadingTvSize(16) + .subHeadingTvText(resources.getString(R.string.nc_push_to_talk_desc)) + .maskColor(Color.parseColor("#dc000000")) + .target(binding!!.microphoneButton) + .lineAnimDuration(400) + .enableDismissAfterShown(true) + .dismissOnBackPress(true) + .usageId("pushToTalk") return viewThemeUtils.talk.themeSpotlightView(context, builder).show() } @@ -1285,15 +1321,17 @@ class CallActivity : CallBaseActivity() { fun switchCamera() { val cameraVideoCapturer = videoCapturer as CameraVideoCapturer? - cameraVideoCapturer?.switchCamera(object : CameraSwitchHandler { - override fun onCameraSwitchDone(currentCameraIsFront: Boolean) { - binding!!.selfVideoRenderer.setMirror(currentCameraIsFront) - } + cameraVideoCapturer?.switchCamera( + object : CameraSwitchHandler { + override fun onCameraSwitchDone(currentCameraIsFront: Boolean) { + binding!!.selfVideoRenderer.setMirror(currentCameraIsFront) + } - override fun onCameraSwitchError(s: String) { - Log.e(TAG, "Error while switching camera: $s") + override fun onCameraSwitchError(s: String) { + Log.e(TAG, "Error while switching camera: $s") + } } - }) + ) } private fun toggleMedia(enable: Boolean, video: Boolean) { @@ -1391,60 +1429,66 @@ class CallActivity : CallBaseActivity() { .alpha(alpha) .setDuration(duration) .setStartDelay(startDelay) - .setListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - super.onAnimationEnd(animation) - if (!show) { - binding!!.callControls.visibility = View.GONE - if (spotlightView != null && spotlightView!!.visibility != View.GONE) { - spotlightView!!.visibility = View.GONE - } - } else { - callControlHandler.postDelayed({ - if (!isPushToTalkActive) { - animateCallControls(false, 0) + .setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + super.onAnimationEnd(animation) + if (!show) { + binding!!.callControls.visibility = View.GONE + if (spotlightView != null && spotlightView!!.visibility != View.GONE) { + spotlightView!!.visibility = View.GONE } - }, 7500) + } else { + callControlHandler.postDelayed({ + if (!isPushToTalkActive) { + animateCallControls(false, 0) + } + }, 7500) + } + binding!!.callControls.isEnabled = true } - binding!!.callControls.isEnabled = true } - }) + ) binding!!.callInfosLinearLayout.isEnabled = false binding!!.callInfosLinearLayout.animate() .translationY(0f) .alpha(alpha) .setDuration(duration) .setStartDelay(startDelay) - .setListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - super.onAnimationEnd(animation) - if (!show) { - binding!!.callInfosLinearLayout.visibility = View.GONE - } else { - callInfosHandler.postDelayed({ - if (!isPushToTalkActive) { - animateCallControls(false, 0) - } - }, 7500) + .setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + super.onAnimationEnd(animation) + if (!show) { + binding!!.callInfosLinearLayout.visibility = View.GONE + } else { + callInfosHandler.postDelayed({ + if (!isPushToTalkActive) { + animateCallControls(false, 0) + } + }, 7500) + } + binding!!.callInfosLinearLayout.isEnabled = true } - binding!!.callInfosLinearLayout.isEnabled = true } - }) + ) binding!!.switchSelfVideoButton.isEnabled = false binding!!.switchSelfVideoButton.animate() .translationY(0f) .alpha(alpha) .setDuration(duration) .setStartDelay(startDelay) - .setListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - super.onAnimationEnd(animation) - if (!show) { - binding!!.switchSelfVideoButton.visibility = View.GONE + .setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + super.onAnimationEnd(animation) + if (!show) { + binding!!.switchSelfVideoButton.visibility = View.GONE + } + binding!!.switchSelfVideoButton.isEnabled = true } - binding!!.switchSelfVideoButton.isEnabled = true } - }) + ) } } @@ -1474,65 +1518,64 @@ class CallActivity : CallBaseActivity() { .subscribeOn(Schedulers.io()) .retry(API_RETRIES) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(signalingSettingsOverall: SignalingSettingsOverall) { - if (signalingSettingsOverall.ocs != null && - signalingSettingsOverall.ocs!!.settings != null - ) { - externalSignalingServer = ExternalSignalingServer() - if (!TextUtils.isEmpty(signalingSettingsOverall.ocs!!.settings!!.externalSignalingServer) && - !TextUtils.isEmpty(signalingSettingsOverall.ocs!!.settings!!.externalSignalingTicket) + override fun onNext(signalingSettingsOverall: SignalingSettingsOverall) { + if (signalingSettingsOverall.ocs != null && + signalingSettingsOverall.ocs!!.settings != null ) { externalSignalingServer = ExternalSignalingServer() - externalSignalingServer!!.externalSignalingServer = - signalingSettingsOverall.ocs!!.settings!!.externalSignalingServer - externalSignalingServer!!.externalSignalingTicket = - signalingSettingsOverall.ocs!!.settings!!.externalSignalingTicket - hasExternalSignalingServer = true - } else { - hasExternalSignalingServer = false - } - Log.d(TAG, " hasExternalSignalingServer: $hasExternalSignalingServer") - - if ("?" != conversationUser!!.userId && conversationUser!!.id != null) { - Log.d( - TAG, - "Update externalSignalingServer for: " + conversationUser!!.id + - " / " + conversationUser!!.userId - ) - userManager!!.updateExternalSignalingServer( - conversationUser!!.id!!, - externalSignalingServer!! - ) - .subscribeOn(Schedulers.io()) - .subscribe() - } else { - conversationUser!!.externalSignalingServer = externalSignalingServer - } + if (!TextUtils.isEmpty(signalingSettingsOverall.ocs!!.settings!!.externalSignalingServer) && + !TextUtils.isEmpty(signalingSettingsOverall.ocs!!.settings!!.externalSignalingTicket) + ) { + externalSignalingServer = ExternalSignalingServer() + externalSignalingServer!!.externalSignalingServer = + signalingSettingsOverall.ocs!!.settings!!.externalSignalingServer + externalSignalingServer!!.externalSignalingTicket = + signalingSettingsOverall.ocs!!.settings!!.externalSignalingTicket + hasExternalSignalingServer = true + } else { + hasExternalSignalingServer = false + } + Log.d(TAG, " hasExternalSignalingServer: $hasExternalSignalingServer") + + if ("?" != conversationUser!!.userId && conversationUser!!.id != null) { + Log.d( + TAG, + "Update externalSignalingServer for: " + conversationUser!!.id + + " / " + conversationUser!!.userId + ) + userManager!!.updateExternalSignalingServer( + conversationUser!!.id!!, + externalSignalingServer!! + ) + .subscribeOn(Schedulers.io()) + .subscribe() + } else { + conversationUser!!.externalSignalingServer = externalSignalingServer + } - addIceServers(signalingSettingsOverall, apiVersion) + addIceServers(signalingSettingsOverall, apiVersion) + } + checkCapabilities() } - checkCapabilities() - } - override fun onError(e: Throwable) { - Log.e(TAG, e.message, e) - } + override fun onError(e: Throwable) { + Log.e(TAG, e.message, e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } - private fun addIceServers( - signalingSettingsOverall: SignalingSettingsOverall, - apiVersion: Int - ) { + private fun addIceServers(signalingSettingsOverall: SignalingSettingsOverall, apiVersion: Int) { if (signalingSettingsOverall.ocs!!.settings!!.stunServers != null) { val stunServers = signalingSettingsOverall.ocs!!.settings!!.stunServers if (apiVersion == ApiUtils.APIv3) { @@ -1572,34 +1615,36 @@ class CallActivity : CallBaseActivity() { .retry(API_RETRIES) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(capabilitiesOverall: CapabilitiesOverall) { - // FIXME check for compatible Call API version - if (hasExternalSignalingServer) { - setupAndInitiateWebSocketsConnection() - } else { - signalingMessageReceiver = internalSignalingMessageReceiver - signalingMessageReceiver!!.addListener(localParticipantMessageListener) - signalingMessageReceiver!!.addListener(offerMessageListener) - signalingMessageSender = internalSignalingMessageSender - joinRoomAndCall() + override fun onNext(capabilitiesOverall: CapabilitiesOverall) { + // FIXME check for compatible Call API version + if (hasExternalSignalingServer) { + setupAndInitiateWebSocketsConnection() + } else { + signalingMessageReceiver = internalSignalingMessageReceiver + signalingMessageReceiver!!.addListener(localParticipantMessageListener) + signalingMessageReceiver!!.addListener(offerMessageListener) + signalingMessageSender = internalSignalingMessageSender + joinRoomAndCall() + } } - } - override fun onError(e: Throwable) { - Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - Log.e(TAG, "Failed to fetch capabilities", e) - // unused atm - } + override fun onError(e: Throwable) { + Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + Log.e(TAG, "Failed to fetch capabilities", e) + // unused atm + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun joinRoomAndCall() { @@ -1618,30 +1663,32 @@ class CallActivity : CallBaseActivity() { .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(API_RETRIES) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(roomOverall: RoomOverall) { - val conversation = roomOverall.ocs!!.data - callRecordingViewModel!!.setRecordingState(conversation!!.callRecording) - callSession = conversation.sessionId - Log.d(TAG, " new callSession by joinRoom= $callSession") + override fun onNext(roomOverall: RoomOverall) { + val conversation = roomOverall.ocs!!.data + callRecordingViewModel!!.setRecordingState(conversation!!.callRecording) + callSession = conversation.sessionId + Log.d(TAG, " new callSession by joinRoom= $callSession") - setInitialApplicationWideCurrentRoomHolderValues(conversation) + setInitialApplicationWideCurrentRoomHolderValues(conversation) - callOrJoinRoomViaWebSocket() - } + callOrJoinRoomViaWebSocket() + } - override fun onError(e: Throwable) { - Log.e(TAG, "joinRoom onError", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "joinRoom onError", e) + } - override fun onComplete() { - Log.d(TAG, "joinRoom onComplete") + override fun onComplete() { + Log.d(TAG, "joinRoom onComplete") + } } - }) + ) } else { // we are in a room and start a call -> same session needs to be used callOrJoinRoomViaWebSocket() @@ -1658,55 +1705,58 @@ class CallActivity : CallBaseActivity() { private fun performCall() { fun getRoomAndContinue() { - val getRoomApiVersion = ApiUtils.getConversationApiVersion( - conversationUser, - intArrayOf(ApiUtils.APIv4, 1) - ) + val getRoomApiVersion = + ApiUtils.getConversationApiVersion( + conversationUser, + intArrayOf(ApiUtils.APIv4, 1) + ) ncApi!!.getRoom(credentials, ApiUtils.getUrlForRoom(getRoomApiVersion, baseUrl, roomToken)) .retry(API_RETRIES) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(roomOverall: RoomOverall) { - val conversation = roomOverall.ocs!!.data - callRecordingViewModel!!.setRecordingState(conversation!!.callRecording) - callSession = conversation.sessionId + override fun onNext(roomOverall: RoomOverall) { + val conversation = roomOverall.ocs!!.data + callRecordingViewModel!!.setRecordingState(conversation!!.callRecording) + callSession = conversation.sessionId - setInitialApplicationWideCurrentRoomHolderValues(conversation) + setInitialApplicationWideCurrentRoomHolderValues(conversation) - startCallTimeCounter(conversation.callStartTime) + startCallTimeCounter(conversation.callStartTime) - if (currentCallStatus !== CallStatus.LEAVING) { - if (currentCallStatus !== CallStatus.IN_CONVERSATION) { - setCallState(CallStatus.JOINED) - } - ApplicationWideCurrentRoomHolder.getInstance().isInCall = true - ApplicationWideCurrentRoomHolder.getInstance().isDialing = false - if (!TextUtils.isEmpty(roomToken)) { - cancelExistingNotificationsForRoom( - applicationContext, - conversationUser!!, - roomToken!! - ) - } - if (!hasExternalSignalingServer) { - pullSignalingMessages() + if (currentCallStatus !== CallStatus.LEAVING) { + if (currentCallStatus !== CallStatus.IN_CONVERSATION) { + setCallState(CallStatus.JOINED) + } + ApplicationWideCurrentRoomHolder.getInstance().isInCall = true + ApplicationWideCurrentRoomHolder.getInstance().isDialing = false + if (!TextUtils.isEmpty(roomToken)) { + cancelExistingNotificationsForRoom( + applicationContext, + conversationUser!!, + roomToken!! + ) + } + if (!hasExternalSignalingServer) { + pullSignalingMessages() + } } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to get room", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to get room", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } var inCallFlag = Participant.InCallFlags.IN_CALL @@ -1730,25 +1780,27 @@ class CallActivity : CallBaseActivity() { .subscribeOn(Schedulers.io()) .retry(API_RETRIES) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - getRoomAndContinue() - } + override fun onNext(genericOverall: GenericOverall) { + getRoomAndContinue() + } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to join call", e) - Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - hangup(true) - } + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to join call", e) + Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + hangup(true) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun setInitialApplicationWideCurrentRoomHolderValues(conversation: Conversation) { @@ -1765,21 +1817,22 @@ class CallActivity : CallBaseActivity() { val currentTimeInSec = System.currentTimeMillis() / SECOND_IN_MILLIES elapsedSeconds = currentTimeInSec - callStartTime - val callTimeTask: Runnable = object : Runnable { - override fun run() { - if (othersInCall) { - binding!!.callDuration.text = DateUtils.formatElapsedTime(elapsedSeconds) - if (elapsedSeconds.toInt() == CALL_TIME_ONE_HOUR) { - showCallRunningSinceOneHourOrMoreInfo() + val callTimeTask: Runnable = + object : Runnable { + override fun run() { + if (othersInCall) { + binding!!.callDuration.text = DateUtils.formatElapsedTime(elapsedSeconds) + if (elapsedSeconds.toInt() == CALL_TIME_ONE_HOUR) { + showCallRunningSinceOneHourOrMoreInfo() + } + } else { + binding!!.callDuration.text = CALL_DURATION_EMPTY } - } else { - binding!!.callDuration.text = CALL_DURATION_EMPTY - } - elapsedSeconds += 1 - callTimeHandler.postDelayed(this, CALL_TIME_COUNTER_DELAY) + elapsedSeconds += 1 + callTimeHandler.postDelayed(this, CALL_TIME_COUNTER_DELAY) + } } - } callTimeHandler.post(callTimeTask) } else { binding!!.callDuration.visibility = View.GONE @@ -1826,23 +1879,25 @@ class CallActivity : CallBaseActivity() { Observable.timer(delayOnError.get().toLong(), TimeUnit.SECONDS) } } - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - signalingDisposable = d - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + signalingDisposable = d + } - override fun onNext(signalingOverall: SignalingOverall) { - receivedSignalingMessages(signalingOverall.ocs!!.signalings) - } + override fun onNext(signalingOverall: SignalingOverall) { + receivedSignalingMessages(signalingOverall.ocs!!.signalings) + } - override fun onError(e: Throwable) { - dispose(signalingDisposable) - } + override fun onError(e: Throwable) { + dispose(signalingDisposable) + } - override fun onComplete() { - dispose(signalingDisposable) + override fun onComplete() { + dispose(signalingDisposable) + } } - }) + ) } private fun setupAndInitiateWebSocketsConnection() { @@ -1850,12 +1905,13 @@ class CallActivity : CallBaseActivity() { webSocketConnectionHelper = WebSocketConnectionHelper() } if (webSocketClient == null) { - webSocketClient = WebSocketConnectionHelper.getExternalSignalingInstanceForServer( - externalSignalingServer!!.externalSignalingServer, - conversationUser, - externalSignalingServer!!.externalSignalingTicket, - TextUtils.isEmpty(credentials) - ) + webSocketClient = + WebSocketConnectionHelper.getExternalSignalingInstanceForServer( + externalSignalingServer!!.externalSignalingServer, + conversationUser, + externalSignalingServer!!.externalSignalingTicket, + TextUtils.isEmpty(credentials) + ) // Although setupAndInitiateWebSocketsConnection could be called several times the web socket is // initialized just once, so the message receiver is also initialized just once. signalingMessageReceiver = webSocketClient!!.getSignalingMessageReceiver() @@ -1953,10 +2009,11 @@ class CallActivity : CallBaseActivity() { internalSignalingMessageReceiver.process(signaling.messageWrapper as List?>?) "message" -> { - val ncSignalingMessage = LoganSquare.parse( - signaling.messageWrapper.toString(), - NCSignalingMessage::class.java - ) + val ncSignalingMessage = + LoganSquare.parse( + signaling.messageWrapper.toString(), + NCSignalingMessage::class.java + ) internalSignalingMessageReceiver.process(ncSignalingMessage) } @@ -2043,41 +2100,43 @@ class CallActivity : CallBaseActivity() { ncApi!!.leaveCall(credentials, ApiUtils.getUrlForCall(apiVersion, baseUrl, roomToken)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - if (!switchToRoomToken.isEmpty()) { - val intent = Intent(context, ChatActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - val bundle = Bundle() - bundle.putBoolean(KEY_SWITCH_TO_ROOM, true) - bundle.putBoolean(KEY_START_CALL_AFTER_ROOM_SWITCH, true) - bundle.putString(KEY_ROOM_TOKEN, switchToRoomToken) - bundle.putBoolean(KEY_CALL_VOICE_ONLY, isVoiceOnlyCall) - intent.putExtras(bundle) - startActivity(intent) - finish() - } else if (shutDownView) { - finish() - } else if (currentCallStatus === CallStatus.RECONNECTING || - currentCallStatus === CallStatus.PUBLISHER_FAILED - ) { - initiateCall() + override fun onNext(genericOverall: GenericOverall) { + if (!switchToRoomToken.isEmpty()) { + val intent = Intent(context, ChatActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + val bundle = Bundle() + bundle.putBoolean(KEY_SWITCH_TO_ROOM, true) + bundle.putBoolean(KEY_START_CALL_AFTER_ROOM_SWITCH, true) + bundle.putString(KEY_ROOM_TOKEN, switchToRoomToken) + bundle.putBoolean(KEY_CALL_VOICE_ONLY, isVoiceOnlyCall) + intent.putExtras(bundle) + startActivity(intent) + finish() + } else if (shutDownView) { + finish() + } else if (currentCallStatus === CallStatus.RECONNECTING || + currentCallStatus === CallStatus.PUBLISHER_FAILED + ) { + initiateCall() + } } - } - override fun onError(e: Throwable) { - Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - Log.e(TAG, "Error while leaving the call", e) - } + override fun onError(e: Throwable) { + Snackbar.make(binding!!.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + Log.e(TAG, "Error while leaving the call", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun startVideoCapture() { @@ -2183,11 +2242,12 @@ class CallActivity : CallBaseActivity() { callParticipants[sessionId]!!.setInternal(participant.internal) } - val nick: String? = if (hasExternalSignalingServer) { - webSocketClient!!.getDisplayNameForSession(sessionId) - } else { - if (offerAnswerNickProviders[sessionId] != null) offerAnswerNickProviders[sessionId]?.nick else "" - } + val nick: String? = + if (hasExternalSignalingServer) { + webSocketClient!!.getDisplayNameForSession(sessionId) + } else { + if (offerAnswerNickProviders[sessionId] != null) offerAnswerNickProviders[sessionId]?.nick else "" + } callParticipants[sessionId]!!.setNick(nick) val participantHasAudioOrVideo = participantInCallFlagsHaveAudioOrVideo(participant) @@ -2203,11 +2263,12 @@ class CallActivity : CallBaseActivity() { getOrCreatePeerConnectionWrapperForSessionIdAndType(sessionId, VIDEO_STREAM_TYPE_VIDEO, false) } } - othersInCall = if (selfJoined) { - joined.size > 1 - } else { - joined.isNotEmpty() - } + othersInCall = + if (selfJoined) { + joined.size > 1 + } else { + joined.isNotEmpty() + } if (othersInCall && currentCallStatus !== CallStatus.IN_CONVERSATION) { setCallState(CallStatus.IN_CONVERSATION) @@ -2260,50 +2321,22 @@ class CallActivity : CallBaseActivity() { hangup(true) return null } - peerConnectionWrapper = if (hasMCU && publisher) { - PeerConnectionWrapper( - peerConnectionFactory, - iceServers, - sdpConstraintsForMCU, - sessionId, - callSession, - localStream, - true, - true, - type, - signalingMessageReceiver, - signalingMessageSender - ) - } else if (hasMCU) { - PeerConnectionWrapper( - peerConnectionFactory, - iceServers, - sdpConstraints, - sessionId, - callSession, - null, - false, - true, - type, - signalingMessageReceiver, - signalingMessageSender - ) - } else { - if ("screen" != type) { + peerConnectionWrapper = + if (hasMCU && publisher) { PeerConnectionWrapper( peerConnectionFactory, iceServers, - sdpConstraints, + sdpConstraintsForMCU, sessionId, callSession, localStream, - false, - false, + true, + true, type, signalingMessageReceiver, signalingMessageSender ) - } else { + } else if (hasMCU) { PeerConnectionWrapper( peerConnectionFactory, iceServers, @@ -2312,13 +2345,42 @@ class CallActivity : CallBaseActivity() { callSession, null, false, - false, + true, type, signalingMessageReceiver, signalingMessageSender ) + } else { + if ("screen" != type) { + PeerConnectionWrapper( + peerConnectionFactory, + iceServers, + sdpConstraints, + sessionId, + callSession, + localStream, + false, + false, + type, + signalingMessageReceiver, + signalingMessageSender + ) + } else { + PeerConnectionWrapper( + peerConnectionFactory, + iceServers, + sdpConstraints, + sessionId, + callSession, + null, + false, + false, + type, + signalingMessageReceiver, + signalingMessageSender + ) + } } - } peerConnectionWrapperList.add(peerConnectionWrapper) if (!publisher) { var callParticipant = callParticipants[sessionId] @@ -2428,8 +2490,9 @@ class CallActivity : CallBaseActivity() { } private fun updateSelfVideoViewIceConnectionState(iceConnectionState: IceConnectionState) { - val connected = iceConnectionState == IceConnectionState.CONNECTED || - iceConnectionState == IceConnectionState.COMPLETED + val connected = + iceConnectionState == IceConnectionState.CONNECTED || + iceConnectionState == IceConnectionState.COMPLETED // FIXME In voice only calls there is no video view, so the progress bar would appear floating in the middle of // nowhere. However, a way to signal that the local participant is not connected to the HPB is still need in @@ -2449,11 +2512,12 @@ class CallActivity : CallBaseActivity() { val screenWidthPx = displayMetrics.widthPixels val screenWidthDp = DisplayUtils.convertPixelToDp(screenWidthPx.toFloat(), applicationContext).toInt() var newXafterRotate = 0f - val newYafterRotate: Float = if (binding!!.callInfosLinearLayout.visibility == View.VISIBLE) { - 250f - } else { - 20f - } + val newYafterRotate: Float = + if (binding!!.callInfosLinearLayout.visibility == View.VISIBLE) { + 250f + } else { + 20f + } if (resources.configuration.orientation == Configuration.ORIENTATION_LANDSCAPE) { layoutParams.height = resources.getDimension(R.dimen.call_self_video_short_side_length).toInt() layoutParams.width = resources.getDimension(R.dimen.call_self_video_long_side_length).toInt() @@ -2475,8 +2539,9 @@ class CallActivity : CallBaseActivity() { @Subscribe(threadMode = ThreadMode.MAIN) fun onMessageEvent(proximitySensorEvent: ProximitySensorEvent) { if (!isVoiceOnlyCall) { - val enableVideo = proximitySensorEvent.proximitySensorEventType == - ProximitySensorEvent.ProximitySensorEventType.SENSOR_FAR && videoOn + val enableVideo = + proximitySensorEvent.proximitySensorEventType == + ProximitySensorEvent.ProximitySensorEventType.SENSOR_FAR && videoOn if (permissionUtil!!.isCameraPermissionGranted() && (currentCallStatus === CallStatus.CONNECTING || isConnectionEstablished) && videoOn && enableVideo != localVideoTrack!!.enabled() @@ -2499,23 +2564,25 @@ class CallActivity : CallBaseActivity() { .interval(1, TimeUnit.SECONDS) .repeatUntil { !isConnectionEstablished || isDestroyed } .observeOn(Schedulers.io()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(aLong: Long) { - peerConnectionWrapper.sendChannelData(dataChannelMessage) - } + override fun onNext(aLong: Long) { + peerConnectionWrapper.sendChannelData(dataChannelMessage) + } - override fun onError(e: Throwable) { - // unused atm - } + override fun onError(e: Throwable) { + // unused atm + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) break } } @@ -2526,13 +2593,14 @@ class CallActivity : CallBaseActivity() { return } val defaultGuestNick = resources.getString(R.string.nc_nick_guest) - val participantDisplayItem = ParticipantDisplayItem( - baseUrl, - defaultGuestNick, - rootEglBase, - videoStreamType, - callParticipantModel - ) + val participantDisplayItem = + ParticipantDisplayItem( + baseUrl, + defaultGuestNick, + rootEglBase, + videoStreamType, + callParticipantModel + ) val sessionId = callParticipantModel.sessionId participantDisplayItems!!["$sessionId-$videoStreamType"] = participantDisplayItem initGridAdapter() @@ -2547,83 +2615,87 @@ class CallActivity : CallBaseActivity() { handler!!.removeCallbacksAndMessages(null) } when (callState) { - CallStatus.CONNECTING -> handler!!.post { - playCallingSound() - if (isIncomingCallFromNotification) { - binding!!.callStates.callStateTextView.setText(R.string.nc_call_incoming) - } else { - binding!!.callStates.callStateTextView.setText(R.string.nc_call_ringing) - } - binding!!.callConversationNameTextView.text = conversationName - binding!!.callModeTextView.text = descriptionForCallType - if (binding!!.callStates.callStateRelativeLayout.visibility != View.VISIBLE) { - binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE - } - if (binding!!.gridview.visibility != View.INVISIBLE) { - binding!!.gridview.visibility = View.INVISIBLE - } - if (binding!!.callStates.callStateProgressBar.visibility != View.VISIBLE) { - binding!!.callStates.callStateProgressBar.visibility = View.VISIBLE - } - if (binding!!.callStates.errorImageView.visibility != View.GONE) { - binding!!.callStates.errorImageView.visibility = View.GONE + CallStatus.CONNECTING -> + handler!!.post { + playCallingSound() + if (isIncomingCallFromNotification) { + binding!!.callStates.callStateTextView.setText(R.string.nc_call_incoming) + } else { + binding!!.callStates.callStateTextView.setText(R.string.nc_call_ringing) + } + binding!!.callConversationNameTextView.text = conversationName + binding!!.callModeTextView.text = descriptionForCallType + if (binding!!.callStates.callStateRelativeLayout.visibility != View.VISIBLE) { + binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE + } + if (binding!!.gridview.visibility != View.INVISIBLE) { + binding!!.gridview.visibility = View.INVISIBLE + } + if (binding!!.callStates.callStateProgressBar.visibility != View.VISIBLE) { + binding!!.callStates.callStateProgressBar.visibility = View.VISIBLE + } + if (binding!!.callStates.errorImageView.visibility != View.GONE) { + binding!!.callStates.errorImageView.visibility = View.GONE + } } - } - CallStatus.CALLING_TIMEOUT -> handler!!.post { - hangup(false) - binding!!.callStates.callStateTextView.setText(R.string.nc_call_timeout) - binding!!.callModeTextView.text = descriptionForCallType - if (binding!!.callStates.callStateRelativeLayout.visibility != View.VISIBLE) { - binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE - } - if (binding!!.callStates.callStateProgressBar.visibility != View.GONE) { - binding!!.callStates.callStateProgressBar.visibility = View.GONE - } - if (binding!!.gridview.visibility != View.INVISIBLE) { - binding!!.gridview.visibility = View.INVISIBLE - } - binding!!.callStates.errorImageView.setImageResource(R.drawable.ic_av_timer_timer_24dp) - if (binding!!.callStates.errorImageView.visibility != View.VISIBLE) { - binding!!.callStates.errorImageView.visibility = View.VISIBLE + CallStatus.CALLING_TIMEOUT -> + handler!!.post { + hangup(false) + binding!!.callStates.callStateTextView.setText(R.string.nc_call_timeout) + binding!!.callModeTextView.text = descriptionForCallType + if (binding!!.callStates.callStateRelativeLayout.visibility != View.VISIBLE) { + binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE + } + if (binding!!.callStates.callStateProgressBar.visibility != View.GONE) { + binding!!.callStates.callStateProgressBar.visibility = View.GONE + } + if (binding!!.gridview.visibility != View.INVISIBLE) { + binding!!.gridview.visibility = View.INVISIBLE + } + binding!!.callStates.errorImageView.setImageResource(R.drawable.ic_av_timer_timer_24dp) + if (binding!!.callStates.errorImageView.visibility != View.VISIBLE) { + binding!!.callStates.errorImageView.visibility = View.VISIBLE + } } - } - CallStatus.PUBLISHER_FAILED -> handler!!.post { - // No calling sound when the publisher failed - binding!!.callStates.callStateTextView.setText(R.string.nc_call_reconnecting) - binding!!.callModeTextView.text = descriptionForCallType - if (binding!!.callStates.callStateRelativeLayout.visibility != View.VISIBLE) { - binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE - } - if (binding!!.gridview.visibility != View.INVISIBLE) { - binding!!.gridview.visibility = View.INVISIBLE - } - if (binding!!.callStates.callStateProgressBar.visibility != View.VISIBLE) { - binding!!.callStates.callStateProgressBar.visibility = View.VISIBLE - } - if (binding!!.callStates.errorImageView.visibility != View.GONE) { - binding!!.callStates.errorImageView.visibility = View.GONE + CallStatus.PUBLISHER_FAILED -> + handler!!.post { + // No calling sound when the publisher failed + binding!!.callStates.callStateTextView.setText(R.string.nc_call_reconnecting) + binding!!.callModeTextView.text = descriptionForCallType + if (binding!!.callStates.callStateRelativeLayout.visibility != View.VISIBLE) { + binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE + } + if (binding!!.gridview.visibility != View.INVISIBLE) { + binding!!.gridview.visibility = View.INVISIBLE + } + if (binding!!.callStates.callStateProgressBar.visibility != View.VISIBLE) { + binding!!.callStates.callStateProgressBar.visibility = View.VISIBLE + } + if (binding!!.callStates.errorImageView.visibility != View.GONE) { + binding!!.callStates.errorImageView.visibility = View.GONE + } } - } - CallStatus.RECONNECTING -> handler!!.post { - playCallingSound() - binding!!.callStates.callStateTextView.setText(R.string.nc_call_reconnecting) - binding!!.callModeTextView.text = descriptionForCallType - if (binding!!.callStates.callStateRelativeLayout.visibility != View.VISIBLE) { - binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE - } - if (binding!!.gridview.visibility != View.INVISIBLE) { - binding!!.gridview.visibility = View.INVISIBLE - } - if (binding!!.callStates.callStateProgressBar.visibility != View.VISIBLE) { - binding!!.callStates.callStateProgressBar.visibility = View.VISIBLE - } - if (binding!!.callStates.errorImageView.visibility != View.GONE) { - binding!!.callStates.errorImageView.visibility = View.GONE + CallStatus.RECONNECTING -> + handler!!.post { + playCallingSound() + binding!!.callStates.callStateTextView.setText(R.string.nc_call_reconnecting) + binding!!.callModeTextView.text = descriptionForCallType + if (binding!!.callStates.callStateRelativeLayout.visibility != View.VISIBLE) { + binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE + } + if (binding!!.gridview.visibility != View.INVISIBLE) { + binding!!.gridview.visibility = View.INVISIBLE + } + if (binding!!.callStates.callStateProgressBar.visibility != View.VISIBLE) { + binding!!.callStates.callStateProgressBar.visibility = View.VISIBLE + } + if (binding!!.callStates.errorImageView.visibility != View.GONE) { + binding!!.callStates.errorImageView.visibility = View.GONE + } } - } CallStatus.JOINED -> { handler!!.postDelayed({ setCallState(CallStatus.CALLING_TIMEOUT) }, 45000) @@ -2649,58 +2721,61 @@ class CallActivity : CallBaseActivity() { } } - CallStatus.IN_CONVERSATION -> handler!!.post { - stopCallingSound() - binding!!.callModeTextView.text = descriptionForCallType - if (!isVoiceOnlyCall) { - binding!!.callInfosLinearLayout.visibility = View.GONE - } - if (!isPushToTalkActive) { - animateCallControls(false, 5000) - } - if (binding!!.callStates.callStateRelativeLayout.visibility != View.INVISIBLE) { - binding!!.callStates.callStateRelativeLayout.visibility = View.INVISIBLE - } - if (binding!!.callStates.callStateProgressBar.visibility != View.GONE) { - binding!!.callStates.callStateProgressBar.visibility = View.GONE - } - if (binding!!.gridview.visibility != View.VISIBLE) { - binding!!.gridview.visibility = View.VISIBLE - } - if (binding!!.callStates.errorImageView.visibility != View.GONE) { - binding!!.callStates.errorImageView.visibility = View.GONE + CallStatus.IN_CONVERSATION -> + handler!!.post { + stopCallingSound() + binding!!.callModeTextView.text = descriptionForCallType + if (!isVoiceOnlyCall) { + binding!!.callInfosLinearLayout.visibility = View.GONE + } + if (!isPushToTalkActive) { + animateCallControls(false, 5000) + } + if (binding!!.callStates.callStateRelativeLayout.visibility != View.INVISIBLE) { + binding!!.callStates.callStateRelativeLayout.visibility = View.INVISIBLE + } + if (binding!!.callStates.callStateProgressBar.visibility != View.GONE) { + binding!!.callStates.callStateProgressBar.visibility = View.GONE + } + if (binding!!.gridview.visibility != View.VISIBLE) { + binding!!.gridview.visibility = View.VISIBLE + } + if (binding!!.callStates.errorImageView.visibility != View.GONE) { + binding!!.callStates.errorImageView.visibility = View.GONE + } } - } - CallStatus.OFFLINE -> handler!!.post { - stopCallingSound() - binding!!.callStates.callStateTextView.setText(R.string.nc_offline) - if (binding!!.callStates.callStateRelativeLayout.visibility != View.VISIBLE) { - binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE - } - if (binding!!.gridview.visibility != View.INVISIBLE) { - binding!!.gridview.visibility = View.INVISIBLE - } - if (binding!!.callStates.callStateProgressBar.visibility != View.GONE) { - binding!!.callStates.callStateProgressBar.visibility = View.GONE - } - binding!!.callStates.errorImageView.setImageResource(R.drawable.ic_signal_wifi_off_white_24dp) - if (binding!!.callStates.errorImageView.visibility != View.VISIBLE) { - binding!!.callStates.errorImageView.visibility = View.VISIBLE + CallStatus.OFFLINE -> + handler!!.post { + stopCallingSound() + binding!!.callStates.callStateTextView.setText(R.string.nc_offline) + if (binding!!.callStates.callStateRelativeLayout.visibility != View.VISIBLE) { + binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE + } + if (binding!!.gridview.visibility != View.INVISIBLE) { + binding!!.gridview.visibility = View.INVISIBLE + } + if (binding!!.callStates.callStateProgressBar.visibility != View.GONE) { + binding!!.callStates.callStateProgressBar.visibility = View.GONE + } + binding!!.callStates.errorImageView.setImageResource(R.drawable.ic_signal_wifi_off_white_24dp) + if (binding!!.callStates.errorImageView.visibility != View.VISIBLE) { + binding!!.callStates.errorImageView.visibility = View.VISIBLE + } } - } - CallStatus.LEAVING -> handler!!.post { - if (!isDestroyed) { - stopCallingSound() - binding!!.callModeTextView.text = descriptionForCallType - binding!!.callStates.callStateTextView.setText(R.string.nc_leaving_call) - binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE - binding!!.gridview.visibility = View.INVISIBLE - binding!!.callStates.callStateProgressBar.visibility = View.VISIBLE - binding!!.callStates.errorImageView.visibility = View.GONE + CallStatus.LEAVING -> + handler!!.post { + if (!isDestroyed) { + stopCallingSound() + binding!!.callModeTextView.text = descriptionForCallType + binding!!.callStates.callStateTextView.setText(R.string.nc_leaving_call) + binding!!.callStates.callStateRelativeLayout.visibility = View.VISIBLE + binding!!.gridview.visibility = View.INVISIBLE + binding!!.callStates.callStateProgressBar.visibility = View.VISIBLE + binding!!.callStates.errorImageView.visibility = View.GONE + } } - } } } } @@ -2717,21 +2792,23 @@ class CallActivity : CallBaseActivity() { private fun playCallingSound() { stopCallingSound() - val ringtoneUri: Uri? = if (isIncomingCallFromNotification) { - getCallRingtoneUri(applicationContext, appPreferences) - } else { - Uri.parse("android.resource://" + applicationContext.packageName + "/raw/tr110_1_kap8_3_freiton1") - } + val ringtoneUri: Uri? = + if (isIncomingCallFromNotification) { + getCallRingtoneUri(applicationContext, appPreferences) + } else { + Uri.parse("android.resource://" + applicationContext.packageName + "/raw/tr110_1_kap8_3_freiton1") + } if (ringtoneUri != null) { mediaPlayer = MediaPlayer() try { mediaPlayer!!.setDataSource(this, ringtoneUri) mediaPlayer!!.isLooping = true - val audioAttributes = AudioAttributes.Builder().setContentType( - AudioAttributes.CONTENT_TYPE_SONIFICATION - ) - .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) - .build() + val audioAttributes = + AudioAttributes.Builder().setContentType( + AudioAttributes.CONTENT_TYPE_SONIFICATION + ) + .setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION) + .build() mediaPlayer!!.setAudioAttributes(audioAttributes) mediaPlayer!!.setOnPreparedListener { mp: MediaPlayer? -> mediaPlayer!!.start() } mediaPlayer!!.prepareAsync() @@ -2899,12 +2976,13 @@ class CallActivity : CallBaseActivity() { private inner class InternalSignalingMessageSender : SignalingMessageSender { override fun send(ncSignalingMessage: NCSignalingMessage) { addLocalParticipantNickIfNeeded(ncSignalingMessage) - val serializedNcSignalingMessage: String = try { - LoganSquare.serialize(ncSignalingMessage) - } catch (e: IOException) { - Log.e(TAG, "Failed to serialize signaling message", e) - return - } + val serializedNcSignalingMessage: String = + try { + LoganSquare.serialize(ncSignalingMessage) + } catch (e: IOException) { + Log.e(TAG, "Failed to serialize signaling message", e) + return + } // The message wrapper can not be defined in a JSON model to be directly serialized, as sent messages // need to be serialized twice; first the signaling message, and then the wrapper as a whole. Received @@ -2931,26 +3009,28 @@ class CallActivity : CallBaseActivity() { ) .retry(API_RETRIES) .subscribeOn(Schedulers.io()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(signalingOverall: SignalingOverall) { - // When sending messages to the internal signaling server the response has been empty since - // Talk v2.9.0, so it is not really needed to process it, but there is no harm either in - // doing that, as technically messages could be returned. - receivedSignalingMessages(signalingOverall.ocs!!.signalings) - } + override fun onNext(signalingOverall: SignalingOverall) { + // When sending messages to the internal signaling server the response has been empty since + // Talk v2.9.0, so it is not really needed to process it, but there is no harm either in + // doing that, as technically messages could be returned. + receivedSignalingMessages(signalingOverall.ocs!!.signalings) + } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to send signaling message", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to send signaling message", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } /** @@ -2967,9 +3047,10 @@ class CallActivity : CallBaseActivity() { if ("offer" != type && "answer" != type) { return } - val payload = ncSignalingMessage.payload - ?: // Broken message, this should not happen - return + val payload = + ncSignalingMessage.payload + ?: // Broken message, this should not happen + return payload.nick = conversationUser!!.displayName } } @@ -3011,16 +3092,17 @@ class CallActivity : CallBaseActivity() { Log.d(TAG, "isInPictureInPictureMode= $isInPictureInPictureMode") isInPipMode = isInPictureInPictureMode if (isInPictureInPictureMode) { - mReceiver = object : BroadcastReceiver() { - override fun onReceive(context: Context, intent: Intent) { - if (MICROPHONE_PIP_INTENT_NAME != intent.action) { - return - } - when (intent.getIntExtra(MICROPHONE_PIP_INTENT_EXTRA_ACTION, 0)) { - MICROPHONE_PIP_REQUEST_MUTE, MICROPHONE_PIP_REQUEST_UNMUTE -> onMicrophoneClick() + mReceiver = + object : BroadcastReceiver() { + override fun onReceive(context: Context, intent: Intent) { + if (MICROPHONE_PIP_INTENT_NAME != intent.action) { + return + } + when (intent.getIntExtra(MICROPHONE_PIP_INTENT_EXTRA_ACTION, 0)) { + MICROPHONE_PIP_REQUEST_MUTE, MICROPHONE_PIP_REQUEST_UNMUTE -> onMicrophoneClick() + } } } - } registerReceiver( mReceiver, IntentFilter(MICROPHONE_PIP_INTENT_NAME), @@ -3035,25 +3117,23 @@ class CallActivity : CallBaseActivity() { } } - private fun updatePictureInPictureActions( - @DrawableRes iconId: Int, - title: String?, - requestCode: Int - ) { + private fun updatePictureInPictureActions(@DrawableRes iconId: Int, title: String?, requestCode: Int) { if (isGreaterEqualOreo && isPipModePossible) { val actions = ArrayList() val icon = Icon.createWithResource(this, iconId) - val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_IMMUTABLE - } else { - 0 - } - val intent = PendingIntent.getBroadcast( - this, - requestCode, - Intent(MICROPHONE_PIP_INTENT_NAME).putExtra(MICROPHONE_PIP_INTENT_EXTRA_ACTION, requestCode), - intentFlag - ) + val intentFlag: Int = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_IMMUTABLE + } else { + 0 + } + val intent = + PendingIntent.getBroadcast( + this, + requestCode, + Intent(MICROPHONE_PIP_INTENT_NAME).putExtra(MICROPHONE_PIP_INTENT_EXTRA_ACTION, requestCode), + intentFlag + ) actions.add(RemoteAction(icon, title!!, title, intent)) mPictureInPictureParamsBuilder.setActions(actions) setPictureInPictureParams(mPictureInPictureParamsBuilder.build()) @@ -3062,10 +3142,11 @@ class CallActivity : CallBaseActivity() { override fun updateUiForPipMode() { Log.d(TAG, "updateUiForPipMode") - val params = RelativeLayout.LayoutParams( - ViewGroup.LayoutParams.MATCH_PARENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) + val params = + RelativeLayout.LayoutParams( + ViewGroup.LayoutParams.MATCH_PARENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) params.setMargins(0, 0, 0, 0) binding!!.gridview.layoutParams = params binding!!.callControls.visibility = View.GONE @@ -3105,13 +3186,10 @@ class CallActivity : CallBaseActivity() { } val isAllowedToStartOrStopRecording: Boolean - get() = ( - isCallRecordingAvailable(conversationUser!!) && - isModerator - ) + get() = isCallRecordingAvailable(conversationUser!!) && isModerator val isAllowedToRaiseHand: Boolean - get() = hasSpreedFeatureCapability(conversationUser, "raise-hand") || - isBreakoutRoom + get() = + hasSpreedFeatureCapability(conversationUser, "raise-hand") || isBreakoutRoom private inner class SelfVideoTouchListener : OnTouchListener { @SuppressLint("ClickableViewAccessibility") @@ -3135,12 +3213,8 @@ class CallActivity : CallBaseActivity() { // const val VIDEO_STREAM_TYPE_SCREEN = "screen" const val VIDEO_STREAM_TYPE_VIDEO = "video" const val TAG = "CallActivity" - private val PERMISSIONS_CAMERA = arrayOf( - Manifest.permission.CAMERA - ) - private val PERMISSIONS_MICROPHONE = arrayOf( - Manifest.permission.RECORD_AUDIO - ) + private val PERMISSIONS_CAMERA = arrayOf(Manifest.permission.CAMERA) + private val PERMISSIONS_MICROPHONE = arrayOf(Manifest.permission.RECORD_AUDIO) private const val MICROPHONE_PIP_INTENT_NAME = "microphone_pip_intent" private const val MICROPHONE_PIP_INTENT_EXTRA_ACTION = "microphone_pip_action" private const val MICROPHONE_PIP_REQUEST_MUTE = 1 diff --git a/app/src/main/java/com/nextcloud/talk/activities/CallStatus.kt b/app/src/main/java/com/nextcloud/talk/activities/CallStatus.kt index d717ea02de9..268e2ac6802 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/CallStatus.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/CallStatus.kt @@ -24,5 +24,12 @@ import kotlinx.parcelize.Parcelize @Parcelize enum class CallStatus : Parcelable { - CONNECTING, CALLING_TIMEOUT, JOINED, IN_CONVERSATION, RECONNECTING, OFFLINE, LEAVING, PUBLISHER_FAILED + CONNECTING, + CALLING_TIMEOUT, + JOINED, + IN_CONVERSATION, + RECONNECTING, + OFFLINE, + LEAVING, + PUBLISHER_FAILED } diff --git a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt index ce631df93f7..33828f8944a 100644 --- a/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/activities/MainActivity.kt @@ -74,22 +74,28 @@ class MainActivity : BaseActivity(), ActionBarProvider { @Inject lateinit var userManager: UserManager - private val onBackPressedCallback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - finish() + private val onBackPressedCallback = + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + finish() + } } - } override fun onCreate(savedInstanceState: Bundle?) { Log.d(TAG, "onCreate: Activity: " + System.identityHashCode(this).toString()) super.onCreate(savedInstanceState) - ProcessLifecycleOwner.get().lifecycle.addObserver(object : DefaultLifecycleObserver { - override fun onStart(owner: LifecycleOwner) { - lockScreenIfConditionsApply() - } - }) + ProcessLifecycleOwner + .get() + .lifecycle + .addObserver( + object : DefaultLifecycleObserver { + override fun onStart(owner: LifecycleOwner) { + lockScreenIfConditionsApply() + } + } + ) // Set the default theme to replace the launch screen theme. setTheme(R.style.AppTheme) @@ -201,14 +207,15 @@ class MainActivity : BaseActivity(), ActionBarProvider { val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1)) val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) - val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( - apiVersion, - currentUser?.baseUrl, - roomType, - null, - userId, - null - ) + val retrofitBucket = + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + currentUser?.baseUrl, + roomType, + null, + userId, + null + ) ncApi.createRoom( credentials, @@ -217,28 +224,30 @@ class MainActivity : BaseActivity(), ActionBarProvider { ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(roomOverall: RoomOverall) { - val bundle = Bundle() - bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) + override fun onNext(roomOverall: RoomOverall) { + val bundle = Bundle() + bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) - val chatIntent = Intent(context, ChatActivity::class.java) - chatIntent.putExtras(bundle) - startActivity(chatIntent) - } + val chatIntent = Intent(context, ChatActivity::class.java) + chatIntent.putExtras(bundle) + startActivity(chatIntent) + } - override fun onError(e: Throwable) { - // unused atm - } + override fun onError(e: Throwable) { + // unused atm + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } override fun onNewIntent(intent: Intent) { @@ -274,33 +283,35 @@ class MainActivity : BaseActivity(), ActionBarProvider { appPreferences.isDbRoomMigrated = true } - userManager.users.subscribe(object : SingleObserver> { - override fun onSubscribe(d: Disposable) { - // unused atm - } + userManager.users.subscribe( + object : SingleObserver> { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onSuccess(users: List) { - if (users.isNotEmpty()) { - ClosedInterfaceImpl().setUpPushTokenRegistration() - runOnUiThread { - openConversationList() - } - } else { - runOnUiThread { - launchServerSelection() + override fun onSuccess(users: List) { + if (users.isNotEmpty()) { + ClosedInterfaceImpl().setUpPushTokenRegistration() + runOnUiThread { + openConversationList() + } + } else { + runOnUiThread { + launchServerSelection() + } } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "Error loading existing users", e) - Toast.makeText( - context, - context.resources.getString(R.string.nc_common_error_sorry), - Toast.LENGTH_SHORT - ).show() + override fun onError(e: Throwable) { + Log.e(TAG, "Error loading existing users", e) + Toast.makeText( + context, + context.resources.getString(R.string.nc_common_error_sorry), + Toast.LENGTH_SHORT + ).show() + } } - }) + ) } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/GeocodingAdapter.kt b/app/src/main/java/com/nextcloud/talk/adapters/GeocodingAdapter.kt index 603c73537e0..bbba7872ca3 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/GeocodingAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/GeocodingAdapter.kt @@ -32,7 +32,6 @@ import fr.dudie.nominatim.model.Address class GeocodingAdapter(private val context: Context, private var dataSource: List
) : RecyclerView.Adapter() { - interface OnItemClickListener { fun onItemClick(position: Int) } @@ -44,6 +43,7 @@ class GeocodingAdapter(private val context: Context, private var dataSource: Lis } private var listener: OnItemClickListener? = null + fun setOnItemClickListener(listener: OnItemClickListener) { this.listener = listener } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt index 7ddd83be4ff..b71ba3f2e18 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/PredefinedStatusViewHolder.kt @@ -41,11 +41,13 @@ class PredefinedStatusViewHolder(private val binding: PredefinedStatusBinding) : } else { val clearAt = status.clearAt!! if (clearAt.type.equals("period")) { - binding.clearAt.text = DisplayUtils.getRelativeTimestamp( - context, - System.currentTimeMillis() + clearAt.time.toInt() * ONE_SECOND_IN_MILLIS, - true - ) + binding.clearAt.text = + DisplayUtils + .getRelativeTimestamp( + context, + System.currentTimeMillis() + clearAt.time.toInt() * ONE_SECOND_IN_MILLIS, + true + ) } else { // end-of if (clearAt.time.equals("day")) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt index 4906bb7bcfa..e4aad005b1f 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/ConversationItem.kt @@ -203,9 +203,7 @@ class ConversationItem( } } - private fun shouldLoadAvatar( - holder: ConversationItemViewHolder - ): Boolean { + private fun shouldLoadAvatar(holder: ConversationItemViewHolder): Boolean { return when (model.objectType) { Conversation.ObjectType.SHARE_PASSWORD -> { holder.binding.dialogAvatar.setImageDrawable( @@ -237,18 +235,16 @@ class ConversationItem( } } - private fun setLastMessage( - holder: ConversationItemViewHolder, - appContext: Context - ) { + private fun setLastMessage(holder: ConversationItemViewHolder, appContext: Context) { if (model.lastMessage != null) { holder.binding.dialogDate.visibility = View.VISIBLE - holder.binding.dialogDate.text = DateUtils.getRelativeTimeSpanString( - model.lastActivity * MILLIES, - System.currentTimeMillis(), - 0, - DateUtils.FORMAT_ABBREV_RELATIVE - ) + holder.binding.dialogDate.text = + DateUtils.getRelativeTimeSpanString( + model.lastActivity * MILLIES, + System.currentTimeMillis(), + 0, + DateUtils.FORMAT_ABBREV_RELATIVE + ) if (!TextUtils.isEmpty(model.lastMessage!!.systemMessage) || ConversationType.ROOM_SYSTEM === model.type ) { @@ -304,16 +300,18 @@ class ConversationItem( } else { holder.binding.dialogUnreadBubble.setText(R.string.tooManyUnreadMessages) } - val lightBubbleFillColor = ColorStateList.valueOf( + val lightBubbleFillColor = + ColorStateList.valueOf( + ContextCompat.getColor( + context, + R.color.conversation_unread_bubble + ) + ) + val lightBubbleTextColor = ContextCompat.getColor( context, - R.color.conversation_unread_bubble + R.color.conversation_unread_bubble_text ) - ) - val lightBubbleTextColor = ContextCompat.getColor( - context, - R.color.conversation_unread_bubble_text - ) if (model.type === ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL) { viewThemeUtils.material.colorChipBackground(holder.binding.dialogUnreadBubble) } else if (model.unreadMention) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt b/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt index c9384ce2aae..38d1cf21d06 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/items/MessageResultItem.kt @@ -39,17 +39,15 @@ import eu.davidea.flexibleadapter.items.IFlexible import eu.davidea.flexibleadapter.items.ISectionable import eu.davidea.viewholders.FlexibleViewHolder -data class MessageResultItem constructor( +data class MessageResultItem( private val context: Context, private val currentUser: User, val messageEntry: SearchMessageEntry, private val showHeader: Boolean = false, private val viewThemeUtils: ViewThemeUtils -) : - AbstractFlexibleItem(), +) : AbstractFlexibleItem(), IFilterable, ISectionable { - class ViewHolder(view: View, adapter: FlexibleAdapter<*>) : FlexibleViewHolder(view, adapter) { var binding: RvItemSearchMessageBinding @@ -95,10 +93,11 @@ data class MessageResultItem constructor( const val VIEW_TYPE = FlexibleItemViewType.MESSAGE_RESULT_ITEM } - override fun getHeader(): GenericTextHeaderItem = MessagesTextHeaderItem(context, viewThemeUtils) - .apply { - isHidden = showHeader // FlexibleAdapter needs this hack for some reason - } + override fun getHeader(): GenericTextHeaderItem = + MessagesTextHeaderItem(context, viewThemeUtils) + .apply { + isHidden = showHeader // FlexibleAdapter needs this hack for some reason + } override fun setHeader(header: GenericTextHeaderItem?) { // nothing, header is always the same diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedMessageInterface.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedMessageInterface.kt index 9d39d987c5b..a71e19943ad 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedMessageInterface.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedMessageInterface.kt @@ -21,5 +21,6 @@ package com.nextcloud.talk.adapters.messages interface CallStartedMessageInterface { fun joinAudioCall() + fun joinVideoCall() } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt index c6154ddaa14..6da15193dc3 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/CallStartedViewHolder.kt @@ -75,34 +75,39 @@ class CallStartedViewHolder(incomingView: View, payload: Any) : private fun setUpAvatarProfile(message: ChatMessage) { val user = userManager.currentUser.blockingGet() - val url: String = if (message.actorType == "guests" || message.actorType == "guest") { - ApiUtils.getUrlForGuestAvatar( - user!!.baseUrl, - message.actorDisplayName, - true - ) - } else { - ApiUtils.getUrlForAvatar(user!!.baseUrl, message.actorDisplayName, false) - } - - val imageRequest: ImageRequest = ImageRequest.Builder(context) - .data(url) - .crossfade(true) - .transformations(CircleCropTransformation()) - .target(object : Target { - override fun onStart(placeholder: Drawable?) { - // unused atm - } - - override fun onError(error: Drawable?) { - // unused atm - } - - override fun onSuccess(result: Drawable) { - binding.callAuthorChip.chipIcon = result - } - }) - .build() + val url: String = + if (message.actorType == "guests" || message.actorType == "guest") { + ApiUtils.getUrlForGuestAvatar( + user!!.baseUrl, + message.actorDisplayName, + true + ) + } else { + ApiUtils.getUrlForAvatar(user!!.baseUrl, message.actorDisplayName, false) + } + + val imageRequest: ImageRequest = + ImageRequest + .Builder(context) + .data(url) + .crossfade(true) + .transformations(CircleCropTransformation()) + .target( + object : Target { + override fun onStart(placeholder: Drawable?) { + // unused atm + } + + override fun onError(error: Drawable?) { + // unused atm + } + + override fun onSuccess(result: Drawable) { + binding.callAuthorChip.chipIcon = result + } + } + ) + .build() imageLoader(context).enqueue(imageRequest) } @@ -124,6 +129,6 @@ class CallStartedViewHolder(incomingView: View, payload: Any) : } companion object { - var TAG: String? = CallStartedViewHolder::class.simpleName + val TAG: String? = CallStartedViewHolder::class.simpleName } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt index 34116479bfa..d334cc25612 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/CommonMessageInterface.kt @@ -4,6 +4,8 @@ import com.nextcloud.talk.models.json.chat.ChatMessage interface CommonMessageInterface { fun onLongClickReactions(chatMessage: ChatMessage) + fun onClickReaction(chatMessage: ChatMessage, emoji: String) + fun onOpenMessageActionsDialog(chatMessage: ChatMessage) } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt index 60ea4bb88cb..62f37ec69a4 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLinkPreviewMessageViewHolder.kt @@ -81,20 +81,24 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) : sharedApplication!!.componentApplication.inject(this) binding.messageTime.text = dateUtils.getLocalTimeStringFromTimestamp(message.timestamp) - var processedMessageText = messageUtils.enrichChatMessageText( - binding.messageText.context, - message, - true, - viewThemeUtils - ) + var processedMessageText = + messageUtils + .enrichChatMessageText( + binding.messageText.context, + message, + true, + viewThemeUtils + ) - processedMessageText = messageUtils.processMessageParameters( - binding.messageText.context, - viewThemeUtils, - processedMessageText!!, - message, - itemView - ) + processedMessageText = + messageUtils + .processMessageParameters( + binding.messageText.context, + viewThemeUtils, + processedMessageText!!, + message, + itemView + ) binding.messageText.text = processedMessageText @@ -195,13 +199,14 @@ class IncomingLinkPreviewMessageViewHolder(incomingView: View, payload: Any) : } binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName ?: context.getText(R.string.nc_nick_guest) - binding.messageQuote.quotedMessage.text = messageUtils - .enrichChatReplyMessageText( - binding.messageQuote.quotedMessage.context, - parentChatMessage, - true, - viewThemeUtils - ) + binding.messageQuote.quotedMessage.text = + messageUtils + .enrichChatReplyMessageText( + binding.messageQuote.quotedMessage.context, + parentChatMessage, + true, + viewThemeUtils + ) binding.messageQuote.quotedMessageAuthor .setTextColor(ContextCompat.getColor(context, R.color.textColorMaxContrast)) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt index a88a1de93a6..2d8a4a4c653 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingLocationMessageViewHolder.kt @@ -180,13 +180,14 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : } binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName ?: context.getText(R.string.nc_nick_guest) - binding.messageQuote.quotedMessage.text = messageUtils - .enrichChatReplyMessageText( - binding.messageQuote.quotedMessage.context, - parentChatMessage, - true, - viewThemeUtils - ) + binding.messageQuote.quotedMessage.text = + messageUtils + .enrichChatReplyMessageText( + binding.messageQuote.quotedMessage.context, + parentChatMessage, + true, + viewThemeUtils + ) binding.messageQuote.quotedMessageAuthor .setTextColor(context.resources.getColor(R.color.textColorMaxContrast, null)) @@ -219,18 +220,19 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : binding.webview.settings.javaScriptEnabled = true - binding.webview.webViewClient = object : WebViewClient() { - @Deprecated("Use shouldOverrideUrlLoading(WebView view, WebResourceRequest request)") - override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { - return if (url != null && UriUtils.hasHttpProtocolPrefixed(url) - ) { - view?.context?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) - true - } else { - false + binding.webview.webViewClient = + object : WebViewClient() { + @Deprecated("Use shouldOverrideUrlLoading(WebView view, WebResourceRequest request)") + override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { + return if (url != null && UriUtils.hasHttpProtocolPrefixed(url) + ) { + view?.context?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) + true + } else { + false + } } } - } val urlStringBuffer = StringBuffer("file:///android_asset/leafletMapMessagePreview.html") urlStringBuffer.append( @@ -246,15 +248,17 @@ class IncomingLocationMessageViewHolder(incomingView: View, payload: Any) : binding.webview.loadUrl(urlStringBuffer.toString()) - binding.webview.setOnTouchListener(object : View.OnTouchListener { - override fun onTouch(v: View?, event: MotionEvent?): Boolean { - when (event?.action) { - MotionEvent.ACTION_UP -> openGeoLink() - } + binding.webview.setOnTouchListener( + object : View.OnTouchListener { + override fun onTouch(v: View?, event: MotionEvent?): Boolean { + when (event?.action) { + MotionEvent.ACTION_UP -> openGeoLink() + } - return v?.onTouchEvent(event) ?: true + return v?.onTouchEvent(event) ?: true + } } - }) + ) } private fun openGeoLink() { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt index 14867564693..bd600a6b80c 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingPollMessageViewHolder.kt @@ -49,7 +49,6 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : MessageHolders.IncomingTextMessageViewHolder(incomingView, payload) { - private val binding: ItemCustomIncomingPollMessageBinding = ItemCustomIncomingPollMessageBinding.bind(itemView) @Inject @@ -132,13 +131,15 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : val isOwnerOrModerator = (payload as? MessagePayload)!!.isOwnerOrModerator ?: false binding.bubble.setOnClickListener { - val pollVoteDialog = PollMainDialogFragment.newInstance( - message.activeUser!!, - roomToken, - isOwnerOrModerator, - pollId, - pollName - ) + val pollVoteDialog = + PollMainDialogFragment + .newInstance( + message.activeUser!!, + roomToken, + isOwnerOrModerator, + pollId, + pollName + ) pollVoteDialog.show( (binding.messagePollIcon.context as ChatActivity).supportFragmentManager, TAG @@ -203,13 +204,14 @@ class IncomingPollMessageViewHolder(incomingView: View, payload: Any) : } binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName ?: context.getText(R.string.nc_nick_guest) - binding.messageQuote.quotedMessage.text = messageUtils - .enrichChatReplyMessageText( - binding.messageQuote.quotedMessage.context, - parentChatMessage, - true, - viewThemeUtils - ) + binding.messageQuote.quotedMessage.text = + messageUtils + .enrichChatReplyMessageText( + binding.messageQuote.quotedMessage.context, + parentChatMessage, + true, + viewThemeUtils + ) binding.messageQuote.quotedMessageAuthor .setTextColor(ContextCompat.getColor(context, R.color.textColorMaxContrast)) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt index 5b07dbbd5ba..9aeef171bf9 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingTextMessageViewHolder.kt @@ -85,20 +85,24 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) : var textSize = context.resources!!.getDimension(R.dimen.chat_text_size) - var processedMessageText = messageUtils.enrichChatMessageText( - binding.messageText.context, - message, - true, - viewThemeUtils - ) + var processedMessageText = + messageUtils + .enrichChatMessageText( + binding.messageText.context, + message, + true, + viewThemeUtils + ) - processedMessageText = messageUtils.processMessageParameters( - binding.messageText.context, - viewThemeUtils, - processedMessageText!!, - message, - itemView - ) + processedMessageText = + messageUtils + .processMessageParameters( + binding.messageText.context, + viewThemeUtils, + processedMessageText!!, + message, + itemView + ) val messageParameters = message.messageParameters if ( @@ -197,19 +201,21 @@ class IncomingTextMessageViewHolder(itemView: View, payload: Any) : } ?: run { binding.messageQuote.quotedMessageImage.visibility = View.GONE } - binding.messageQuote.quotedMessageAuthor.text = if (parentChatMessage.actorDisplayName.isNullOrEmpty()) { - context.getText(R.string.nc_nick_guest) - } else { - parentChatMessage.actorDisplayName - } + binding.messageQuote.quotedMessageAuthor.text = + if (parentChatMessage.actorDisplayName.isNullOrEmpty()) { + context.getText(R.string.nc_nick_guest) + } else { + parentChatMessage.actorDisplayName + } - binding.messageQuote.quotedMessage.text = messageUtils - .enrichChatReplyMessageText( - binding.messageQuote.quotedMessage.context, - parentChatMessage, - true, - viewThemeUtils - ) + binding.messageQuote.quotedMessage.text = + messageUtils + .enrichChatReplyMessageText( + binding.messageQuote.quotedMessage.context, + parentChatMessage, + true, + viewThemeUtils + ) if (parentChatMessage.actorId?.equals(message.activeUser!!.userId) == true) { viewThemeUtils.platform.colorViewBackground(binding.messageQuote.quoteColoredView, ColorRole.PRIMARY) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt index bb90eb72c4d..df70edff9b4 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/IncomingVoiceMessageViewHolder.kt @@ -115,10 +115,12 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : if (message.isPlayingVoiceMessage) { showPlayButton() - binding.playPauseBtn.icon = ContextCompat.getDrawable( - context!!, - R.drawable.ic_baseline_pause_voice_message_24 - ) + binding.playPauseBtn.icon = + ContextCompat + .getDrawable( + context!!, + R.drawable.ic_baseline_pause_voice_message_24 + ) val d = message.voiceMessageDuration.toLong() val t = message.voiceMessagePlayedSeconds.toLong() binding.voiceMessageDuration.text = android.text.format.DateUtils.formatElapsedTime(d - t) @@ -126,10 +128,12 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : binding.seekbar.progress = message.voiceMessageSeekbarProgress } else { binding.playPauseBtn.visibility = View.VISIBLE - binding.playPauseBtn.icon = ContextCompat.getDrawable( - context!!, - R.drawable.ic_baseline_play_arrow_voice_message_24 - ) + binding.playPauseBtn.icon = + ContextCompat + .getDrawable( + context!!, + R.drawable.ic_baseline_play_arrow_voice_message_24 + ) } if (message.isDownloadingVoiceMessage) { @@ -145,31 +149,35 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : if (message.resetVoiceMessage) { binding.playPauseBtn.visibility = View.VISIBLE - binding.playPauseBtn.icon = ContextCompat.getDrawable( - context!!, - R.drawable.ic_baseline_play_arrow_voice_message_24 - ) + binding.playPauseBtn.icon = + ContextCompat + .getDrawable( + context!!, + R.drawable.ic_baseline_play_arrow_voice_message_24 + ) binding.seekbar.progress = SEEKBAR_START message.resetVoiceMessage = false message.voiceMessagePlayedSeconds = 0 binding.voiceMessageDuration.visibility = View.INVISIBLE } - binding.seekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { - override fun onStopTrackingTouch(seekBar: SeekBar) { - // unused atm - } + binding.seekbar.setOnSeekBarChangeListener( + object : SeekBar.OnSeekBarChangeListener { + override fun onStopTrackingTouch(seekBar: SeekBar) { + // unused atm + } - override fun onStartTrackingTouch(seekBar: SeekBar) { - // unused atm - } + override fun onStartTrackingTouch(seekBar: SeekBar) { + // unused atm + } - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - voiceMessageInterface.updateMediaPlayerProgressBySlider(message, progress) + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + if (fromUser) { + voiceMessageInterface.updateMediaPlayerProgressBySlider(message, progress) + } } } - }) + ) Reaction().showReactions( message, @@ -219,14 +227,17 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : Log.d(TAG, "WorkInfo.State.RUNNING in ViewHolder") showVoiceMessageLoading() } + WorkInfo.State.SUCCEEDED -> { Log.d(TAG, "WorkInfo.State.SUCCEEDED in ViewHolder") showPlayButton() } + WorkInfo.State.FAILED -> { Log.d(TAG, "WorkInfo.State.FAILED in ViewHolder") showPlayButton() } + else -> { } } @@ -274,14 +285,16 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : } else if (message.actorType == "bots" && message.actorId == "changelog") { binding.messageUserAvatar.loadChangelogBotAvatar() } else if (message.actorType == "bots") { - val drawable = TextDrawable.builder() - .beginConfig() - .bold() - .endConfig() - .buildRound( - ">", - ResourcesCompat.getColor(context!!.resources, R.color.black, null) - ) + val drawable = + TextDrawable + .builder() + .beginConfig() + .bold() + .endConfig() + .buildRound( + ">", + ResourcesCompat.getColor(context!!.resources, R.color.black, null) + ) binding.messageUserAvatar.visibility = View.VISIBLE binding.messageUserAvatar.setImageDrawable(drawable) } @@ -306,15 +319,17 @@ class IncomingVoiceMessageViewHolder(incomingView: View, payload: Any) : } ?: run { binding.messageQuote.quotedMessageImage.visibility = View.GONE } - binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName - ?: context!!.getText(R.string.nc_nick_guest) - binding.messageQuote.quotedMessage.text = messageUtils - .enrichChatReplyMessageText( - binding.messageQuote.quotedMessage.context, - parentChatMessage, - true, - viewThemeUtils - ) + binding.messageQuote.quotedMessageAuthor.text = + parentChatMessage + .actorDisplayName ?: context!!.getText(R.string.nc_nick_guest) + binding.messageQuote.quotedMessage.text = + messageUtils + .enrichChatReplyMessageText( + binding.messageQuote.quotedMessage.context, + parentChatMessage, + true, + viewThemeUtils + ) binding.messageQuote.quotedMessageAuthor .setTextColor(ContextCompat.getColor(context!!, R.color.textColorMaxContrast)) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt index f73524cf3cd..9c994a6c5ea 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/LinkPreview.kt @@ -37,12 +37,7 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers class LinkPreview { - fun showLink( - message: ChatMessage, - ncApi: NcApi, - binding: ReferenceInsideMessageBinding, - context: Context - ) { + fun showLink(message: ChatMessage, ncApi: NcApi, binding: ReferenceInsideMessageBinding, context: Context) { binding.referenceName.text = "" binding.referenceDescription.text = "" binding.referenceLink.text = "" @@ -58,68 +53,70 @@ class LinkPreview { ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(openGraphOverall: OpenGraphOverall) { - val reference = openGraphOverall.ocs?.data?.references?.entries?.iterator()?.next()?.value + override fun onNext(openGraphOverall: OpenGraphOverall) { + val reference = openGraphOverall.ocs?.data?.references?.entries?.iterator()?.next()?.value - if (reference != null) { - val referenceName = reference.openGraphObject?.name - if (!referenceName.isNullOrEmpty()) { - binding.referenceName.visibility = View.VISIBLE - binding.referenceName.text = referenceName - } else { - binding.referenceName.visibility = View.GONE - } + if (reference != null) { + val referenceName = reference.openGraphObject?.name + if (!referenceName.isNullOrEmpty()) { + binding.referenceName.visibility = View.VISIBLE + binding.referenceName.text = referenceName + } else { + binding.referenceName.visibility = View.GONE + } - val referenceDescription = reference.openGraphObject?.description - if (!referenceDescription.isNullOrEmpty()) { - binding.referenceDescription.visibility = View.VISIBLE - binding.referenceDescription.text = referenceDescription - } else { - binding.referenceDescription.visibility = View.GONE - } + val referenceDescription = reference.openGraphObject?.description + if (!referenceDescription.isNullOrEmpty()) { + binding.referenceDescription.visibility = View.VISIBLE + binding.referenceDescription.text = referenceDescription + } else { + binding.referenceDescription.visibility = View.GONE + } - val referenceLink = reference.openGraphObject?.link - if (!referenceLink.isNullOrEmpty()) { - binding.referenceLink.visibility = View.VISIBLE - binding.referenceLink.text = referenceLink.replace(HTTPS_PROTOCOL, "") - } else { - binding.referenceLink.visibility = View.GONE - } + val referenceLink = reference.openGraphObject?.link + if (!referenceLink.isNullOrEmpty()) { + binding.referenceLink.visibility = View.VISIBLE + binding.referenceLink.text = referenceLink.replace(HTTPS_PROTOCOL, "") + } else { + binding.referenceLink.visibility = View.GONE + } - val referenceThumbUrl = reference.openGraphObject?.thumb - if (!referenceThumbUrl.isNullOrEmpty()) { - binding.referenceThumbImage.visibility = View.VISIBLE - binding.referenceThumbImage.load(referenceThumbUrl) - } else { - binding.referenceThumbImage.visibility = View.GONE - } + val referenceThumbUrl = reference.openGraphObject?.thumb + if (!referenceThumbUrl.isNullOrEmpty()) { + binding.referenceThumbImage.visibility = View.VISIBLE + binding.referenceThumbImage.load(referenceThumbUrl) + } else { + binding.referenceThumbImage.visibility = View.GONE + } - binding.referenceWrapper.setOnClickListener { - val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(referenceLink)) - browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) - context.startActivity(browserIntent) + binding.referenceWrapper.setOnClickListener { + val browserIntent = Intent(Intent.ACTION_VIEW, Uri.parse(referenceLink)) + browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) + context.startActivity(browserIntent) + } } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "failed to get openGraph data", e) - binding.referenceName.visibility = View.GONE - binding.referenceDescription.visibility = View.GONE - binding.referenceLink.visibility = View.GONE - binding.referenceThumbImage.visibility = View.GONE - binding.referenceIndentedSideBar.visibility = View.GONE - } + override fun onError(e: Throwable) { + Log.e(TAG, "failed to get openGraph data", e) + binding.referenceName.visibility = View.GONE + binding.referenceDescription.visibility = View.GONE + binding.referenceLink.visibility = View.GONE + binding.referenceThumbImage.visibility = View.GONE + binding.referenceIndentedSideBar.visibility = View.GONE + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt index 7a1f9aa81b9..6b9b1938f89 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLinkPreviewMessageViewHolder.kt @@ -47,7 +47,6 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) : MessageHolders.OutcomingTextMessageViewHolder(outcomingView, payload) { - private val binding: ItemCustomOutcomingLinkPreviewMessageBinding = ItemCustomOutcomingLinkPreviewMessageBinding.bind(itemView) @@ -84,13 +83,15 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) : colorizeMessageBubble(message) var processedMessageText = messageUtils.enrichChatMessageText(binding.messageText.context, message, false, viewThemeUtils) - processedMessageText = messageUtils.processMessageParameters( - binding.messageText.context, - viewThemeUtils, - processedMessageText!!, - message, - itemView - ) + processedMessageText = + messageUtils + .processMessageParameters( + binding.messageText.context, + viewThemeUtils, + processedMessageText!!, + message, + itemView + ) binding.messageText.text = processedMessageText @@ -99,17 +100,19 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) : // parent message handling setParentMessageDataOnMessageItem(message) - val readStatusDrawableInt = when (message.readStatus) { - ReadStatus.READ -> R.drawable.ic_check_all - ReadStatus.SENT -> R.drawable.ic_check - else -> null - } + val readStatusDrawableInt = + when (message.readStatus) { + ReadStatus.READ -> R.drawable.ic_check_all + ReadStatus.SENT -> R.drawable.ic_check + else -> null + } - val readStatusContentDescriptionString = when (message.readStatus) { - ReadStatus.READ -> context.resources?.getString(R.string.nc_message_read) - ReadStatus.SENT -> context.resources?.getString(R.string.nc_message_sent) - else -> null - } + val readStatusContentDescriptionString = + when (message.readStatus) { + ReadStatus.READ -> context.resources?.getString(R.string.nc_message_read) + ReadStatus.SENT -> context.resources?.getString(R.string.nc_message_sent) + else -> null + } readStatusDrawableInt?.let { drawableInt -> AppCompatResources.getDrawable(context, drawableInt)?.let { @@ -167,15 +170,16 @@ class OutcomingLinkPreviewMessageViewHolder(outcomingView: View, payload: Any) : } ?: run { binding.messageQuote.quotedMessageImage.visibility = View.GONE } - binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName - ?: context.getText(R.string.nc_nick_guest) - binding.messageQuote.quotedMessage.text = messageUtils - .enrichChatReplyMessageText( - binding.messageQuote.quotedMessage.context, - parentChatMessage, - false, - viewThemeUtils - ) + binding.messageQuote.quotedMessageAuthor.text = + parentChatMessage.actorDisplayName ?: context.getText(R.string.nc_nick_guest) + binding.messageQuote.quotedMessage.text = + messageUtils + .enrichChatReplyMessageText( + binding.messageQuote.quotedMessage.context, + parentChatMessage, + false, + viewThemeUtils + ) viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage) viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor) viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt index bfea357d407..f016644a1cd 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingLocationMessageViewHolder.kt @@ -102,17 +102,19 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : // parent message handling setParentMessageDataOnMessageItem(message) - val readStatusDrawableInt = when (message.readStatus) { - ReadStatus.READ -> R.drawable.ic_check_all - ReadStatus.SENT -> R.drawable.ic_check - else -> null - } + val readStatusDrawableInt = + when (message.readStatus) { + ReadStatus.READ -> R.drawable.ic_check_all + ReadStatus.SENT -> R.drawable.ic_check + else -> null + } - val readStatusContentDescriptionString = when (message.readStatus) { - ReadStatus.READ -> context.resources?.getString(R.string.nc_message_read) - ReadStatus.SENT -> context.resources?.getString(R.string.nc_message_sent) - else -> null - } + val readStatusContentDescriptionString = + when (message.readStatus) { + ReadStatus.READ -> context.resources?.getString(R.string.nc_message_read) + ReadStatus.SENT -> context.resources?.getString(R.string.nc_message_sent) + else -> null + } readStatusDrawableInt?.let { drawableInt -> AppCompatResources.getDrawable(context, drawableInt)?.let { @@ -161,30 +163,32 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : binding.webview.settings.javaScriptEnabled = true - binding.webview.webViewClient = object : WebViewClient() { - @Deprecated("Use shouldOverrideUrlLoading(WebView view, WebResourceRequest request)") - override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { - return if (url != null && UriUtils.hasHttpProtocolPrefixed(url) - ) { - view?.context?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) - true - } else { - false + binding.webview.webViewClient = + object : WebViewClient() { + @Deprecated("Use shouldOverrideUrlLoading(WebView view, WebResourceRequest request)") + override fun shouldOverrideUrlLoading(view: WebView?, url: String?): Boolean { + return if (url != null && UriUtils.hasHttpProtocolPrefixed(url) + ) { + view?.context?.startActivity(Intent(Intent.ACTION_VIEW, Uri.parse(url))) + true + } else { + false + } } } - } val urlStringBuffer = StringBuffer("file:///android_asset/leafletMapMessagePreview.html") urlStringBuffer.append( "?mapProviderUrl=" + URLEncoder.encode(context.getString(R.string.osm_tile_server_url)) ) urlStringBuffer.append( - "&mapProviderAttribution=" + URLEncoder.encode( - context.getString( - R.string - .osm_tile_server_attributation + "&mapProviderAttribution=" + + URLEncoder.encode( + context.getString( + R.string + .osm_tile_server_attributation + ) ) - ) ) urlStringBuffer.append("&locationLat=" + URLEncoder.encode(locationLat)) urlStringBuffer.append("&locationLon=" + URLEncoder.encode(locationLon)) @@ -193,15 +197,17 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : binding.webview.loadUrl(urlStringBuffer.toString()) - binding.webview.setOnTouchListener(object : View.OnTouchListener { - override fun onTouch(v: View?, event: MotionEvent?): Boolean { - when (event?.action) { - MotionEvent.ACTION_UP -> openGeoLink() - } + binding.webview.setOnTouchListener( + object : View.OnTouchListener { + override fun onTouch(v: View?, event: MotionEvent?): Boolean { + when (event?.action) { + MotionEvent.ACTION_UP -> openGeoLink() + } - return v?.onTouchEvent(event) ?: true + return v?.onTouchEvent(event) ?: true + } } - }) + ) } private fun setParentMessageDataOnMessageItem(message: ChatMessage) { @@ -219,15 +225,16 @@ class OutcomingLocationMessageViewHolder(incomingView: View) : } ?: run { binding.messageQuote.quotedMessageImage.visibility = View.GONE } - binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName - ?: context.getText(R.string.nc_nick_guest) - binding.messageQuote.quotedMessage.text = messageUtils - .enrichChatReplyMessageText( - binding.messageQuote.quotedMessage.context, - parentChatMessage, - false, - viewThemeUtils - ) + binding.messageQuote.quotedMessageAuthor.text = + parentChatMessage.actorDisplayName ?: context.getText(R.string.nc_nick_guest) + binding.messageQuote.quotedMessage.text = + messageUtils + .enrichChatReplyMessageText( + binding.messageQuote.quotedMessage.context, + parentChatMessage, + false, + viewThemeUtils + ) viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage) viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor) viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt index 8b93d61268f..21efb95c485 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingPollMessageViewHolder.kt @@ -87,17 +87,19 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : // parent message handling setParentMessageDataOnMessageItem(message) - val readStatusDrawableInt = when (message.readStatus) { - ReadStatus.READ -> R.drawable.ic_check_all - ReadStatus.SENT -> R.drawable.ic_check - else -> null - } + val readStatusDrawableInt = + when (message.readStatus) { + ReadStatus.READ -> R.drawable.ic_check_all + ReadStatus.SENT -> R.drawable.ic_check + else -> null + } - val readStatusContentDescriptionString = when (message.readStatus) { - ReadStatus.READ -> context.resources?.getString(R.string.nc_message_read) - ReadStatus.SENT -> context.resources?.getString(R.string.nc_message_sent) - else -> null - } + val readStatusContentDescriptionString = + when (message.readStatus) { + ReadStatus.READ -> context.resources?.getString(R.string.nc_message_read) + ReadStatus.SENT -> context.resources?.getString(R.string.nc_message_sent) + else -> null + } readStatusDrawableInt?.let { drawableInt -> AppCompatResources.getDrawable(context, drawableInt)?.let { @@ -150,13 +152,14 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : val isOwnerOrModerator = (payload as? MessagePayload)!!.isOwnerOrModerator ?: false binding.bubble.setOnClickListener { - val pollVoteDialog = PollMainDialogFragment.newInstance( - message.activeUser!!, - roomToken, - isOwnerOrModerator, - pollId, - pollName - ) + val pollVoteDialog = + PollMainDialogFragment.newInstance( + message.activeUser!!, + roomToken, + isOwnerOrModerator, + pollId, + pollName + ) pollVoteDialog.show( (binding.messagePollIcon.context as ChatActivity).supportFragmentManager, TAG @@ -182,13 +185,14 @@ class OutcomingPollMessageViewHolder(outcomingView: View, payload: Any) : } binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName ?: context.getText(R.string.nc_nick_guest) - binding.messageQuote.quotedMessage.text = messageUtils - .enrichChatReplyMessageText( - binding.messageQuote.quotedMessage.context, - parentChatMessage, - false, - viewThemeUtils - ) + binding.messageQuote.quotedMessage.text = + messageUtils + .enrichChatReplyMessageText( + binding.messageQuote.quotedMessage.context, + parentChatMessage, + false, + viewThemeUtils + ) viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage) viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor) viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt index 680af44a61a..a53e63abe62 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingTextMessageViewHolder.kt @@ -73,19 +73,21 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH layoutParams.isWrapBefore = false var textSize = context.resources.getDimension(R.dimen.chat_text_size) viewThemeUtils.platform.colorTextView(binding.messageTime, ColorRole.ON_SURFACE_VARIANT) - var processedMessageText = messageUtils.enrichChatMessageText( - binding.messageText.context, - message, - false, - viewThemeUtils - ) - processedMessageText = messageUtils.processMessageParameters( - binding.messageText.context, - viewThemeUtils, - processedMessageText!!, - message, - itemView - ) + var processedMessageText = + messageUtils.enrichChatMessageText( + binding.messageText.context, + message, + false, + viewThemeUtils + ) + processedMessageText = + messageUtils.processMessageParameters( + binding.messageText.context, + viewThemeUtils, + processedMessageText!!, + message, + itemView + ) val messageParameters = message.messageParameters if ( @@ -114,17 +116,19 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH binding.messageQuote.quotedChatMessageView.visibility = View.GONE } - val readStatusDrawableInt = when (message.readStatus) { - ReadStatus.READ -> R.drawable.ic_check_all - ReadStatus.SENT -> R.drawable.ic_check - else -> null - } + val readStatusDrawableInt = + when (message.readStatus) { + ReadStatus.READ -> R.drawable.ic_check_all + ReadStatus.SENT -> R.drawable.ic_check + else -> null + } - val readStatusContentDescriptionString = when (message.readStatus) { - ReadStatus.READ -> context.resources?.getString(R.string.nc_message_read) - ReadStatus.SENT -> context.resources?.getString(R.string.nc_message_sent) - else -> null - } + val readStatusContentDescriptionString = + when (message.readStatus) { + ReadStatus.READ -> context.resources?.getString(R.string.nc_message_read) + ReadStatus.SENT -> context.resources?.getString(R.string.nc_message_sent) + else -> null + } readStatusDrawableInt?.let { drawableInt -> ResourcesCompat.getDrawable(context.resources, drawableInt, null)?.let { @@ -172,13 +176,14 @@ class OutcomingTextMessageViewHolder(itemView: View) : OutcomingTextMessageViewH } binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName ?: context.getText(R.string.nc_nick_guest) - binding.messageQuote.quotedMessage.text = messageUtils - .enrichChatReplyMessageText( - binding.messageQuote.quotedMessage.context, - parentChatMessage, - false, - viewThemeUtils - ) + binding.messageQuote.quotedMessage.text = + messageUtils + .enrichChatReplyMessageText( + binding.messageQuote.quotedMessage.context, + parentChatMessage, + false, + viewThemeUtils + ) viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage) viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt index 80323d9e208..4979fbd4006 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/OutcomingVoiceMessageViewHolder.kt @@ -116,33 +116,37 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : handleResetVoiceMessageState(message) - binding.seekbar.setOnSeekBarChangeListener(object : SeekBar.OnSeekBarChangeListener { - override fun onStopTrackingTouch(seekBar: SeekBar) { - // unused atm - } + binding.seekbar.setOnSeekBarChangeListener( + object : SeekBar.OnSeekBarChangeListener { + override fun onStopTrackingTouch(seekBar: SeekBar) { + // unused atm + } - override fun onStartTrackingTouch(seekBar: SeekBar) { - // unused atm - } + override fun onStartTrackingTouch(seekBar: SeekBar) { + // unused atm + } - override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { - if (fromUser) { - voiceMessageInterface.updateMediaPlayerProgressBySlider(message, progress) + override fun onProgressChanged(seekBar: SeekBar, progress: Int, fromUser: Boolean) { + if (fromUser) { + voiceMessageInterface.updateMediaPlayerProgressBySlider(message, progress) + } } } - }) + ) - val readStatusDrawableInt = when (message.readStatus) { - ReadStatus.READ -> R.drawable.ic_check_all - ReadStatus.SENT -> R.drawable.ic_check - else -> null - } + val readStatusDrawableInt = + when (message.readStatus) { + ReadStatus.READ -> R.drawable.ic_check_all + ReadStatus.SENT -> R.drawable.ic_check + else -> null + } - val readStatusContentDescriptionString = when (message.readStatus) { - ReadStatus.READ -> context?.resources?.getString(R.string.nc_message_read) - ReadStatus.SENT -> context?.resources?.getString(R.string.nc_message_sent) - else -> null - } + val readStatusContentDescriptionString = + when (message.readStatus) { + ReadStatus.READ -> context?.resources?.getString(R.string.nc_message_read) + ReadStatus.SENT -> context?.resources?.getString(R.string.nc_message_sent) + else -> null + } readStatusDrawableInt?.let { drawableInt -> AppCompatResources.getDrawable(context!!, drawableInt)?.let { @@ -175,10 +179,11 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : private fun handleResetVoiceMessageState(message: ChatMessage) { if (message.resetVoiceMessage) { binding.playPauseBtn.visibility = View.VISIBLE - binding.playPauseBtn.icon = ContextCompat.getDrawable( - context!!, - R.drawable.ic_baseline_play_arrow_voice_message_24 - ) + binding.playPauseBtn.icon = + ContextCompat.getDrawable( + context!!, + R.drawable.ic_baseline_play_arrow_voice_message_24 + ) binding.seekbar.progress = SEEKBAR_START message.voiceMessagePlayedSeconds = 0 binding.voiceMessageDuration.visibility = View.INVISIBLE @@ -202,10 +207,11 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : private fun handleIsPlayingVoiceMessageState(message: ChatMessage) { if (message.isPlayingVoiceMessage) { showPlayButton() - binding.playPauseBtn.icon = ContextCompat.getDrawable( - context!!, - R.drawable.ic_baseline_pause_voice_message_24 - ) + binding.playPauseBtn.icon = + ContextCompat.getDrawable( + context!!, + R.drawable.ic_baseline_pause_voice_message_24 + ) val d = message.voiceMessageDuration.toLong() val t = message.voiceMessagePlayedSeconds.toLong() @@ -214,10 +220,11 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : binding.seekbar.progress = message.voiceMessageSeekbarProgress } else { binding.playPauseBtn.visibility = View.VISIBLE - binding.playPauseBtn.icon = ContextCompat.getDrawable( - context!!, - R.drawable.ic_baseline_play_arrow_voice_message_24 - ) + binding.playPauseBtn.icon = + ContextCompat.getDrawable( + context!!, + R.drawable.ic_baseline_play_arrow_voice_message_24 + ) } } @@ -250,14 +257,17 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : Log.d(TAG, "WorkInfo.State.RUNNING in ViewHolder") showVoiceMessageLoading() } + WorkInfo.State.SUCCEEDED -> { Log.d(TAG, "WorkInfo.State.SUCCEEDED in ViewHolder") showPlayButton() } + WorkInfo.State.FAILED -> { Log.d(TAG, "WorkInfo.State.FAILED in ViewHolder") showPlayButton() } + else -> { Log.d(TAG, "WorkInfo.State unused in ViewHolder") } @@ -292,13 +302,14 @@ class OutcomingVoiceMessageViewHolder(outcomingView: View) : } binding.messageQuote.quotedMessageAuthor.text = parentChatMessage.actorDisplayName ?: context!!.getText(R.string.nc_nick_guest) - binding.messageQuote.quotedMessage.text = messageUtils - .enrichChatReplyMessageText( - binding.messageQuote.quotedMessage.context, - parentChatMessage, - false, - viewThemeUtils - ) + binding.messageQuote.quotedMessage.text = + messageUtils + .enrichChatReplyMessageText( + binding.messageQuote.quotedMessage.context, + parentChatMessage, + false, + viewThemeUtils + ) viewThemeUtils.talk.colorOutgoingQuoteText(binding.messageQuote.quotedMessage) viewThemeUtils.talk.colorOutgoingQuoteAuthorText(binding.messageQuote.quotedMessageAuthor) viewThemeUtils.talk.colorOutgoingQuoteBackground(binding.messageQuote.quoteColoredView) diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt index 6674240fb84..f1c4c52d103 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/PreviewMessageViewHolder.kt @@ -230,10 +230,11 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) : if (message.selectedIndividualHashMap!!.containsKey(KEY_CONTACT_PHOTO)) { image = previewContactPhoto - placeholder = getDrawableFromContactDetails( - context, - message.selectedIndividualHashMap!![KEY_CONTACT_PHOTO] - ) + placeholder = + getDrawableFromContactDetails( + context, + message.selectedIndividualHashMap!![KEY_CONTACT_PHOTO] + ) } else { image = previewContactPhoto image.setImageDrawable(ContextCompat.getDrawable(context!!, R.drawable.ic_mimetype_text_vcard)) @@ -247,8 +248,7 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) : val mimetype = message.selectedIndividualHashMap!![KEY_MIMETYPE] val drawableResourceId = getDrawableResourceIdForMimeType(mimetype) var drawable = ContextCompat.getDrawable(context!!, drawableResourceId) - if (drawable != null && - ( + if (drawable != null && ( drawableResourceId == R.drawable.ic_mimetype_folder || drawableResourceId == R.drawable.ic_mimetype_package_x_generic ) @@ -269,16 +269,18 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) : private fun getDrawableFromContactDetails(context: Context?, base64: String?): Drawable? { var drawable: Drawable? = null if (base64 != "") { - val inputStream = ByteArrayInputStream( - Base64.decode(base64!!.toByteArray(), Base64.DEFAULT) - ) - drawable = Drawable.createFromResourceStream( - context!!.resources, - null, - inputStream, - null, - null - ) + val inputStream = + ByteArrayInputStream( + Base64.decode(base64!!.toByteArray(), Base64.DEFAULT) + ) + drawable = + Drawable.createFromResourceStream( + context!!.resources, + null, + inputStream, + null, + null + ) try { inputStream.close() } catch (e: IOException) { @@ -294,28 +296,30 @@ abstract class PreviewMessageViewHolder(itemView: View?, payload: Any?) : private fun fetchFileInformation(url: String, activeUser: User?) { Single.fromCallable { ReadFilesystemOperation(okHttpClient, activeUser, url, 0) } .observeOn(Schedulers.io()) - .subscribe(object : SingleObserver { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : SingleObserver { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onSuccess(readFilesystemOperation: ReadFilesystemOperation) { - val davResponse = readFilesystemOperation.readRemotePath() - if (davResponse.data != null) { - val browserFileList = davResponse.data as List - if (browserFileList.isNotEmpty()) { - Handler(context!!.mainLooper).post { - val resourceId = getDrawableResourceIdForMimeType(browserFileList[0].mimeType) - placeholder = ContextCompat.getDrawable(context!!, resourceId) + override fun onSuccess(readFilesystemOperation: ReadFilesystemOperation) { + val davResponse = readFilesystemOperation.readRemotePath() + if (davResponse.data != null) { + val browserFileList = davResponse.data as List + if (browserFileList.isNotEmpty()) { + Handler(context!!.mainLooper).post { + val resourceId = getDrawableResourceIdForMimeType(browserFileList[0].mimeType) + placeholder = ContextCompat.getDrawable(context!!, resourceId) + } } } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "Error reading file information", e) + override fun onError(e: Throwable) { + Log.e(TAG, "Error reading file information", e) + } } - }) + ) } fun assignCommonMessageInterface(commonMessageInterface: CommonMessageInterface) { diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/Reaction.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/Reaction.kt index 2500863ea4c..d4011f56b68 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/Reaction.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/Reaction.kt @@ -59,26 +59,28 @@ class Reaction { val paddingBottom = DisplayUtils.convertDpToPixel(WRAPPER_PADDING_BOTTOM, context).toInt() for ((emoji, amount) in message.reactions!!) { - val isSelfReaction = message.reactionsSelf != null && - message.reactionsSelf!!.isNotEmpty() && - message.reactionsSelf!!.contains(emoji) + val isSelfReaction = + message.reactionsSelf != null && + message.reactionsSelf!!.isNotEmpty() && + message.reactionsSelf!!.contains(emoji) val textColor = viewThemeUtils.talk.getTextColor(isOutgoingMessage, isSelfReaction, binding) - val emojiWithAmountWrapper = getEmojiWithAmountWrapperLayout( - binding.reactionsEmojiWrapper.context, - emoji, - amount, - EmojiWithAmountWrapperLayoutInfo( - textColor, - amountParams, - wrapperParams, - paddingSide, - paddingTop, - paddingBottom, - viewThemeUtils, - isOutgoingMessage, - isSelfReaction + val emojiWithAmountWrapper = + getEmojiWithAmountWrapperLayout( + binding.reactionsEmojiWrapper.context, + emoji, + amount, + EmojiWithAmountWrapperLayoutInfo( + textColor, + amountParams, + wrapperParams, + paddingSide, + paddingTop, + paddingBottom, + viewThemeUtils, + isOutgoingMessage, + isSelfReaction + ) ) - ) emojiWithAmountWrapper.setOnClickListener { clickOnReaction(message, emoji) @@ -148,19 +150,21 @@ class Reaction { } private fun getWrapperLayoutParams(context: Context): LinearLayout.LayoutParams { - val wrapperParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) + val wrapperParams = + LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) wrapperParams.marginEnd = DisplayUtils.convertDpToPixel(EMOJI_END_MARGIN, context).toInt() return wrapperParams } private fun getAmountLayoutParams(context: Context): LinearLayout.LayoutParams { - val amountParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) + val amountParams = + LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) amountParams.marginStart = DisplayUtils.convertDpToPixel(AMOUNT_START_MARGIN, context).toInt() return amountParams } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/SystemMessageInterface.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/SystemMessageInterface.kt index 8b4ce5bc5df..1bdb0fd1e28 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/SystemMessageInterface.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/SystemMessageInterface.kt @@ -4,5 +4,6 @@ import com.nextcloud.talk.models.json.chat.ChatMessage interface SystemMessageInterface { fun expandSystemMessage(chatMessage: ChatMessage) + fun collapseSystemMessages() } diff --git a/app/src/main/java/com/nextcloud/talk/adapters/messages/SystemMessageViewHolder.kt b/app/src/main/java/com/nextcloud/talk/adapters/messages/SystemMessageViewHolder.kt index 14f363b968d..09e966ca112 100644 --- a/app/src/main/java/com/nextcloud/talk/adapters/messages/SystemMessageViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/adapters/messages/SystemMessageViewHolder.kt @@ -69,26 +69,29 @@ class SystemMessageViewHolder(itemView: View) : MessageHolders.IncomingTextMessa val resources = itemView.resources val pressedColor: Int = resources.getColor(R.color.bg_message_list_incoming_bubble) val mentionColor: Int = resources.getColor(R.color.textColorMaxContrast) - val bubbleDrawable = DisplayUtils.getMessageSelector( - resources.getColor(R.color.transparent), - resources.getColor(R.color.transparent), - pressedColor, - R.drawable.shape_grouped_incoming_message - ) + val bubbleDrawable = + DisplayUtils + .getMessageSelector( + resources.getColor(R.color.transparent), + resources.getColor(R.color.transparent), + pressedColor, + R.drawable.shape_grouped_incoming_message + ) ViewCompat.setBackground(background, bubbleDrawable) var messageString: Spannable = SpannableString(message.text) if (message.messageParameters != null && message.messageParameters!!.size > 0) { for (key in message.messageParameters!!.keys) { val individualMap: Map? = message.messageParameters!![key] if (individualMap != null && individualMap.containsKey("name")) { - var searchText: String? = if ("user" == individualMap["type"] || - "guest" == individualMap["type"] || - "call" == individualMap["type"] - ) { - "@" + individualMap["name"] - } else { - individualMap["name"] - } + var searchText: String? = + if ("user" == individualMap["type"] || + "guest" == individualMap["type"] || + "call" == individualMap["type"] + ) { + "@" + individualMap["name"] + } else { + individualMap["name"] + } messageString = DisplayUtils.searchAndColor(messageString, searchText, mentionColor) } } @@ -100,10 +103,12 @@ class SystemMessageViewHolder(itemView: View) : MessageHolders.IncomingTextMessa binding.expandCollapseIcon.visibility = View.VISIBLE if (!message.isExpanded) { - val similarMessages = String.format( - sharedApplication!!.resources.getString(R.string.see_similar_system_messages), - message.expandableChildrenAmount - ) + val similarMessages = + String + .format( + sharedApplication!!.resources.getString(R.string.see_similar_system_messages), + message.expandableChildrenAmount + ) binding.messageText.text = messageString binding.similarMessagesHint.visibility = View.VISIBLE diff --git a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt index 56974740942..f0d43febd94 100644 --- a/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt +++ b/app/src/main/java/com/nextcloud/talk/application/NextcloudTalkApplication.kt @@ -117,17 +117,18 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver { lateinit var okHttpClient: OkHttpClient //endregion - val hook: SQLiteDatabaseHook = object : SQLiteDatabaseHook { - override fun preKey(database: SQLiteDatabase) { - // unused atm - } + val hook: SQLiteDatabaseHook = + object : SQLiteDatabaseHook { + override fun preKey(database: SQLiteDatabase) { + // unused atm + } - override fun postKey(database: SQLiteDatabase) { - Log.i("TalkApplication", "DB cipher_migrate START") - database.rawExecSQL("PRAGMA cipher_migrate;") - Log.i("TalkApplication", "DB cipher_migrate END") + override fun postKey(database: SQLiteDatabase) { + Log.i("TalkApplication", "DB cipher_migrate START") + database.rawExecSQL("PRAGMA cipher_migrate;") + Log.i("TalkApplication", "DB cipher_migrate END") + } } - } //region private methods private fun initializeWebRtc() { @@ -157,9 +158,11 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver { sharedApplication = this val securityKeyManager = SecurityKeyManager.getInstance() - val securityKeyConfig = SecurityKeyManagerConfig.Builder() - .setEnableDebugLogging(BuildConfig.DEBUG) - .build() + val securityKeyConfig = + SecurityKeyManagerConfig + .Builder() + .setEnableDebugLogging(BuildConfig.DEBUG) + .build() securityKeyManager.init(this, securityKeyConfig) initializeWebRtc() @@ -201,11 +204,13 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver { .then(websocketConnectionsWorker) .enqueue() - val periodicCapabilitiesUpdateWork = PeriodicWorkRequest.Builder( - CapabilitiesWorker::class.java, - HALF_DAY, - TimeUnit.HOURS - ).build() + val periodicCapabilitiesUpdateWork = + PeriodicWorkRequest + .Builder( + CapabilitiesWorker::class.java, + HALF_DAY, + TimeUnit.HOURS + ).build() WorkManager.getInstance(applicationContext).enqueueUniquePeriodicWork( "DailyCapabilitiesUpdateWork", ExistingPeriodicWorkPolicy.REPLACE, @@ -221,13 +226,15 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver { //region Protected methods protected fun buildComponent() { - componentApplication = DaggerNextcloudTalkApplicationComponent.builder() - .busModule(BusModule()) - .contextModule(ContextModule(applicationContext)) - .databaseModule(DatabaseModule()) - .restModule(RestModule(applicationContext)) - .arbitraryStorageModule(ArbitraryStorageModule()) - .build() + componentApplication = + DaggerNextcloudTalkApplicationComponent + .builder() + .busModule(BusModule()) + .contextModule(ContextModule(applicationContext)) + .databaseModule(DatabaseModule()) + .restModule(RestModule(applicationContext)) + .arbitraryStorageModule(ArbitraryStorageModule()) + .build() } override fun attachBaseContext(base: Context) { @@ -236,20 +243,22 @@ class NextcloudTalkApplication : MultiDexApplication(), LifecycleObserver { } private fun buildDefaultImageLoader(): ImageLoader { - val imageLoaderBuilder = ImageLoader.Builder(applicationContext) - .memoryCache { - // Use 50% of the application's available memory. - MemoryCache.Builder(applicationContext).maxSizePercent(FIFTY_PERCENT).build() - } - .crossfade(true) // Show a short crossfade when loading images from network or disk into an ImageView. - .components { - if (SDK_INT >= P) { - add(ImageDecoderDecoder.Factory()) - } else { - add(GifDecoder.Factory()) + val imageLoaderBuilder = + ImageLoader + .Builder(applicationContext) + .memoryCache { + // Use 50% of the application's available memory. + MemoryCache.Builder(applicationContext).maxSizePercent(FIFTY_PERCENT).build() + } + .crossfade(true) // Show a short crossfade when loading images from network or disk into an ImageView. + .components { + if (SDK_INT >= P) { + add(ImageDecoderDecoder.Factory()) + } else { + add(GifDecoder.Factory()) + } + add(SvgDecoder.Factory()) } - add(SvgDecoder.Factory()) - } if (BuildConfig.DEBUG) { imageLoaderBuilder.logger(DebugLogger()) diff --git a/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BasicListItemWithImage.kt b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BasicListItemWithImage.kt index 247494f0ba6..9eb9d1ca67a 100644 --- a/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BasicListItemWithImage.kt +++ b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BasicListItemWithImage.kt @@ -25,6 +25,7 @@ import androidx.annotation.DrawableRes interface ListItemWithImage { val title: String + fun populateIcon(imageView: ImageView) } @@ -32,7 +33,6 @@ data class BasicListItemWithImage( @DrawableRes val iconRes: Int, override val title: String ) : ListItemWithImage { - override fun populateIcon(imageView: ImageView) { imageView.setImageResource(iconRes) } diff --git a/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BottomSheets.kt b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BottomSheets.kt index fa11981de08..12b5cf52a63 100644 --- a/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BottomSheets.kt +++ b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/BottomSheets.kt @@ -46,14 +46,14 @@ fun MaterialDialog.listItemsWithImage( val layoutManager = LinearLayoutManager(windowContext) return customListAdapter( - adapter = ListIconDialogAdapter( + ListIconDialogAdapter( dialog = this, items = items, disabledItems = disabledIndices, waitForPositiveButton = waitForPositiveButton, selection = selection ), - layoutManager = layoutManager + layoutManager ) } diff --git a/app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt index ccae4ac4f17..f7bbe3bba09 100644 --- a/app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/bottomsheet/items/ListIconDialogAdapter.kt @@ -57,7 +57,6 @@ internal class ListIconDialogAdapter( private var waitForPositiveButton: Boolean, private var selection: ListItemListener ) : RecyclerView.Adapter(), DialogAdapter> { - private var disabledIndices: IntArray = disabledItems ?: IntArray(0) fun itemClicked(index: Int) { @@ -79,25 +78,20 @@ internal class ListIconDialogAdapter( } } - override fun onCreateViewHolder( - parent: ViewGroup, - viewType: Int - ): ListItemViewHolder { + override fun onCreateViewHolder(parent: ViewGroup, viewType: Int): ListItemViewHolder { val listItemView: View = parent.inflate(dialog.windowContext, R.layout.menu_item_sheet) - val viewHolder = ListItemViewHolder( - itemView = listItemView, - adapter = this - ) + val viewHolder = + ListItemViewHolder( + itemView = listItemView, + adapter = this + ) viewHolder.titleView.maybeSetTextColor(dialog.windowContext, R.attr.md_color_content) return viewHolder } override fun getItemCount() = items.size - override fun onBindViewHolder( - holder: ListItemViewHolder, - position: Int - ) { + override fun onBindViewHolder(holder: ListItemViewHolder, position: Int) { holder.itemView.isEnabled = !disabledIndices.contains(position) val currentItem = items[position] @@ -120,10 +114,7 @@ internal class ListIconDialogAdapter( } } - override fun replaceItems( - items: List, - listener: ListItemListener - ) { + override fun replaceItems(items: List, listener: ListItemListener) { this.items = items if (listener != null) { this.selection = listener diff --git a/app/src/main/java/com/nextcloud/talk/call/ReactionAnimator.kt b/app/src/main/java/com/nextcloud/talk/call/ReactionAnimator.kt index 746644b8c64..fbcd024febb 100644 --- a/app/src/main/java/com/nextcloud/talk/call/ReactionAnimator.kt +++ b/app/src/main/java/com/nextcloud/talk/call/ReactionAnimator.kt @@ -46,10 +46,7 @@ class ReactionAnimator( ) { private val reactionsList: MutableList = ArrayList() - fun addReaction( - emoji: String, - displayName: String - ) { + fun addReaction(emoji: String, displayName: String) { val callReaction = CallReaction(emoji, displayName) reactionsList.add(callReaction) @@ -58,56 +55,61 @@ class ReactionAnimator( } } - private fun animateReaction( - callReaction: CallReaction - ) { + private fun animateReaction(callReaction: CallReaction) { val reactionWrapper = getReactionWrapperView(callReaction) - val params = RelativeLayout.LayoutParams( - LinearLayout.LayoutParams.WRAP_CONTENT, - LinearLayout.LayoutParams.WRAP_CONTENT - ).apply { - leftMargin = 0 - bottomMargin = 0 - } + val params = + RelativeLayout + .LayoutParams( + LinearLayout.LayoutParams.WRAP_CONTENT, + LinearLayout.LayoutParams.WRAP_CONTENT + ).apply { + leftMargin = 0 + bottomMargin = 0 + } params.addRule(RelativeLayout.ALIGN_PARENT_BOTTOM, 1) startPointView.addView(reactionWrapper, params) - val moveWithFullAlpha = ObjectAnimator.ofFloat( - reactionWrapper, - TRANSLATION_Y_PROPERTY, - POSITION_Y_WITH_FULL_ALPHA - ) + val moveWithFullAlpha = + ObjectAnimator.ofFloat( + reactionWrapper, + TRANSLATION_Y_PROPERTY, + POSITION_Y_WITH_FULL_ALPHA + ) moveWithFullAlpha.duration = DURATION_FULL_ALPHA moveWithFullAlpha.interpolator = LinearInterpolator() - val moveWithDecreasingAlpha = ObjectAnimator.ofFloat( - reactionWrapper, - TRANSLATION_Y_PROPERTY, - POSITION_Y_WITH_DECREASING_ALPHA - ) + val moveWithDecreasingAlpha = + ObjectAnimator.ofFloat( + reactionWrapper, + TRANSLATION_Y_PROPERTY, + POSITION_Y_WITH_DECREASING_ALPHA + ) moveWithDecreasingAlpha.duration = DURATION_DECREASING_ALPHA moveWithDecreasingAlpha.interpolator = LinearInterpolator() - val decreasingAlpha: ObjectAnimator = ObjectAnimator.ofFloat( - reactionWrapper, - ALPHA_PROPERTY, - ZERO_ALPHA - ) + val decreasingAlpha: ObjectAnimator = + ObjectAnimator.ofFloat( + reactionWrapper, + ALPHA_PROPERTY, + ZERO_ALPHA + ) decreasingAlpha.duration = DURATION_DECREASING_ALPHA val animatorWithFullAlpha = AnimatorSet() animatorWithFullAlpha.play(moveWithFullAlpha) - animatorWithFullAlpha.addListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - reactionsList.remove(callReaction) - if (reactionsList.isNotEmpty()) { - animateReaction(reactionsList[0]) + animatorWithFullAlpha.addListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + reactionsList.remove(callReaction) + if (reactionsList.isNotEmpty()) { + animateReaction(reactionsList[0]) + } } } - }) + ) val animatorWithDecreasingAlpha = AnimatorSet() animatorWithDecreasingAlpha.playTogether(moveWithDecreasingAlpha, decreasingAlpha) @@ -136,10 +138,11 @@ class ReactionAnimator( private fun getNameView(callReaction: CallReaction): TextView { val nameView = TextView(context) - val nameViewParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) + val nameViewParams = + LinearLayout.LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) nameViewParams.setMargins(HORIZONTAL_MARGIN, 0, HORIZONTAL_MARGIN, BOTTOM_MARGIN) nameView.layoutParams = nameViewParams @@ -147,14 +150,16 @@ class ReactionAnimator( nameView.text = " " + callReaction.userName + " " nameView.setTextColor(context.resources.getColor(R.color.white, null)) - val backgroundColor = ContextCompat.getColor( - context, - R.color.colorPrimary - ) + val backgroundColor = + ContextCompat.getColor( + context, + R.color.colorPrimary + ) - val drawable = AppCompatResources - .getDrawable(context, R.drawable.reaction_self_background)!! - .mutate() + val drawable = + AppCompatResources + .getDrawable(context, R.drawable.reaction_self_background)!! + .mutate() DrawableCompat.setTintList( drawable, ColorStateList.valueOf(backgroundColor) @@ -182,6 +187,7 @@ class ReactionAnimator( private const val BOTTOM_MARGIN: Int = 5 } } + data class CallReaction( var emoji: String, var userName: String diff --git a/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt b/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt index 5d88fdc6490..fa5e1f80628 100644 --- a/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/callnotification/CallNotificationActivity.kt @@ -147,14 +147,16 @@ class CallNotificationActivity : CallBaseActivity() { } private fun initObservers() { - val apiVersion = ApiUtils.getConversationApiVersion( - userBeingCalled, - intArrayOf( - ApiUtils.APIv4, - ApiUtils.APIv3, - 1 - ) - ) + val apiVersion = + ApiUtils + .getConversationApiVersion( + userBeingCalled, + intArrayOf( + ApiUtils.APIv4, + ApiUtils.APIv3, + 1 + ) + ) callNotificationViewModel.getRoomViewState.observe(this) { state -> when (state) { @@ -174,34 +176,41 @@ class CallNotificationActivity : CallBaseActivity() { } val notificationHandler = Handler(Looper.getMainLooper()) - notificationHandler.post(object : Runnable { - override fun run() { - if (NotificationUtils.isNotificationVisible(context, notificationTimestamp!!.toInt())) { - notificationHandler.postDelayed(this, ONE_SECOND) - } else { - finish() + notificationHandler.post( + object : Runnable { + override fun run() { + if (NotificationUtils.isNotificationVisible(context, notificationTimestamp!!.toInt())) { + notificationHandler.postDelayed(this, ONE_SECOND) + } else { + finish() + } } } - }) + ) showAnswerControls() if (apiVersion >= ApiUtils.APIv3) { - val hasCallFlags = hasSpreedFeatureCapability( - userBeingCalled, - "conversation-call-flags" - ) + val hasCallFlags = + hasSpreedFeatureCapability( + userBeingCalled, + "conversation-call-flags" + ) if (hasCallFlags) { if (isInCallWithVideo(currentConversation!!.callFlag)) { - binding!!.incomingCallVoiceOrVideoTextView.text = String.format( - resources.getString(R.string.nc_call_video), - resources.getString(R.string.nc_app_product_name) - ) + binding!!.incomingCallVoiceOrVideoTextView.text = + String + .format( + resources.getString(R.string.nc_call_video), + resources.getString(R.string.nc_app_product_name) + ) } else { - binding!!.incomingCallVoiceOrVideoTextView.text = String.format( - resources.getString(R.string.nc_call_voice), - resources.getString(R.string.nc_app_product_name) - ) + binding!!.incomingCallVoiceOrVideoTextView.text = + String + .format( + resources.getString(R.string.nc_call_voice), + resources.getString(R.string.nc_app_product_name) + ) } } } @@ -219,10 +228,12 @@ class CallNotificationActivity : CallBaseActivity() { } private fun setCallDescriptionText() { - val callDescriptionWithoutTypeInfo = String.format( - resources.getString(R.string.nc_call_unknown), - resources.getString(R.string.nc_app_product_name) - ) + val callDescriptionWithoutTypeInfo = + String + .format( + resources.getString(R.string.nc_call_unknown), + resources.getString(R.string.nc_app_product_name) + ) binding!!.incomingCallVoiceOrVideoTextView.text = callDescriptionWithoutTypeInfo } @@ -242,10 +253,11 @@ class CallNotificationActivity : CallBaseActivity() { originalBundle!!.putString(KEY_ROOM_TOKEN, currentConversation!!.token) originalBundle!!.putString(KEY_CONVERSATION_NAME, currentConversation!!.displayName) - val participantPermission = ParticipantPermissions( - userBeingCalled!!, - currentConversation!! - ) + val participantPermission = + ParticipantPermissions( + userBeingCalled!!, + currentConversation!! + ) originalBundle!!.putBoolean( BundleKeys.KEY_PARTICIPANT_PERMISSION_CAN_PUBLISH_AUDIO, participantPermission.canPublishAudio() diff --git a/app/src/main/java/com/nextcloud/talk/callnotification/viewmodel/CallNotificationViewModel.kt b/app/src/main/java/com/nextcloud/talk/callnotification/viewmodel/CallNotificationViewModel.kt index 776daf6d8ae..80b833fc7ba 100644 --- a/app/src/main/java/com/nextcloud/talk/callnotification/viewmodel/CallNotificationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/callnotification/viewmodel/CallNotificationViewModel.kt @@ -33,12 +33,16 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class CallNotificationViewModel @Inject constructor(private val repository: ChatRepository) : +class CallNotificationViewModel +@Inject +constructor(private val repository: ChatRepository) : ViewModel() { sealed interface ViewState object GetRoomStartState : ViewState + object GetRoomErrorState : ViewState + open class GetRoomSuccessState(val conversationModel: ConversationModel) : ViewState private val _getRoomViewState: MutableLiveData = MutableLiveData(GetRoomStartState) diff --git a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt index a82e70fce61..4b49b72af5c 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -346,9 +346,17 @@ class ChatActivity : private var isVoicePreviewPlaying: Boolean = false private var recorder: MediaRecorder? = null + private enum class MediaRecorderState { - INITIAL, INITIALIZED, CONFIGURED, PREPARED, RECORDING, RELEASED, ERROR + INITIAL, + INITIALIZED, + CONFIGURED, + PREPARED, + RECORDING, + RELEASED, + ERROR } + private var mediaRecorderState: MediaRecorderState = MediaRecorderState.INITIAL private var voicePreviewMediaPlayer: MediaPlayer? = null @@ -362,11 +370,12 @@ class ChatActivity : private lateinit var micInputAudioRecorder: AudioRecord private var micInputAudioRecordThread: Thread? = null private var isMicInputAudioThreadRunning: Boolean = false - private val bufferSize = AudioRecord.getMinBufferSize( - SAMPLE_RATE, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT - ) + private val bufferSize = + AudioRecord.getMinBufferSize( + SAMPLE_RATE, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT + ) private var voiceRecordDuration = 0L private var voiceRecordPauseTime = 0L @@ -379,13 +388,14 @@ class ChatActivity : private var videoURI: Uri? = null - private val onBackPressedCallback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - val intent = Intent(this@ChatActivity, ConversationsListActivity::class.java) - intent.putExtras(Bundle()) - startActivity(intent) + private val onBackPressedCallback = + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + val intent = Intent(this@ChatActivity, ConversationsListActivity::class.java) + intent.putExtras(Bundle()) + startActivity(intent) + } } - } var typingTimer: CountDownTimer? = null var typedWhileTypingTimerIsRunning: Boolean = false @@ -393,55 +403,58 @@ class ChatActivity : var callStarted = false - private val localParticipantMessageListener = object : SignalingMessageReceiver.LocalParticipantMessageListener { - override fun onSwitchTo(token: String?) { - if (token != null) { - if (CallActivity.active) { - Log.d(TAG, "CallActivity is running. Ignore to switch chat in ChatActivity...") - } else { - switchToRoom(token, false, false) + private val localParticipantMessageListener = + object : SignalingMessageReceiver.LocalParticipantMessageListener { + override fun onSwitchTo(token: String?) { + if (token != null) { + if (CallActivity.active) { + Log.d(TAG, "CallActivity is running. Ignore to switch chat in ChatActivity...") + } else { + switchToRoom(token, false, false) + } } } } - } - private val conversationMessageListener = object : SignalingMessageReceiver.ConversationMessageListener { - override fun onStartTyping(userId: String?, session: String?) { - val userIdOrGuestSession = userId ?: session + private val conversationMessageListener = + object : SignalingMessageReceiver.ConversationMessageListener { + override fun onStartTyping(userId: String?, session: String?) { + val userIdOrGuestSession = userId ?: session - if (isTypingStatusEnabled() && conversationUser?.userId != userIdOrGuestSession) { - var displayName = webSocketInstance?.getDisplayNameForSession(session) + if (isTypingStatusEnabled() && conversationUser?.userId != userIdOrGuestSession) { + var displayName = webSocketInstance?.getDisplayNameForSession(session) - if (displayName != null && !typingParticipants.contains(userIdOrGuestSession)) { - if (displayName == "") { - displayName = context.resources?.getString(R.string.nc_guest)!! - } + if (displayName != null && !typingParticipants.contains(userIdOrGuestSession)) { + if (displayName == "") { + displayName = context.resources?.getString(R.string.nc_guest)!! + } - runOnUiThread { - val typingParticipant = TypingParticipant(userIdOrGuestSession!!, displayName) { - typingParticipants.remove(userIdOrGuestSession) + runOnUiThread { + val typingParticipant = + TypingParticipant(userIdOrGuestSession!!, displayName) { + typingParticipants.remove(userIdOrGuestSession) + updateTypingIndicator() + } + + typingParticipants[userIdOrGuestSession] = typingParticipant updateTypingIndicator() } - - typingParticipants[userIdOrGuestSession] = typingParticipant - updateTypingIndicator() + } else if (typingParticipants.contains(userIdOrGuestSession)) { + typingParticipants[userIdOrGuestSession]?.restartTimer() } - } else if (typingParticipants.contains(userIdOrGuestSession)) { - typingParticipants[userIdOrGuestSession]?.restartTimer() } } - } - override fun onStopTyping(userId: String?, session: String?) { - val userIdOrGuestSession = userId ?: session + override fun onStopTyping(userId: String?, session: String?) { + val userIdOrGuestSession = userId ?: session - if (isTypingStatusEnabled() && conversationUser?.userId != userId) { - typingParticipants[userIdOrGuestSession]?.cancelTimer() - typingParticipants.remove(userIdOrGuestSession) - updateTypingIndicator() + if (isTypingStatusEnabled() && conversationUser?.userId != userId) { + typingParticipants[userIdOrGuestSession]?.cancelTimer() + typingParticipants.remove(userIdOrGuestSession) + updateTypingIndicator() + } } } - } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -499,11 +512,12 @@ class ChatActivity : roomPassword = extras?.getString(BundleKeys.KEY_CONVERSATION_PASSWORD).orEmpty() - credentials = if (conversationUser?.userId == "?") { - null - } else { - ApiUtils.getCredentials(conversationUser!!.username, conversationUser!!.token) - } + credentials = + if (conversationUser?.userId == "?") { + null + } else { + ApiUtils.getCredentials(conversationUser!!.username, conversationUser!!.token) + } startCallFromNotification = extras?.getBoolean(BundleKeys.KEY_FROM_NOTIFICATION_START_CALL, false) == true startCallFromRoomSwitch = extras?.getBoolean(KEY_START_CALL_AFTER_ROOM_SWITCH, false) == true @@ -576,11 +590,12 @@ class ChatActivity : Log.d(TAG, "already inConversation. joinRoomWithPassword is skipped") } - val delayForRecursiveCall = if (shouldShowLobby()) { - GET_ROOM_INFO_DELAY_LOBBY - } else { - GET_ROOM_INFO_DELAY_NORMAL - } + val delayForRecursiveCall = + if (shouldShowLobby()) { + GET_ROOM_INFO_DELAY_LOBBY + } else { + GET_ROOM_INFO_DELAY_NORMAL + } if (getRoomInfoTimerHandler == null) { getRoomInfoTimerHandler = Handler() @@ -680,11 +695,12 @@ class ChatActivity : binding.popupBubbleView.setPopupBubbleListener { context -> if (newMessagesCount != 0) { - val scrollPosition = if (newMessagesCount - 1 < 0) { - 0 - } else { - newMessagesCount - 1 - } + val scrollPosition = + if (newMessagesCount - 1 < 0) { + 0 + } else { + newMessagesCount - 1 + } Handler().postDelayed( { binding.messagesListView.smoothScrollToPosition(scrollPosition) @@ -707,29 +723,31 @@ class ChatActivity : binding.messageInputView.setPadding(0, 0, 0, 0) - binding.messagesListView.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - super.onScrollStateChanged(recyclerView, newState) + binding.messagesListView.addOnScrollListener( + object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) - if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) { - if (layoutManager!!.findFirstCompletelyVisibleItemPosition() > 0) { - binding.scrollDownButton.visibility = View.VISIBLE - } else { - binding.scrollDownButton.visibility = View.GONE - } + if (newState == AbsListView.OnScrollListener.SCROLL_STATE_IDLE) { + if (layoutManager!!.findFirstCompletelyVisibleItemPosition() > 0) { + binding.scrollDownButton.visibility = View.VISIBLE + } else { + binding.scrollDownButton.visibility = View.GONE + } - if (newMessagesCount != 0 && layoutManager != null) { - if (layoutManager!!.findFirstCompletelyVisibleItemPosition() < newMessagesCount) { - newMessagesCount = 0 + if (newMessagesCount != 0 && layoutManager != null) { + if (layoutManager!!.findFirstCompletelyVisibleItemPosition() < newMessagesCount) { + newMessagesCount = 0 - if (binding.popupBubbleView.isShown == true) { - binding.popupBubbleView.hide() + if (binding.popupBubbleView.isShown == true) { + binding.popupBubbleView.hide() + } } } } } } - }) + ) initMessageInputView() @@ -746,53 +764,57 @@ class ChatActivity : filters[0] = InputFilter.LengthFilter(lengthFilter) binding.messageInputView.inputEditText?.filters = filters - binding.messageInputView.inputEditText?.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - // unused atm - } - - @Suppress("Detekt.TooGenericExceptionCaught") - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - updateOwnTypingStatus(s) - - if (s.length >= lengthFilter) { - binding.messageInputView.inputEditText?.error = String.format( - Objects.requireNonNull(resources).getString(R.string.nc_limit_hit), - lengthFilter.toString() - ) - } else { - binding.messageInputView.inputEditText?.error = null + binding.messageInputView.inputEditText?.addTextChangedListener( + object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // unused atm } - val editable = binding.messageInputView.inputEditText?.editableText - if (editable != null && binding.messageInputView.inputEditText != null) { - val mentionSpans = editable.getSpans( - 0, - binding.messageInputView.inputEditText!!.length(), - Spans.MentionChipSpan::class.java - ) - var mentionSpan: Spans.MentionChipSpan - for (i in mentionSpans.indices) { - mentionSpan = mentionSpans[i] - if (start >= editable.getSpanStart(mentionSpan) && - start < editable.getSpanEnd(mentionSpan) - ) { - if (editable.subSequence( - editable.getSpanStart(mentionSpan), - editable.getSpanEnd(mentionSpan) - ).toString().trim { it <= ' ' } != mentionSpan.label + @Suppress("Detekt.TooGenericExceptionCaught") + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + updateOwnTypingStatus(s) + + if (s.length >= lengthFilter) { + binding.messageInputView.inputEditText?.error = + String.format( + Objects.requireNonNull(resources).getString(R.string.nc_limit_hit), + lengthFilter.toString() + ) + } else { + binding.messageInputView.inputEditText?.error = null + } + + val editable = binding.messageInputView.inputEditText?.editableText + if (editable != null && binding.messageInputView.inputEditText != null) { + val mentionSpans = + editable.getSpans( + 0, + binding.messageInputView.inputEditText!!.length(), + Spans.MentionChipSpan::class.java + ) + var mentionSpan: Spans.MentionChipSpan + for (i in mentionSpans.indices) { + mentionSpan = mentionSpans[i] + if (start >= editable.getSpanStart(mentionSpan) && + start < editable.getSpanEnd(mentionSpan) ) { - editable.removeSpan(mentionSpan) + if (editable.subSequence( + editable.getSpanStart(mentionSpan), + editable.getSpanEnd(mentionSpan) + ).toString().trim { it <= ' ' } != mentionSpan.label + ) { + editable.removeSpan(mentionSpan) + } } } } } - } - override fun afterTextChanged(s: Editable) { - // unused atm + override fun afterTextChanged(s: Editable) { + // unused atm + } } - }) + ) // Image keyboard support // See: https://developer.android.com/guide/topics/text/image-keyboard @@ -875,22 +897,24 @@ class ChatActivity : } private fun initAdapter() { - val senderId = if (!conversationUser!!.userId.equals("?")) { - "users/" + conversationUser!!.userId - } else { - currentConversation?.actorType + "/" + currentConversation?.actorId - } + val senderId = + if (!conversationUser!!.userId.equals("?")) { + "users/" + conversationUser!!.userId + } else { + currentConversation?.actorType + "/" + currentConversation?.actorId + } Log.d(TAG, "Initialize TalkMessagesListAdapter with senderId: $senderId") - adapter = TalkMessagesListAdapter( - senderId, - initMessageHolders(), - ImageLoader { imageView, url, placeholder -> - imageView.loadAvatarOrImagePreview(url!!, conversationUser!!, placeholder as Drawable?) - }, - this - ) + adapter = + TalkMessagesListAdapter( + senderId, + initMessageHolders(), + ImageLoader { imageView, url, placeholder -> + imageView.loadAvatarOrImagePreview(url!!, conversationUser!!, placeholder as Drawable?) + }, + this + ) adapter?.setLoadMoreListener(this) adapter?.setDateHeadersFormatter { format(it) } @@ -943,11 +967,12 @@ class ChatActivity : val messageHolders = MessageHolders() val profileBottomSheet = ProfileBottomSheet(ncApi, conversationUser!!) - val payload = MessagePayload( - roomToken, - ConversationUtils.isParticipantOwnerOrModerator(currentConversation!!), - profileBottomSheet - ) + val payload = + MessagePayload( + roomToken, + ConversationUtils.isParticipantOwnerOrModerator(currentConversation!!), + profileBottomSheet + ) messageHolders.setIncomingTextConfig( IncomingTextMessageViewHolder::class.java, @@ -1117,164 +1142,178 @@ class ChatActivity : if (isVoicePreviewPlaying) { Log.d(TAG, "Paused") pausePreviewVoicePlaying() - binding.messageInputView.playPauseBtn.icon = ContextCompat.getDrawable( - context, - R.drawable - .ic_baseline_play_arrow_voice_message_24 - ) + binding.messageInputView.playPauseBtn.icon = + ContextCompat.getDrawable( + context, + R.drawable + .ic_baseline_play_arrow_voice_message_24 + ) isVoicePreviewPlaying = false } else { Log.d(TAG, "Started") startPreviewVoicePlaying() - binding.messageInputView.playPauseBtn.icon = ContextCompat.getDrawable( - context, - R.drawable - .ic_baseline_pause_voice_message_24 - ) + binding.messageInputView.playPauseBtn.icon = + ContextCompat.getDrawable( + context, + R.drawable + .ic_baseline_pause_voice_message_24 + ) isVoicePreviewPlaying = true } } - binding.messageInputView.recordAudioButton.setOnTouchListener(object : OnTouchListener { - override fun onTouch(v: View?, event: MotionEvent?): Boolean { - v?.performClick() // ????????? - when (event?.action) { - MotionEvent.ACTION_DOWN -> { - if (!isRecordAudioPermissionGranted()) { - requestRecordAudioPermissions() - return true - } - if (!permissionUtil.isFilesPermissionGranted()) { - UploadAndShareFilesWorker.requestStoragePermission(this@ChatActivity) - return true - } - - voiceRecordStartTime = System.currentTimeMillis() + binding.messageInputView.recordAudioButton.setOnTouchListener( + object : OnTouchListener { + override fun onTouch(v: View?, event: MotionEvent?): Boolean { + v?.performClick() // ????????? + when (event?.action) { + MotionEvent.ACTION_DOWN -> { + if (!isRecordAudioPermissionGranted()) { + requestRecordAudioPermissions() + return true + } + if (!permissionUtil.isFilesPermissionGranted()) { + UploadAndShareFilesWorker.requestStoragePermission(this@ChatActivity) + return true + } - setVoiceRecordFileName() - startAudioRecording(currentVoiceRecordFile) - downX = event.x - originY = event.y - showRecordAudioUi(true) - } + voiceRecordStartTime = System.currentTimeMillis() - MotionEvent.ACTION_CANCEL -> { - Log.d(TAG, "ACTION_CANCEL. same as for UP") - if (mediaRecorderState != MediaRecorderState.RECORDING || !isRecordAudioPermissionGranted()) { - return true + setVoiceRecordFileName() + startAudioRecording(currentVoiceRecordFile) + downX = event.x + originY = event.y + showRecordAudioUi(true) } - stopAndDiscardAudioRecording() - endVoiceRecordingUI() - binding.messageInputView.slideToCancelDescription.x = sliderInitX - } - - MotionEvent.ACTION_UP -> { - Log.d(TAG, "ACTION_UP. stop recording??") - if (mediaRecorderState != MediaRecorderState.RECORDING || - !isRecordAudioPermissionGranted() || - isVoiceRecordingLocked - ) { - return true - } - showRecordAudioUi(false) + MotionEvent.ACTION_CANCEL -> { + Log.d(TAG, "ACTION_CANCEL. same as for UP") + if ( + mediaRecorderState != MediaRecorderState.RECORDING || + !isRecordAudioPermissionGranted() + ) { + return true + } - voiceRecordEndTime = System.currentTimeMillis() - voiceRecordDuration = voiceRecordEndTime - voiceRecordStartTime - if (voiceRecordDuration < MINIMUM_VOICE_RECORD_DURATION) { - Log.d(TAG, "voiceRecordDuration: $voiceRecordDuration") - Snackbar.make( - binding.root, - context.getString(R.string.nc_voice_message_hold_to_record_info), - Snackbar.LENGTH_SHORT - ).show() stopAndDiscardAudioRecording() - return true - } else { - voiceRecordStartTime = 0L - voiceRecordEndTime = 0L - stopAndSendAudioRecording() + endVoiceRecordingUI() + binding.messageInputView.slideToCancelDescription.x = sliderInitX } - binding.messageInputView.slideToCancelDescription.x = sliderInitX - } - - MotionEvent.ACTION_MOVE -> { - Log.d(TAG, "ACTION_MOVE.") + MotionEvent.ACTION_UP -> { + Log.d(TAG, "ACTION_UP. stop recording??") + if (mediaRecorderState != MediaRecorderState.RECORDING || + !isRecordAudioPermissionGranted() || + isVoiceRecordingLocked + ) { + return true + } + showRecordAudioUi(false) + + voiceRecordEndTime = System.currentTimeMillis() + voiceRecordDuration = voiceRecordEndTime - voiceRecordStartTime + if (voiceRecordDuration < MINIMUM_VOICE_RECORD_DURATION) { + Log.d(TAG, "voiceRecordDuration: $voiceRecordDuration") + Snackbar.make( + binding.root, + context.getString(R.string.nc_voice_message_hold_to_record_info), + Snackbar.LENGTH_SHORT + ).show() + stopAndDiscardAudioRecording() + return true + } else { + voiceRecordStartTime = 0L + voiceRecordEndTime = 0L + stopAndSendAudioRecording() + } - if (mediaRecorderState != MediaRecorderState.RECORDING || !isRecordAudioPermissionGranted()) { - return true + binding.messageInputView.slideToCancelDescription.x = sliderInitX } - showRecordAudioUi(true) - - val movedX: Float = event.x - val movedY: Float = event.y - deltaX = movedX - downX - deltaY = movedY - originY - - binding.voiceRecordingLock.translationY.let { - if (it < VOICE_RECORD_LOCK_BUTTON_Y) { - Log.d(TAG, "Voice Recording Locked") - isVoiceRecordingLocked = true - showVoiceRecordingLocked(true) - showVoiceRecordingLockedInterface(true) - startMicInputRecordingAnimation() - } else if (deltaY < 0f) { - binding.voiceRecordingLock.translationY = deltaY + MotionEvent.ACTION_MOVE -> { + Log.d(TAG, "ACTION_MOVE.") + + if ( + mediaRecorderState != MediaRecorderState.RECORDING || + !isRecordAudioPermissionGranted() + ) { + return true } - } - // only allow slide to left - binding.messageInputView.slideToCancelDescription.x.let { - if (sliderInitX == 0.0F) { - sliderInitX = it + showRecordAudioUi(true) + + val movedX: Float = event.x + val movedY: Float = event.y + deltaX = movedX - downX + deltaY = movedY - originY + + binding.voiceRecordingLock.translationY.let { + if (it < VOICE_RECORD_LOCK_BUTTON_Y) { + Log.d(TAG, "Voice Recording Locked") + isVoiceRecordingLocked = true + showVoiceRecordingLocked(true) + showVoiceRecordingLockedInterface(true) + startMicInputRecordingAnimation() + } else if (deltaY < 0f) { + binding.voiceRecordingLock.translationY = deltaY + } } - if (it > sliderInitX) { - binding.messageInputView.slideToCancelDescription.x = sliderInitX + // only allow slide to left + binding.messageInputView.slideToCancelDescription.x.let { + if (sliderInitX == 0.0F) { + sliderInitX = it + } + + if (it > sliderInitX) { + binding.messageInputView.slideToCancelDescription.x = sliderInitX + } } - } - binding.messageInputView.slideToCancelDescription.x.let { - if (it < VOICE_RECORD_CANCEL_SLIDER_X) { - Log.d(TAG, "stopping recording because slider was moved to left") - stopAndDiscardAudioRecording() - endVoiceRecordingUI() - binding.messageInputView.slideToCancelDescription.x = sliderInitX - return true - } else { - binding.messageInputView.slideToCancelDescription.x = it + deltaX - downX = movedX + binding.messageInputView.slideToCancelDescription.x.let { + if (it < VOICE_RECORD_CANCEL_SLIDER_X) { + Log.d(TAG, "stopping recording because slider was moved to left") + stopAndDiscardAudioRecording() + endVoiceRecordingUI() + binding.messageInputView.slideToCancelDescription.x = sliderInitX + return true + } else { + binding.messageInputView.slideToCancelDescription.x = it + deltaX + downX = movedX + } } } } - } - return v?.onTouchEvent(event) ?: true + return v?.onTouchEvent(event) ?: true + } } - }) + ) } private fun showPreviewVoiceRecording(value: Boolean) { - val micInputCloudLayoutParams: LayoutParams = binding.messageInputView.micInputCloud - .layoutParams as LayoutParams + val micInputCloudLayoutParams: LayoutParams = + binding.messageInputView.micInputCloud + .layoutParams as LayoutParams - val deleteVoiceRecordingLayoutParams: LayoutParams = binding.messageInputView.deleteVoiceRecording - .layoutParams as LayoutParams + val deleteVoiceRecordingLayoutParams: LayoutParams = + binding.messageInputView.deleteVoiceRecording + .layoutParams as LayoutParams - val sendVoiceRecordingLayoutParams: LayoutParams = binding.messageInputView.sendVoiceRecording - .layoutParams as LayoutParams + val sendVoiceRecordingLayoutParams: LayoutParams = + binding.messageInputView.sendVoiceRecording + .layoutParams as LayoutParams if (value) { voiceRecordPauseTime = binding.messageInputView.audioRecordDuration.base - SystemClock.elapsedRealtime() binding.messageInputView.audioRecordDuration.stop() binding.messageInputView.audioRecordDuration.visibility = View.GONE binding.messageInputView.playPauseBtn.visibility = View.VISIBLE - binding.messageInputView.playPauseBtn.icon = ContextCompat.getDrawable( - context, - R.drawable.ic_baseline_play_arrow_voice_message_24 - ) + binding.messageInputView.playPauseBtn.icon = + ContextCompat.getDrawable( + context, + R.drawable.ic_baseline_play_arrow_voice_message_24 + ) binding.messageInputView.seekBar.visibility = View.VISIBLE binding.messageInputView.seekBar.progress = 0 binding.messageInputView.seekBar.max = 0 @@ -1300,29 +1339,31 @@ class ChatActivity : } private fun initPreviewVoiceRecording() { - voicePreviewMediaPlayer = MediaPlayer().apply { - setDataSource(currentVoiceRecordFile) - prepare() - setOnPreparedListener { - binding.messageInputView.seekBar.progress = 0 - binding.messageInputView.seekBar.max = it.duration - voicePreviewObjectAnimator = ObjectAnimator.ofInt( - binding.messageInputView.seekBar, - "progress", - 0, - it.duration - ).apply { - duration = it.duration.toLong() - interpolator = LinearInterpolator() + voicePreviewMediaPlayer = + MediaPlayer().apply { + setDataSource(currentVoiceRecordFile) + prepare() + setOnPreparedListener { + binding.messageInputView.seekBar.progress = 0 + binding.messageInputView.seekBar.max = it.duration + voicePreviewObjectAnimator = + ObjectAnimator.ofInt( + binding.messageInputView.seekBar, + "progress", + 0, + it.duration + ).apply { + duration = it.duration.toLong() + interpolator = LinearInterpolator() + } + voicePreviewMediaPlayer!!.start() + voicePreviewObjectAnimator!!.start() } - voicePreviewMediaPlayer!!.start() - voicePreviewObjectAnimator!!.start() - } - setOnCompletionListener { - stopPreviewVoicePlaying() + setOnCompletionListener { + stopPreviewVoicePlaying() + } } - } } private fun startPreviewVoicePlaying() { @@ -1382,24 +1423,25 @@ class ChatActivity : } private fun showVoiceRecordingLockedInterface(value: Boolean) { - val audioDurationLayoutParams: LayoutParams = binding.messageInputView.audioRecordDuration - .layoutParams as LayoutParams + val audioDurationLayoutParams: LayoutParams = + binding.messageInputView.audioRecordDuration.layoutParams as LayoutParams - val micInputCloudLayoutParams: LayoutParams = binding.messageInputView.micInputCloud - .layoutParams as LayoutParams + val micInputCloudLayoutParams: LayoutParams = + binding.messageInputView.micInputCloud.layoutParams as LayoutParams - val deleteVoiceRecordingLayoutParams: LayoutParams = binding.messageInputView.deleteVoiceRecording - .layoutParams as LayoutParams + val deleteVoiceRecordingLayoutParams: LayoutParams = + binding.messageInputView.deleteVoiceRecording.layoutParams as LayoutParams - val sendVoiceRecordingLayoutParams: LayoutParams = binding.messageInputView.sendVoiceRecording - .layoutParams as LayoutParams + val sendVoiceRecordingLayoutParams: LayoutParams = + binding.messageInputView.sendVoiceRecording.layoutParams as LayoutParams - val standardQuarterMargin = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - resources.getDimension(R.dimen.standard_quarter_margin), - resources - .displayMetrics - ).toInt() + val standardQuarterMargin = + TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + resources.getDimension(R.dimen.standard_quarter_margin), + resources + .displayMetrics + ).toInt() binding.messageInputView.button.isEnabled = true if (value) { @@ -1439,27 +1481,28 @@ class ChatActivity : private fun initSmileyKeyboardToggler() { val smileyButton = binding.messageInputView.findViewById(R.id.smileyButton) - emojiPopup = binding.messageInputView.inputEditText?.let { - EmojiPopup( - rootView = binding.root, - editText = it, - onEmojiPopupShownListener = { - if (resources != null) { + emojiPopup = + binding.messageInputView.inputEditText?.let { + EmojiPopup( + rootView = binding.root, + editText = it, + onEmojiPopupShownListener = { + if (resources != null) { + smileyButton?.setImageDrawable( + ContextCompat.getDrawable(context, R.drawable.ic_baseline_keyboard_24) + ) + } + }, + onEmojiPopupDismissListener = { smileyButton?.setImageDrawable( - ContextCompat.getDrawable(context, R.drawable.ic_baseline_keyboard_24) + ContextCompat.getDrawable(context, R.drawable.ic_insert_emoticon_black_24dp) ) + }, + onEmojiClickListener = { + binding.messageInputView.inputEditText?.editableText?.append(" ") } - }, - onEmojiPopupDismissListener = { - smileyButton?.setImageDrawable( - ContextCompat.getDrawable(context, R.drawable.ic_insert_emoticon_black_24dp) - ) - }, - onEmojiClickListener = { - binding.messageInputView.inputEditText?.editableText?.append(" ") - } - ) - } + ) + } smileyButton?.setOnClickListener { emojiPopup?.toggle() @@ -1483,48 +1526,58 @@ class ChatActivity : 0 -> typingString = SpannableStringBuilder().append(binding.typingIndicator.text) // person1 is typing - 1 -> typingString = SpannableStringBuilder() - .bold { append(ellipsize(participantNames[0])) } - .append(WHITESPACE + context.resources?.getString(R.string.typing_is_typing)) + 1 -> + typingString = + SpannableStringBuilder() + .bold { append(ellipsize(participantNames[0])) } + .append(WHITESPACE + context.resources?.getString(R.string.typing_is_typing)) // person1 and person2 are typing - 2 -> typingString = SpannableStringBuilder() - .bold { append(ellipsize(participantNames[0])) } - .append(WHITESPACE + context.resources?.getString(R.string.nc_common_and) + WHITESPACE) - .bold { append(ellipsize(participantNames[1])) } - .append(WHITESPACE + context.resources?.getString(R.string.typing_are_typing)) + 2 -> + typingString = + SpannableStringBuilder() + .bold { append(ellipsize(participantNames[0])) } + .append(WHITESPACE + context.resources?.getString(R.string.nc_common_and) + WHITESPACE) + .bold { append(ellipsize(participantNames[1])) } + .append(WHITESPACE + context.resources?.getString(R.string.typing_are_typing)) // person1, person2 and person3 are typing - 3 -> typingString = SpannableStringBuilder() - .bold { append(ellipsize(participantNames[0])) } - .append(COMMA) - .bold { append(ellipsize(participantNames[1])) } - .append(WHITESPACE + context.resources?.getString(R.string.nc_common_and) + WHITESPACE) - .bold { append(ellipsize(participantNames[2])) } - .append(WHITESPACE + context.resources?.getString(R.string.typing_are_typing)) + 3 -> + typingString = + SpannableStringBuilder() + .bold { append(ellipsize(participantNames[0])) } + .append(COMMA) + .bold { append(ellipsize(participantNames[1])) } + .append(WHITESPACE + context.resources?.getString(R.string.nc_common_and) + WHITESPACE) + .bold { append(ellipsize(participantNames[2])) } + .append(WHITESPACE + context.resources?.getString(R.string.typing_are_typing)) // person1, person2, person3 and 1 other is typing - 4 -> typingString = SpannableStringBuilder() - .bold { append(participantNames[0]) } - .append(COMMA) - .bold { append(participantNames[1]) } - .append(COMMA) - .bold { append(participantNames[2]) } - .append(WHITESPACE + context.resources?.getString(R.string.typing_1_other)) + 4 -> + typingString = + SpannableStringBuilder() + .bold { append(participantNames[0]) } + .append(COMMA) + .bold { append(participantNames[1]) } + .append(COMMA) + .bold { append(participantNames[2]) } + .append(WHITESPACE + context.resources?.getString(R.string.typing_1_other)) // person1, person2, person3 and x others are typing else -> { val moreTypersAmount = typingParticipants.size - 3 - val othersTyping = context.resources?.getString(R.string.typing_x_others)?.let { - String.format(it, moreTypersAmount) - } - typingString = SpannableStringBuilder() - .bold { append(participantNames[0]) } - .append(COMMA) - .bold { append(participantNames[1]) } - .append(COMMA) - .bold { append(participantNames[2]) } - .append(othersTyping) + val othersTyping = + context.resources?.getString(R.string.typing_x_others)?.let { + String.format(it, moreTypersAmount) + } + typingString = + SpannableStringBuilder() + .bold { append(participantNames[0]) } + .append(COMMA) + .bold { append(participantNames[1]) } + .append(COMMA) + .bold { append(participantNames[2]) } + .append(othersTyping) } } @@ -1568,25 +1621,26 @@ class ChatActivity : } else if (typingTimer == null) { sendStartTypingSignalingMessage() - typingTimer = object : CountDownTimer( - TYPING_DURATION_TO_SEND_NEXT_TYPING_MESSAGE, - TYPING_INTERVAL_TO_SEND_NEXT_TYPING_MESSAGE - ) { - override fun onTick(millisUntilFinished: Long) { - // unused - } + typingTimer = + object : CountDownTimer( + TYPING_DURATION_TO_SEND_NEXT_TYPING_MESSAGE, + TYPING_INTERVAL_TO_SEND_NEXT_TYPING_MESSAGE + ) { + override fun onTick(millisUntilFinished: Long) { + // unused + } - override fun onFinish() { - if (typedWhileTypingTimerIsRunning) { - sendStartTypingSignalingMessage() - cancel() - start() - typedWhileTypingTimerIsRunning = false - } else { - sendStopTypingMessage() + override fun onFinish() { + if (typedWhileTypingTimerIsRunning) { + sendStartTypingSignalingMessage() + cancel() + start() + typedWhileTypingTimerIsRunning = false + } else { + sendStopTypingMessage() + } } - } - }.start() + }.start() } else { typedWhileTypingTimerIsRunning = true } @@ -1617,17 +1671,18 @@ class ChatActivity : participantPermissions.hasChatPermission() && !isReadOnlyConversation() ) { - val messageSwipeCallback = MessageSwipeCallback( - this, - object : MessageSwipeActions { - override fun showReplyUI(position: Int) { - val chatMessage = adapter?.items?.getOrNull(position)?.item as ChatMessage? - if (chatMessage != null) { - replyToMessage(chatMessage) + val messageSwipeCallback = + MessageSwipeCallback( + this, + object : MessageSwipeActions { + override fun showReplyUI(position: Int) { + val chatMessage = adapter?.items?.getOrNull(position)?.item as ChatMessage? + if (chatMessage != null) { + replyToMessage(chatMessage) + } } } - } - ) + ) val itemTouchHelper = ItemTouchHelper(messageSwipeCallback) itemTouchHelper.attachToRecyclerView(binding.messagesListView) @@ -1640,53 +1695,58 @@ class ChatActivity : } if (isOneToOneConversation()) { - var url = ApiUtils.getUrlForAvatar( - conversationUser!!.baseUrl, - currentConversation!!.name, - true - ) + var url = + ApiUtils.getUrlForAvatar( + conversationUser!!.baseUrl, + currentConversation!!.name, + true + ) if (DisplayUtils.isDarkModeOn(supportActionBar?.themedContext)) { url = "$url/dark" } - val target = object : Target { - private fun setIcon(drawable: Drawable?) { - supportActionBar?.let { - val avatarSize = (it.height / TOOLBAR_AVATAR_RATIO).roundToInt() - val size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, context) - if (drawable != null && avatarSize > 0) { - val bitmap = drawable.toBitmap(avatarSize, avatarSize) - val status = StatusDrawable( - currentConversation!!.status, - null, - size, - 0, - binding.chatToolbar.context - ) - viewThemeUtils.talk.themeStatusDrawable(context, status) - binding.chatToolbar.findViewById(R.id.chat_toolbar_avatar) - .setImageDrawable(BitmapDrawable(resources, bitmap)) - binding.chatToolbar.findViewById(R.id.chat_toolbar_status) - .setImageDrawable(status) - binding.chatToolbar.findViewById(R.id.chat_toolbar_status).contentDescription = - currentConversation?.status - binding.chatToolbar.findViewById(R.id.chat_toolbar_avatar_container) - .visibility = View.VISIBLE - } else { - Log.d(TAG, "loadAvatarForStatusBar avatarSize <= 0") + val target = + object : Target { + private fun setIcon(drawable: Drawable?) { + supportActionBar?.let { + val avatarSize = (it.height / TOOLBAR_AVATAR_RATIO).roundToInt() + val size = DisplayUtils.convertDpToPixel(STATUS_SIZE_IN_DP, context) + if (drawable != null && avatarSize > 0) { + val bitmap = drawable.toBitmap(avatarSize, avatarSize) + val status = + StatusDrawable( + currentConversation!!.status, + null, + size, + 0, + binding.chatToolbar.context + ) + viewThemeUtils.talk.themeStatusDrawable(context, status) + binding.chatToolbar.findViewById(R.id.chat_toolbar_avatar) + .setImageDrawable(BitmapDrawable(resources, bitmap)) + binding.chatToolbar.findViewById(R.id.chat_toolbar_status) + .setImageDrawable(status) + binding + .chatToolbar + .findViewById(R.id.chat_toolbar_status) + .contentDescription = currentConversation?.status + binding.chatToolbar.findViewById(R.id.chat_toolbar_avatar_container) + .visibility = View.VISIBLE + } else { + Log.d(TAG, "loadAvatarForStatusBar avatarSize <= 0") + } } } - } - override fun onStart(placeholder: Drawable?) { - this.setIcon(placeholder) - } + override fun onStart(placeholder: Drawable?) { + this.setIcon(placeholder) + } - override fun onSuccess(result: Drawable) { - this.setIcon(result) + override fun onSuccess(result: Drawable) { + this.setIcon(result) + } } - } val credentials = ApiUtils.getCredentials(conversationUser!!.username, conversationUser!!.token) @@ -1706,14 +1766,17 @@ class ChatActivity : } } - fun isOneToOneConversation() = currentConversation != null && currentConversation?.type != null && - currentConversation?.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL + fun isOneToOneConversation() = + currentConversation != null && currentConversation?.type != null && + currentConversation?.type == ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL - private fun isGroupConversation() = currentConversation != null && currentConversation?.type != null && - currentConversation?.type == ConversationType.ROOM_GROUP_CALL + private fun isGroupConversation() = + currentConversation != null && currentConversation?.type != null && + currentConversation?.type == ConversationType.ROOM_GROUP_CALL - private fun isPublicConversation() = currentConversation != null && currentConversation?.type != null && - currentConversation?.type == ConversationType.ROOM_PUBLIC_CALL + private fun isPublicConversation() = + currentConversation != null && currentConversation?.type != null && + currentConversation?.type == ConversationType.ROOM_PUBLIC_CALL private fun switchToRoom(token: String, startCallAfterRoomSwitch: Boolean, isVoiceOnlyCall: Boolean) { if (conversationUser != null) { @@ -1751,11 +1814,12 @@ class ChatActivity : } private fun showSendButtonMenu() { - val popupMenu = PopupMenu( - ContextThemeWrapper(this, R.style.ChatSendButtonMenu), - binding.messageInputView.button, - Gravity.END - ) + val popupMenu = + PopupMenu( + ContextThemeWrapper(this, R.style.ChatSendButtonMenu), + binding.messageInputView.button, + Gravity.END + ) popupMenu.inflate(R.menu.chat_send_menu) popupMenu.setOnMenuItemClickListener { item: MenuItem -> @@ -1772,18 +1836,20 @@ class ChatActivity : } private fun showCallButtonMenu(isVoiceOnlyCall: Boolean) { - val anchor: View? = if (isVoiceOnlyCall) { - findViewById(R.id.conversation_voice_call) - } else { - findViewById(R.id.conversation_video_call) - } + val anchor: View? = + if (isVoiceOnlyCall) { + findViewById(R.id.conversation_voice_call) + } else { + findViewById(R.id.conversation_video_call) + } if (anchor != null) { - val popupMenu = PopupMenu( - ContextThemeWrapper(this, R.style.CallButtonMenu), - anchor, - Gravity.END - ) + val popupMenu = + PopupMenu( + ContextThemeWrapper(this, R.style.CallButtonMenu), + anchor, + Gravity.END + ) popupMenu.inflate(R.menu.chat_call_menu) popupMenu.setOnMenuItemClickListener { item: MenuItem -> @@ -1818,28 +1884,30 @@ class ChatActivity : } mediaPlayerHandler = Handler() - runOnUiThread(object : Runnable { - override fun run() { - if (mediaPlayer != null) { - if (message.isPlayingVoiceMessage) { - val pos = mediaPlayer!!.currentPosition / VOICE_MESSAGE_SEEKBAR_BASE - if (pos < (mediaPlayer!!.duration / VOICE_MESSAGE_SEEKBAR_BASE)) { - lastRecordMediaPosition = mediaPlayer!!.currentPosition - message.voiceMessagePlayedSeconds = pos - message.voiceMessageSeekbarProgress = mediaPlayer!!.currentPosition - adapter?.update(message) - } else { - message.resetVoiceMessage = true - message.voiceMessagePlayedSeconds = 0 - message.voiceMessageSeekbarProgress = 0 - adapter?.update(message) - stopMediaPlayer(message) + runOnUiThread( + object : Runnable { + override fun run() { + if (mediaPlayer != null) { + if (message.isPlayingVoiceMessage) { + val pos = mediaPlayer!!.currentPosition / VOICE_MESSAGE_SEEKBAR_BASE + if (pos < (mediaPlayer!!.duration / VOICE_MESSAGE_SEEKBAR_BASE)) { + lastRecordMediaPosition = mediaPlayer!!.currentPosition + message.voiceMessagePlayedSeconds = pos + message.voiceMessageSeekbarProgress = mediaPlayer!!.currentPosition + adapter?.update(message) + } else { + message.resetVoiceMessage = true + message.voiceMessagePlayedSeconds = 0 + message.voiceMessageSeekbarProgress = 0 + adapter?.update(message) + stopMediaPlayer(message) + } } } + mediaPlayerHandler.postDelayed(this, MILISEC_15) } - mediaPlayerHandler.postDelayed(this, MILISEC_15) } - }) + ) message.isDownloadingVoiceMessage = false message.isPlayingVoiceMessage = true @@ -1868,26 +1936,27 @@ class ChatActivity : val absolutePath = context.cacheDir.absolutePath + "/" + fileName try { - mediaPlayer = MediaPlayer().apply { - setDataSource(absolutePath) - prepare() - setOnPreparedListener { - currentlyPlayedVoiceMessage = message - message.voiceMessageDuration = mediaPlayer!!.duration / VOICE_MESSAGE_SEEKBAR_BASE - lastRecordedSeeked = false - } - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { - setOnMediaTimeDiscontinuityListener { mp, _ -> - if (lastRecordMediaPosition > ONE_SECOND_IN_MILLIS && !lastRecordedSeeked) { - mp.seekTo(lastRecordMediaPosition) - lastRecordedSeeked = true + mediaPlayer = + MediaPlayer().apply { + setDataSource(absolutePath) + prepare() + setOnPreparedListener { + currentlyPlayedVoiceMessage = message + message.voiceMessageDuration = mediaPlayer!!.duration / VOICE_MESSAGE_SEEKBAR_BASE + lastRecordedSeeked = false + } + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { + setOnMediaTimeDiscontinuityListener { mp, _ -> + if (lastRecordMediaPosition > ONE_SECOND_IN_MILLIS && !lastRecordedSeeked) { + mp.seekTo(lastRecordMediaPosition) + lastRecordedSeeked = true + } } } + setOnCompletionListener { + stopMediaPlayer(message) + } } - setOnCompletionListener { - stopMediaPlayer(message) - } - } } catch (e: Exception) { Log.e(TAG, "failed to initialize mediaPlayer", e) Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() @@ -1989,9 +2058,10 @@ class ChatActivity : val path = message.selectedIndividualHashMap!!["path"] // check if download worker is already running - val workers = WorkManager.getInstance( - context - ).getWorkInfosByTag(fileId!!) + val workers = + WorkManager.getInstance( + context + ).getWorkInfosByTag(fileId!!) try { for (workInfo in workers.get()) { if (workInfo.state == WorkInfo.State.RUNNING || workInfo.state == WorkInfo.State.ENQUEUED) { @@ -2005,19 +2075,21 @@ class ChatActivity : Log.e(TAG, "Error when checking if worker already exists", e) } - val data: Data = Data.Builder() - .putString(DownloadFileToCacheWorker.KEY_BASE_URL, baseUrl) - .putString(DownloadFileToCacheWorker.KEY_USER_ID, userId) - .putString(DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, attachmentFolder) - .putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileName) - .putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path) - .putLong(DownloadFileToCacheWorker.KEY_FILE_SIZE, fileSize) - .build() + val data: Data = + Data.Builder() + .putString(DownloadFileToCacheWorker.KEY_BASE_URL, baseUrl) + .putString(DownloadFileToCacheWorker.KEY_USER_ID, userId) + .putString(DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, attachmentFolder) + .putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileName) + .putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path) + .putLong(DownloadFileToCacheWorker.KEY_FILE_SIZE, fileSize) + .build() - val downloadWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(DownloadFileToCacheWorker::class.java) - .setInputData(data) - .addTag(fileId) - .build() + val downloadWorker: OneTimeWorkRequest = + OneTimeWorkRequest.Builder(DownloadFileToCacheWorker::class.java) + .setInputData(data) + .addTag(fileId) + .build() WorkManager.getInstance().enqueue(downloadWorker) @@ -2034,11 +2106,12 @@ class ChatActivity : val simpleDateFormat = SimpleDateFormat(FILE_DATE_PATTERN) val date: String = simpleDateFormat.format(Date()) - val fileNameWithoutSuffix = String.format( - context.resources.getString(R.string.nc_voice_message_filename), - date, - currentConversation!!.displayName - ) + val fileNameWithoutSuffix = + String.format( + context.resources.getString(R.string.nc_voice_message_filename), + date, + currentConversation!!.displayName + ) val fileName = fileNameWithoutSuffix + VOICE_MESSAGE_FILE_SUFFIX currentVoiceRecordFile = "${context.cacheDir.absolutePath}/$fileName" @@ -2070,20 +2143,22 @@ class ChatActivity : } private fun startMicInputRecordingAnimation() { - val permissionCheck = ContextCompat.checkSelfPermission( - context, - Manifest.permission.RECORD_AUDIO - ) + val permissionCheck = + ContextCompat.checkSelfPermission( + context, + Manifest.permission.RECORD_AUDIO + ) if (micInputAudioRecordThread == null && permissionCheck == PERMISSION_GRANTED) { Log.d(TAG, "Mic Animation Started") - micInputAudioRecorder = AudioRecord( - MediaRecorder.AudioSource.MIC, - SAMPLE_RATE, - AudioFormat.CHANNEL_IN_MONO, - AudioFormat.ENCODING_PCM_16BIT, - bufferSize - ) + micInputAudioRecorder = + AudioRecord( + MediaRecorder.AudioSource.MIC, + SAMPLE_RATE, + AudioFormat.CHANNEL_IN_MONO, + AudioFormat.ENCODING_PCM_16BIT, + bufferSize + ) isMicInputAudioThreadRunning = true micInputAudioRecorder.startRecording() initMicInputAudioRecordThread() @@ -2093,32 +2168,33 @@ class ChatActivity : } private fun initMicInputAudioRecordThread() { - micInputAudioRecordThread = Thread( - Runnable { - while (isMicInputAudioThreadRunning) { - val byteArr = ByteArray(bufferSize / 2) - micInputAudioRecorder.read(byteArr, 0, byteArr.size) - val d = abs(byteArr[0].toDouble()) - if (d > AUDIO_VALUE_MAX) { - binding.messageInputView.micInputCloud.setRotationSpeed( - log10(d).toFloat(), - MicInputCloud.MAXIMUM_RADIUS - ) - } else if (d > AUDIO_VALUE_MIN) { - binding.messageInputView.micInputCloud.setRotationSpeed( - log10(d).toFloat(), - MicInputCloud.EXTENDED_RADIUS - ) - } else { - binding.messageInputView.micInputCloud.setRotationSpeed( - 1f, - MicInputCloud.DEFAULT_RADIUS - ) + micInputAudioRecordThread = + Thread( + Runnable { + while (isMicInputAudioThreadRunning) { + val byteArr = ByteArray(bufferSize / 2) + micInputAudioRecorder.read(byteArr, 0, byteArr.size) + val d = abs(byteArr[0].toDouble()) + if (d > AUDIO_VALUE_MAX) { + binding.messageInputView.micInputCloud.setRotationSpeed( + log10(d).toFloat(), + MicInputCloud.MAXIMUM_RADIUS + ) + } else if (d > AUDIO_VALUE_MIN) { + binding.messageInputView.micInputCloud.setRotationSpeed( + log10(d).toFloat(), + MicInputCloud.EXTENDED_RADIUS + ) + } else { + binding.messageInputView.micInputCloud.setRotationSpeed( + 1f, + MicInputCloud.DEFAULT_RADIUS + ) + } + Thread.sleep(AUDIO_VALUE_SLEEP) } - Thread.sleep(AUDIO_VALUE_SLEEP) } - } - ) + ) } private fun stopMicInputRecordingAnimation() { @@ -2154,36 +2230,37 @@ class ChatActivity : } private fun initMediaRecorder(file: String) { - recorder = MediaRecorder().apply { - setAudioSource(MediaRecorder.AudioSource.MIC) - mediaRecorderState = MediaRecorderState.INITIALIZED + recorder = + MediaRecorder().apply { + setAudioSource(MediaRecorder.AudioSource.MIC) + mediaRecorderState = MediaRecorderState.INITIALIZED - setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) - mediaRecorderState = MediaRecorderState.CONFIGURED + setOutputFormat(MediaRecorder.OutputFormat.MPEG_4) + mediaRecorderState = MediaRecorderState.CONFIGURED - setOutputFile(file) - setAudioEncoder(MediaRecorder.AudioEncoder.AAC) - setAudioSamplingRate(VOICE_MESSAGE_SAMPLING_RATE) - setAudioEncodingBitRate(VOICE_MESSAGE_ENCODING_BIT_RATE) - setAudioChannels(VOICE_MESSAGE_CHANNELS) + setOutputFile(file) + setAudioEncoder(MediaRecorder.AudioEncoder.AAC) + setAudioSamplingRate(VOICE_MESSAGE_SAMPLING_RATE) + setAudioEncodingBitRate(VOICE_MESSAGE_ENCODING_BIT_RATE) + setAudioChannels(VOICE_MESSAGE_CHANNELS) - try { - prepare() - mediaRecorderState = MediaRecorderState.PREPARED - } catch (e: IOException) { - mediaRecorderState = MediaRecorderState.ERROR - Log.e(TAG, "prepare for audio recording failed") - } + try { + prepare() + mediaRecorderState = MediaRecorderState.PREPARED + } catch (e: IOException) { + mediaRecorderState = MediaRecorderState.ERROR + Log.e(TAG, "prepare for audio recording failed") + } - try { - start() - mediaRecorderState = MediaRecorderState.RECORDING - Log.d(TAG, "recording started") - } catch (e: IllegalStateException) { - mediaRecorderState = MediaRecorderState.ERROR - Log.e(TAG, "start for audio recording failed") + try { + start() + mediaRecorderState = MediaRecorderState.RECORDING + Log.d(TAG, "recording started") + } catch (e: IllegalStateException) { + mediaRecorderState = MediaRecorderState.ERROR + Log.e(TAG, "start for audio recording failed") + } } - } } private fun stopAndSendAudioRecording() { @@ -2368,10 +2445,11 @@ class ChatActivity : 0L ) { val timestampMS = (currentConversation?.lobbyTimer ?: 0) * DateConstants.SECOND_DIVIDER - val stringWithStartDate = String.format( - resources!!.getString(R.string.nc_lobby_start_date), - dateUtils.getLocalDateTimeStringFromTimestamp(timestampMS) - ) + val stringWithStartDate = + String.format( + resources!!.getString(R.string.nc_lobby_start_date), + dateUtils.getLocalDateTimeStringFromTimestamp(timestampMS) + ) val relativeTime = dateUtils.relativeStartTimeForLobby(timestampMS, resources!!) sb.append("$stringWithStartDate - $relativeTime") @@ -2416,14 +2494,16 @@ class ChatActivity : pathList .chunked(CHUNK_SIZE) .forEach { paths -> - val data = Data.Builder() - .putLong(KEY_INTERNAL_USER_ID, conversationUser!!.id!!) - .putString(KEY_ROOM_TOKEN, roomToken) - .putStringArray(KEY_FILE_PATHS, paths.toTypedArray()) - .build() - val worker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java) - .setInputData(data) - .build() + val data = + Data.Builder() + .putLong(KEY_INTERNAL_USER_ID, conversationUser!!.id!!) + .putString(KEY_ROOM_TOKEN, roomToken) + .putStringArray(KEY_FILE_PATHS, paths.toTypedArray()) + .build() + val worker = + OneTimeWorkRequest.Builder(ShareOperationWorker::class.java) + .setInputData(data) + .build() WorkManager.getInstance().enqueue(worker) } } @@ -2452,11 +2532,12 @@ class ChatActivity : filenamesWithLineBreaks.append(filename).append("\n") } - val newFragment: DialogFragment = FileAttachmentPreviewFragment.newInstance( - filenamesWithLineBreaks.toString(), - filesToUpload, - this::uploadFiles - ) + val newFragment: DialogFragment = + FileAttachmentPreviewFragment.newInstance( + filenamesWithLineBreaks.toString(), + filesToUpload, + this::uploadFiles + ) newFragment.show(supportFragmentManager, FileAttachmentPreviewFragment.TAG) } catch (e: IllegalStateException) { context.resources?.getString(R.string.nc_upload_failed)?.let { @@ -2489,11 +2570,12 @@ class ChatActivity : val file = File(context.cacheDir, fileName) writeContactToVcfFile(cursor, file) - val shareUri = FileProvider.getUriForFile( - this, - BuildConfig.APPLICATION_ID, - File(file.absolutePath) - ) + val shareUri = + FileProvider.getUriForFile( + this, + BuildConfig.APPLICATION_ID, + File(file.absolutePath) + ) uploadFile(shareUri.toString(), false) } cursor?.close() @@ -2526,11 +2608,12 @@ class ChatActivity : filenamesWithLineBreaks.append(filename).append("\n") } - val newFragment: DialogFragment = FileAttachmentPreviewFragment.newInstance( - filenamesWithLineBreaks.toString(), - filesToUpload, - this::uploadFiles - ) + val newFragment: DialogFragment = + FileAttachmentPreviewFragment.newInstance( + filenamesWithLineBreaks.toString(), + filesToUpload, + this::uploadFiles + ) newFragment.show(supportFragmentManager, FileAttachmentPreviewFragment.TAG) } else { UploadAndShareFilesWorker.requestStoragePermission(this) @@ -2567,9 +2650,10 @@ class ChatActivity : } private fun scrollToMessageWithId(messageId: String) { - val position = adapter?.items?.indexOfFirst { - it.item is ChatMessage && (it.item as ChatMessage).id == messageId - } + val position = + adapter?.items?.indexOfFirst { + it.item is ChatMessage && (it.item as ChatMessage).id == messageId + } if (position != null && position >= 0) { binding.messagesListView.smoothScrollToPosition(position) } else { @@ -2699,11 +2783,12 @@ class ChatActivity : } private fun showLocalFilePicker() { - val action = Intent(Intent.ACTION_OPEN_DOCUMENT).apply { - type = "*/*" - addCategory(Intent.CATEGORY_OPENABLE) - putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) - } + val action = + Intent(Intent.ACTION_OPEN_DOCUMENT).apply { + type = "*/*" + addCategory(Intent.CATEGORY_OPENABLE) + putExtra(Intent.EXTRA_ALLOW_MULTIPLE, true) + } startActivityForResult( Intent.createChooser( action, @@ -2784,21 +2869,23 @@ class ChatActivity : resources?.let { val backgroundDrawable = ColorDrawable(it.getColor(R.color.bg_default, null)) val presenter = MentionAutocompletePresenter(this, roomToken) - val callback = MentionAutocompleteCallback( - this, - conversationUser!!, - binding.messageInputView.inputEditText, - viewThemeUtils - ) + val callback = + MentionAutocompleteCallback( + this, + conversationUser!!, + binding.messageInputView.inputEditText, + viewThemeUtils + ) if (mentionAutocomplete == null && binding.messageInputView.inputEditText != null) { - mentionAutocomplete = Autocomplete.on(binding.messageInputView.inputEditText) - .with(elevation) - .with(backgroundDrawable) - .with(MagicCharPolicy('@')) - .with(presenter) - .with(callback) - .build() + mentionAutocomplete = + Autocomplete.on(binding.messageInputView.inputEditText) + .with(elevation) + .with(backgroundDrawable) + .with(MagicCharPolicy('@')) + .with(presenter) + .with(callback) + .build() } } } @@ -2971,9 +3058,7 @@ class ChatActivity : } } - fun leaveRoom( - funToCallWhenLeaveSuccessful: (() -> Unit)? - ) { + fun leaveRoom(funToCallWhenLeaveSuccessful: (() -> Unit)?) { logConversationInfos("leaveRoom") var apiVersion = 1 @@ -2994,57 +3079,63 @@ class ChatActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - disposables.add(d) - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + disposables.add(d) + } - override fun onNext(genericOverall: GenericOverall) { - Log.d(TAG, "leaveRoom - leaveRoom - got response: $startNanoTime") - logConversationInfos("leaveRoom#onNext") + override fun onNext(genericOverall: GenericOverall) { + Log.d(TAG, "leaveRoom - leaveRoom - got response: $startNanoTime") + logConversationInfos("leaveRoom#onNext") - sendStopTypingMessage() + sendStopTypingMessage() - checkingLobbyStatus = false + checkingLobbyStatus = false - if (getRoomInfoTimerHandler != null) { - getRoomInfoTimerHandler?.removeCallbacksAndMessages(null) - } + if (getRoomInfoTimerHandler != null) { + getRoomInfoTimerHandler?.removeCallbacksAndMessages(null) + } - if (webSocketInstance != null && currentConversation != null) { - webSocketInstance?.joinRoomWithRoomTokenAndSession( - "", - sessionIdAfterRoomJoined - ) - } + if (webSocketInstance != null && currentConversation != null) { + webSocketInstance?.joinRoomWithRoomTokenAndSession( + "", + sessionIdAfterRoomJoined + ) + } - sessionIdAfterRoomJoined = "0" + sessionIdAfterRoomJoined = "0" - if (funToCallWhenLeaveSuccessful != null) { - Log.d(TAG, "a callback action was set and is now executed because room was left successfully") - funToCallWhenLeaveSuccessful() + if (funToCallWhenLeaveSuccessful != null) { + Log.d( + TAG, + "a callback action was set and is now executed because room was left successfully" + ) + funToCallWhenLeaveSuccessful() + } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "leaveRoom - leaveRoom - ERROR", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "leaveRoom - leaveRoom - ERROR", e) + } - override fun onComplete() { - Log.d(TAG, "leaveRoom - leaveRoom - completed: $startNanoTime") - disposables.dispose() + override fun onComplete() { + Log.d(TAG, "leaveRoom - leaveRoom - completed: $startNanoTime") + disposables.dispose() + } } - }) + ) } private fun submitMessage(sendWithoutNotification: Boolean) { if (binding.messageInputView.inputEditText != null) { val editable = binding.messageInputView.inputEditText!!.editableText - val mentionSpans = editable.getSpans( - 0, - editable.length, - Spans.MentionChipSpan::class.java - ) + val mentionSpans = + editable.getSpans( + 0, + editable.length, + Spans.MentionChipSpan::class.java + ) var mentionSpan: Spans.MentionChipSpan for (i in mentionSpans.indices) { mentionSpan = mentionSpans[i] @@ -3088,40 +3179,42 @@ class ChatActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - @Suppress("Detekt.TooGenericExceptionCaught") - override fun onNext(genericOverall: GenericOverall) { - myFirstMessage = message + @Suppress("Detekt.TooGenericExceptionCaught") + override fun onNext(genericOverall: GenericOverall) { + myFirstMessage = message - if (binding.popupBubbleView.isShown == true) { - binding.popupBubbleView.hide() + if (binding.popupBubbleView.isShown == true) { + binding.popupBubbleView.hide() + } + binding.messagesListView.smoothScrollToPosition(0) } - binding.messagesListView.smoothScrollToPosition(0) - } - override fun onError(e: Throwable) { - if (e is HttpException) { - val code = e.code() - if (code.toString().startsWith("2")) { - myFirstMessage = message + override fun onError(e: Throwable) { + if (e is HttpException) { + val code = e.code() + if (code.toString().startsWith("2")) { + myFirstMessage = message - if (binding.popupBubbleView.isShown == true) { - binding.popupBubbleView.hide() - } + if (binding.popupBubbleView.isShown == true) { + binding.popupBubbleView.hide() + } - binding.messagesListView.smoothScrollToPosition(0) + binding.messagesListView.smoothScrollToPosition(0) + } } } - } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } showMicrophoneButton(true) } @@ -3139,11 +3232,7 @@ class ChatActivity : signalingMessageSender = webSocketInstance?.signalingMessageSender } - fun pullChatMessages( - lookIntoFuture: Boolean, - setReadMarker: Boolean = true, - xChatLastCommonRead: Int? = null - ) { + fun pullChatMessages(lookIntoFuture: Boolean, setReadMarker: Boolean = true, xChatLastCommonRead: Int? = null) { if (!validSessionId()) { return } @@ -3160,11 +3249,12 @@ class ChatActivity : } pullChatMessagesPending = true - val pullChatMessagesFieldMap = setupFieldsForPullChatMessages( - lookIntoFuture, - xChatLastCommonRead, - setReadMarker - ) + val pullChatMessagesFieldMap = + setupFieldsForPullChatMessages( + lookIntoFuture, + xChatLastCommonRead, + setReadMarker + ) var apiVersion = 1 // FIXME this is a best guess, guests would need to get the capabilities themselves @@ -3179,120 +3269,124 @@ class ChatActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer> { - override fun onSubscribe(d: Disposable) { - disposables.add(d) - } + ?.subscribe( + object : Observer> { + override fun onSubscribe(d: Disposable) { + disposables.add(d) + } - @SuppressLint("NotifyDataSetChanged") - @Suppress("Detekt.TooGenericExceptionCaught") - override fun onNext(response: Response<*>) { - pullChatMessagesPending = false + @SuppressLint("NotifyDataSetChanged") + @Suppress("Detekt.TooGenericExceptionCaught") + override fun onNext(response: Response<*>) { + pullChatMessagesPending = false - when (response.code()) { - HTTP_CODE_NOT_MODIFIED -> { - Log.d(TAG, "pullChatMessages - HTTP_CODE_NOT_MODIFIED.") + when (response.code()) { + HTTP_CODE_NOT_MODIFIED -> { + Log.d(TAG, "pullChatMessages - HTTP_CODE_NOT_MODIFIED.") - if (lookIntoFuture) { - Log.d(TAG, "recursive call to pullChatMessages.") - pullChatMessages(true, setReadMarker, xChatLastCommonRead) + if (lookIntoFuture) { + Log.d(TAG, "recursive call to pullChatMessages.") + pullChatMessages(true, setReadMarker, xChatLastCommonRead) + } } - } - HTTP_CODE_PRECONDITION_FAILED -> { - Log.d(TAG, "pullChatMessages - HTTP_CODE_PRECONDITION_FAILED.") + HTTP_CODE_PRECONDITION_FAILED -> { + Log.d(TAG, "pullChatMessages - HTTP_CODE_PRECONDITION_FAILED.") - if (lookIntoFuture) { - futurePreconditionFailed = true - } else { - pastPreconditionFailed = true + if (lookIntoFuture) { + futurePreconditionFailed = true + } else { + pastPreconditionFailed = true + } } - } - HTTP_CODE_OK -> { - Log.d(TAG, "pullChatMessages - HTTP_CODE_OK.") + HTTP_CODE_OK -> { + Log.d(TAG, "pullChatMessages - HTTP_CODE_OK.") - val chatOverall = response.body() as ChatOverall? + val chatOverall = response.body() as ChatOverall? - var chatMessageList = chatOverall?.ocs!!.data!! + var chatMessageList = chatOverall?.ocs!!.data!! - chatMessageList = handleSystemMessages(chatMessageList) + chatMessageList = handleSystemMessages(chatMessageList) - determinePreviousMessageIds(chatMessageList) + determinePreviousMessageIds(chatMessageList) - handleExpandableSystemMessages(chatMessageList) + handleExpandableSystemMessages(chatMessageList) - processHeaderChatLastGiven(response, lookIntoFuture) + processHeaderChatLastGiven(response, lookIntoFuture) - if (chatMessageList.isNotEmpty() && - ChatMessage.SystemMessageType.CLEARED_CHAT == chatMessageList[0].systemMessageType - ) { - adapter?.clear() - adapter?.notifyDataSetChanged() - } + if (chatMessageList.isNotEmpty() && + ChatMessage.SystemMessageType.CLEARED_CHAT == chatMessageList[0].systemMessageType + ) { + adapter?.clear() + adapter?.notifyDataSetChanged() + } - if (lookIntoFuture) { - processMessagesFromTheFuture(chatMessageList) - } else { - processMessagesNotFromTheFuture(chatMessageList) + if (lookIntoFuture) { + processMessagesFromTheFuture(chatMessageList) + } else { + processMessagesNotFromTheFuture(chatMessageList) - collapseSystemMessages() - } + collapseSystemMessages() + } - val newXChatLastCommonRead = response.headers()["X-Chat-Last-Common-Read"]?.let { - Integer.parseInt(it) - } + val newXChatLastCommonRead = + response.headers()["X-Chat-Last-Common-Read"]?.let { + Integer.parseInt(it) + } - processCallStartedMessages(chatMessageList) + processCallStartedMessages(chatMessageList) - updateReadStatusOfAllMessages(newXChatLastCommonRead) - adapter?.notifyDataSetChanged() + updateReadStatusOfAllMessages(newXChatLastCommonRead) + adapter?.notifyDataSetChanged() - if (isFirstMessagesProcessing || lookIntoFuture) { - Log.d(TAG, "recursive call to pullChatMessages") - pullChatMessages(true, true, newXChatLastCommonRead) + if (isFirstMessagesProcessing || lookIntoFuture) { + Log.d(TAG, "recursive call to pullChatMessages") + pullChatMessages(true, true, newXChatLastCommonRead) + } } } - } - processExpiredMessages() + processExpiredMessages() - if (isFirstMessagesProcessing) { - cancelNotificationsForCurrentConversation() - isFirstMessagesProcessing = false - binding.progressBar.visibility = View.GONE - binding.messagesListView.visibility = View.VISIBLE + if (isFirstMessagesProcessing) { + cancelNotificationsForCurrentConversation() + isFirstMessagesProcessing = false + binding.progressBar.visibility = View.GONE + binding.messagesListView.visibility = View.VISIBLE - collapseSystemMessages() + collapseSystemMessages() + } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "pullChatMessages - pullChatMessages ERROR", e) - pullChatMessagesPending = false - } + override fun onError(e: Throwable) { + Log.e(TAG, "pullChatMessages - pullChatMessages ERROR", e) + pullChatMessagesPending = false + } - override fun onComplete() { - pullChatMessagesPending = false + override fun onComplete() { + pullChatMessagesPending = false + } } - }) + ) } private fun processCallStartedMessages(chatMessageList: List) { try { - val mostRecentCallSystemMessage = adapter?.items?.first { - it.item is ChatMessage && - (it.item as ChatMessage).systemMessageType in - listOf( - ChatMessage.SystemMessageType.CALL_STARTED, - ChatMessage.SystemMessageType.CALL_JOINED, - ChatMessage.SystemMessageType.CALL_LEFT, - ChatMessage.SystemMessageType.CALL_ENDED, - ChatMessage.SystemMessageType.CALL_TRIED, - ChatMessage.SystemMessageType.CALL_ENDED_EVERYONE, - ChatMessage.SystemMessageType.CALL_MISSED - ) - }?.item + val mostRecentCallSystemMessage = + adapter?.items?.first { + it.item is ChatMessage && + (it.item as ChatMessage).systemMessageType in + listOf( + ChatMessage.SystemMessageType.CALL_STARTED, + ChatMessage.SystemMessageType.CALL_JOINED, + ChatMessage.SystemMessageType.CALL_LEFT, + ChatMessage.SystemMessageType.CALL_ENDED, + ChatMessage.SystemMessageType.CALL_TRIED, + ChatMessage.SystemMessageType.CALL_ENDED_EVERYONE, + ChatMessage.SystemMessageType.CALL_MISSED + ) + }?.item if (mostRecentCallSystemMessage != null) { processMostRecentMessage( @@ -3321,22 +3415,24 @@ class ChatActivity : } } - val lastKnown = if (lookIntoFuture) { - globalLastKnownFutureMessageId - } else { - globalLastKnownPastMessageId - } + val lastKnown = + if (lookIntoFuture) { + globalLastKnownFutureMessageId + } else { + globalLastKnownPastMessageId + } fieldMap["lastKnownMessageId"] = lastKnown xChatLastCommonRead?.let { fieldMap["lastCommonReadId"] = it } - val timeout = if (lookIntoFuture) { - LOOKING_INTO_FUTURE_TIMEOUT - } else { - 0 - } + val timeout = + if (lookIntoFuture) { + LOOKING_INTO_FUTURE_TIMEOUT + } else { + 0 + } fieldMap["timeout"] = timeout fieldMap["limit"] = MESSAGE_PULL_LIMIT @@ -3463,10 +3559,7 @@ class ChatActivity : } } - private fun addMessagesToAdapter( - shouldAddNewMessagesNotice: Boolean, - chatMessageList: List - ) { + private fun addMessagesToAdapter(shouldAddNewMessagesNotice: Boolean, chatMessageList: List) { val isThereANewNotice = shouldAddNewMessagesNotice || adapter?.getMessagePositionByIdInReverse("-1") != -1 for (chatMessage in chatMessageList) { @@ -3539,13 +3632,14 @@ class ChatActivity : private fun processHeaderChatLastGiven(response: Response<*>, isFromTheFuture: Boolean) { val xChatLastGivenHeader: String? = response.headers()["X-Chat-Last-Given"] - val header = if (response.headers().size > 0 && - xChatLastGivenHeader?.isNotEmpty() == true - ) { - xChatLastGivenHeader.toInt() - } else { - return - } + val header = + if (response.headers().size > 0 && + xChatLastGivenHeader?.isNotEmpty() == true + ) { + xChatLastGivenHeader.toInt() + } else { + return + } if (header > 0) { if (isFromTheFuture) { @@ -3719,19 +3813,15 @@ class ChatActivity : chatMessageMap[currentMessage.value.parentMessage!!.id]!!.isDeleted = true } chatMessageIterator.remove() - } - - // delete reactions system messages - else if (isReactionsMessage(currentMessage)) { + } else if (isReactionsMessage(currentMessage)) { + // delete reactions system messages if (!chatMessageMap.containsKey(currentMessage.value.parentMessage!!.id)) { updateAdapterForReaction(currentMessage.value.parentMessage) } chatMessageIterator.remove() - } - - // delete poll system messages - else if (isPollVotedMessage(currentMessage)) { + } else if (isPollVotedMessage(currentMessage)) { + // delete poll system messages chatMessageIterator.remove() } } @@ -3763,8 +3853,9 @@ class ChatActivity : } private fun isInfoMessageAboutDeletion(currentMessage: MutableMap.MutableEntry): Boolean { - return currentMessage.value.parentMessage != null && currentMessage.value.systemMessageType == ChatMessage - .SystemMessageType.MESSAGE_DELETED + return currentMessage.value.parentMessage != null && currentMessage.value.systemMessageType == + ChatMessage + .SystemMessageType.MESSAGE_DELETED } private fun isReactionsMessage(currentMessage: MutableMap.MutableEntry): Boolean { @@ -3961,49 +4052,52 @@ class ChatActivity : ) )?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(t: ChatOverallSingleMessage) { - if (t.ocs!!.meta!!.statusCode == HttpURLConnection.HTTP_ACCEPTED) { - Snackbar.make( - binding.root, - R.string.nc_delete_message_leaked_to_matterbridge, - Snackbar.LENGTH_LONG - ).show() + override fun onNext(t: ChatOverallSingleMessage) { + if (t.ocs!!.meta!!.statusCode == HttpURLConnection.HTTP_ACCEPTED) { + Snackbar.make( + binding.root, + R.string.nc_delete_message_leaked_to_matterbridge, + Snackbar.LENGTH_LONG + ).show() + } } - } - override fun onError(e: Throwable) { - Log.e( - TAG, - "Something went wrong when trying to delete message with id " + - message?.id, - e - ) - Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - } + override fun onError(e: Throwable) { + Log.e( + TAG, + "Something went wrong when trying to delete message with id " + + message?.id, + e + ) + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } } fun replyPrivately(message: IMessage?) { val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) - val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( - apiVersion, - conversationUser?.baseUrl, - "1", - null, - message?.user?.id?.substring(INVITE_LENGTH), - null - ) + val retrofitBucket = + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + conversationUser?.baseUrl, + "1", + null, + message?.user?.id?.substring(INVITE_LENGTH), + null + ) ncApi.createRoom( credentials, retrofitBucket.url, @@ -4011,32 +4105,34 @@ class ChatActivity : ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(roomOverall: RoomOverall) { - val bundle = Bundle() - bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) - bundle.putString(KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId) + override fun onNext(roomOverall: RoomOverall) { + val bundle = Bundle() + bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) + bundle.putString(KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId) - leaveRoom { - val chatIntent = Intent(context, ChatActivity::class.java) - chatIntent.putExtras(bundle) - chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - startActivity(chatIntent) + leaveRoom { + val chatIntent = Intent(context, ChatActivity::class.java) + chatIntent.putExtras(bundle) + chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(chatIntent) + } } - } - override fun onError(e: Throwable) { - Log.e(TAG, e.message, e) - } + override fun onError(e: Throwable) { + Log.e(TAG, e.message, e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } fun forwardMessage(message: IMessage?) { @@ -4052,11 +4148,12 @@ class ChatActivity : fun remindMeLater(message: ChatMessage?) { Log.d(TAG, "remindMeLater called") - val newFragment: DialogFragment = DateTimePickerFragment.newInstance( - roomToken, - message!!.id, - chatViewModel - ) + val newFragment: DialogFragment = + DateTimePickerFragment.newInstance( + roomToken, + message!!.id, + chatViewModel + ) newFragment.show(supportFragmentManager, DateTimePickerFragment.TAG) } @@ -4074,33 +4171,36 @@ class ChatActivity : ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(t: GenericOverall) { - // unused atm - } + override fun onNext(t: GenericOverall) { + // unused atm + } - override fun onError(e: Throwable) { - Log.e(TAG, e.message, e) - } + override fun onError(e: Throwable) { + Log.e(TAG, e.message, e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } } fun copyMessage(message: IMessage?) { val clipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clipData = ClipData.newPlainText( - resources?.getString(R.string.nc_app_product_name), - message?.text - ) + val clipData = + ClipData.newPlainText( + resources?.getString(R.string.nc_app_product_name), + message?.text + ) clipboardManager.setPrimaryClip(clipData) } @@ -4116,18 +4216,20 @@ class ChatActivity : fun share(message: ChatMessage) { val filename = message.selectedIndividualHashMap!!["name"] path = applicationContext.cacheDir.absolutePath + "/" + filename - val shareUri = FileProvider.getUriForFile( - this, - BuildConfig.APPLICATION_ID, - File(path) - ) + val shareUri = + FileProvider.getUriForFile( + this, + BuildConfig.APPLICATION_ID, + File(path) + ) - val shareIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_STREAM, shareUri) - type = Mimetype.IMAGE_PREFIX_GENERIC - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - } + val shareIntent: Intent = + Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, shareUri) + type = Mimetype.IMAGE_PREFIX_GENERIC + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.send_to))) } @@ -4145,9 +4247,10 @@ class ChatActivity : } private fun showSaveToStorageWarning(message: ChatMessage) { - val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance( - message.selectedIndividualHashMap!!["name"]!! - ) + val saveFragment: DialogFragment = + SaveToStorageDialogFragment.newInstance( + message.selectedIndividualHashMap!!["name"]!! + ) saveFragment.show( supportFragmentManager, SaveToStorageDialogFragment.TAG @@ -4210,11 +4313,12 @@ class ChatActivity : chatMessage.imageUrl?.let { previewImageUrl -> quotedMessageImage?.visibility = View.VISIBLE - val px = TypedValue.applyDimension( - TypedValue.COMPLEX_UNIT_DIP, - QUOTED_MESSAGE_IMAGE_MAX_HEIGHT, - resources?.displayMetrics - ) + val px = + TypedValue.applyDimension( + TypedValue.COMPLEX_UNIT_DIP, + QUOTED_MESSAGE_IMAGE_MAX_HEIGHT, + resources?.displayMetrics + ) quotedMessageImage?.maxHeight = px.toInt() val layoutParams = quotedMessageImage?.layoutParams as FlexboxLayout.LayoutParams @@ -4307,15 +4411,17 @@ class ChatActivity : private fun isShowMessageDeletionButton(message: ChatMessage): Boolean { if (conversationUser == null) return false - val isUserAllowedByPrivileges = if (message.actorId == conversationUser!!.userId) { - true - } else { - ConversationUtils.canModerate(currentConversation!!, conversationUser!!) - } + val isUserAllowedByPrivileges = + if (message.actorId == conversationUser!!.userId) { + true + } else { + ConversationUtils.canModerate(currentConversation!!, conversationUser!!) + } - val isOlderThanSixHours = message - .createdAt - .before(Date(System.currentTimeMillis() - AGE_THRESHOLD_FOR_DELETE_MESSAGE)) + val isOlderThanSixHours = + message + .createdAt + .before(Date(System.currentTimeMillis() - AGE_THRESHOLD_FOR_DELETE_MESSAGE)) return when { !isUserAllowedByPrivileges -> false @@ -4400,14 +4506,15 @@ class ChatActivity : apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) } - val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( - apiVersion, - conversationUser?.baseUrl, - "1", - null, - userMentionClickEvent.userId, - null - ) + val retrofitBucket = + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + conversationUser?.baseUrl, + "1", + null, + userMentionClickEvent.userId, + null + ) ncApi.createRoom( credentials, @@ -4416,33 +4523,35 @@ class ChatActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(roomOverall: RoomOverall) { - val bundle = Bundle() - bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) - bundle.putString(KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId) + override fun onNext(roomOverall: RoomOverall) { + val bundle = Bundle() + bundle.putString(KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) + bundle.putString(KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId) - leaveRoom { - val chatIntent = Intent(context, ChatActivity::class.java) - chatIntent.putExtras(bundle) - chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - startActivity(chatIntent) + leaveRoom { + val chatIntent = Intent(context, ChatActivity::class.java) + chatIntent.putExtras(bundle) + chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(chatIntent) + } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "error after clicking on user mention chip", e) - Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - } + override fun onError(e: Throwable) { + Log.e(TAG, "error after clicking on user mention chip", e) + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } } @@ -4460,20 +4569,22 @@ class ChatActivity : } else { Intent(MediaStore.ACTION_VIDEO_CAPTURE).also { takeVideoIntent -> takeVideoIntent.resolveActivity(packageManager)?.also { - val videoFile: File? = try { - val outputDir = context.cacheDir - val dateFormat = SimpleDateFormat(FILE_DATE_PATTERN, Locale.ROOT) - val date = dateFormat.format(Date()) - val videoName = String.format( - context.resources.getString(R.string.nc_video_filename), - date - ) - File("$outputDir/$videoName$VIDEO_SUFFIX") - } catch (e: IOException) { - Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - Log.e(TAG, "error while creating video file", e) - null - } + val videoFile: File? = + try { + val outputDir = context.cacheDir + val dateFormat = SimpleDateFormat(FILE_DATE_PATTERN, Locale.ROOT) + val date = dateFormat.format(Date()) + val videoName = + String.format( + context.resources.getString(R.string.nc_video_filename), + date + ) + File("$outputDir/$videoName$VIDEO_SUFFIX") + } catch (e: IOException) { + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + Log.e(TAG, "error while creating video file", e) + null + } videoFile?.also { videoURI = FileProvider.getUriForFile(context, context.packageName, it) @@ -4486,9 +4597,10 @@ class ChatActivity : } fun createPoll() { - val pollVoteDialog = PollCreateDialogFragment.newInstance( - roomToken - ) + val pollVoteDialog = + PollCreateDialogFragment.newInstance( + roomToken + ) pollVoteDialog.show(supportFragmentManager, TAG) } diff --git a/app/src/main/java/com/nextcloud/talk/chat/TypingParticipant.kt b/app/src/main/java/com/nextcloud/talk/chat/TypingParticipant.kt index a29a13fdc75..87d73c13559 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/TypingParticipant.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/TypingParticipant.kt @@ -30,18 +30,19 @@ class TypingParticipant(val userId: String, val name: String, val funToCallWhenT } private fun startTimer() { - timer = object : CountDownTimer( - TYPING_DURATION_TO_HIDE_TYPING_MESSAGE, - TYPING_DURATION_TO_HIDE_TYPING_MESSAGE - ) { - override fun onTick(millisUntilFinished: Long) { - // unused - } - - override fun onFinish() { - funToCallWhenTimeIsUp(userId) - } - }.start() + timer = + object : CountDownTimer( + TYPING_DURATION_TO_HIDE_TYPING_MESSAGE, + TYPING_DURATION_TO_HIDE_TYPING_MESSAGE + ) { + override fun onTick(millisUntilFinished: Long) { + // unused + } + + override fun onFinish() { + funToCallWhenTimeIsUp(userId) + } + }.start() } fun restartTimer() { diff --git a/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt b/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt index 7c85caa1581..6653c16650b 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepository.kt @@ -28,8 +28,12 @@ import io.reactivex.Observable interface ChatRepository { fun getRoom(user: User, roomToken: String): Observable + fun joinRoom(user: User, roomToken: String, roomPassword: String): Observable + fun setReminder(user: User, roomToken: String, messageId: String, timeStamp: Int): Observable + fun getReminder(user: User, roomToken: String, messageId: String): Observable + fun deleteReminder(user: User, roomToken: String, messageId: String): Observable } diff --git a/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepositoryImpl.kt index 81327cf742f..488aaece3b0 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/data/ChatRepositoryImpl.kt @@ -29,10 +29,7 @@ import com.nextcloud.talk.utils.ApiUtils import io.reactivex.Observable class ChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository { - override fun getRoom( - user: User, - roomToken: String - ): Observable { + override fun getRoom(user: User, roomToken: String): Observable { val credentials: String = ApiUtils.getCredentials(user.username, user.token) val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1)) @@ -42,11 +39,7 @@ class ChatRepositoryImpl(private val ncApi: NcApi) : ChatRepository { ).map { ConversationModel.mapToConversationModel(it.ocs?.data!!) } } - override fun joinRoom( - user: User, - roomToken: String, - roomPassword: String - ): Observable { + override fun joinRoom(user: User, roomToken: String, roomPassword: String): Observable { val credentials: String = ApiUtils.getCredentials(user.username, user.token) val apiVersion = ApiUtils.getConversationApiVersion(user, intArrayOf(ApiUtils.APIv4, 1)) diff --git a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt index a6d9db80fe9..b4e20ac4c71 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/viewmodels/ChatViewModel.kt @@ -35,14 +35,18 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class ChatViewModel @Inject constructor(private val repository: ChatRepository) : +class ChatViewModel +@Inject +constructor(private val repository: ChatRepository) : ViewModel() { - sealed interface ViewState object GetRoomStartState : ViewState + object GetRoomErrorState : ViewState + object GetReminderStartState : ViewState + open class GetReminderExistState(val reminder: Reminder) : ViewState private val _getReminderExistState: MutableLiveData = MutableLiveData(GetReminderStartState) @@ -56,7 +60,9 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository) get() = _getRoomViewState object JoinRoomStartState : ViewState + object JoinRoomErrorState : ViewState + open class JoinRoomSuccessState(val conversationModel: ConversationModel) : ViewState private val _joinRoomViewState: MutableLiveData = MutableLiveData(JoinRoomStartState) @@ -98,23 +104,25 @@ class ChatViewModel @Inject constructor(private val repository: ChatRepository) repository.deleteReminder(user, roomToken, messageId) .subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - _getReminderExistState.value = GetReminderStartState - } - - override fun onError(e: Throwable) { - Log.d(TAG, "Error when deleting reminder $e") - } - - override fun onComplete() { - // unused atm + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + _getReminderExistState.value = GetReminderStartState + } + + override fun onError(e: Throwable) { + Log.d(TAG, "Error when deleting reminder $e") + } + + override fun onComplete() { + // unused atm + } } - }) + ) } inner class GetRoomObserver : Observer { diff --git a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.kt b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.kt index 789987df733..9d921fa5a07 100644 --- a/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.kt +++ b/app/src/main/java/com/nextcloud/talk/components/filebrowser/models/BrowserFile.kt @@ -49,7 +49,6 @@ data class BrowserFile( var modifiedTimestamp: Long = 0, var size: Long = 0, var isFile: Boolean = false, - // Used for remote files var remoteId: String? = null, var hasPreview: Boolean = false, diff --git a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt index c32f748ff6c..e901d92114a 100644 --- a/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/contacts/ContactsActivity.kt @@ -193,17 +193,20 @@ class ContactsActivity : supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setIcon(ColorDrawable(resources!!.getColor(android.R.color.transparent, null))) - supportActionBar?.title = when { - isAddingParticipantsView -> { - resources!!.getString(R.string.nc_add_participants) - } - isNewConversationView -> { - resources!!.getString(R.string.nc_select_participants) - } - else -> { - resources!!.getString(R.string.nc_app_product_name) + supportActionBar?.title = + when { + isAddingParticipantsView -> { + resources!!.getString(R.string.nc_add_participants) + } + + isNewConversationView -> { + resources!!.getString(R.string.nc_select_participants) + } + + else -> { + resources!!.getString(R.string.nc_app_product_name) + } } - } viewThemeUtils.material.themeToolbar(binding.contactsToolbar) } @@ -246,10 +249,12 @@ class ContactsActivity : finish() true } + R.id.contacts_selection_done -> { selectionDone() true } + else -> { super.onOptionsItemSelected(item) } @@ -279,11 +284,13 @@ class ContactsActivity : roomType = "2" userId = selectedGroupIds.iterator().next() } + selectedCircleIds.size == 1 -> { roomType = "2" sourceType = "circles" userId = selectedCircleIds.iterator().next() } + else -> { userId = selectedUserIds.iterator().next() } @@ -292,23 +299,25 @@ class ContactsActivity : // if there are more participants to add, ask for roomName and add them one after another } else { - val roomType: Conversation.ConversationType = if (isPublicCall) { - Conversation.ConversationType.ROOM_PUBLIC_CALL - } else { - Conversation.ConversationType.ROOM_GROUP_CALL - } + val roomType: Conversation.ConversationType = + if (isPublicCall) { + Conversation.ConversationType.ROOM_PUBLIC_CALL + } else { + Conversation.ConversationType.ROOM_GROUP_CALL + } val userIdsArray = ArrayList(selectedUserIds) val groupIdsArray = ArrayList(selectedGroupIds) val emailsArray = ArrayList(selectedEmails) val circleIdsArray = ArrayList(selectedCircleIds) - val createConversationDialog = CreateConversationDialogFragment.newInstance( - userIdsArray, - groupIdsArray, - emailsArray, - circleIdsArray, - Parcels.wrap(roomType) - ) + val createConversationDialog = + CreateConversationDialogFragment.newInstance( + userIdsArray, + groupIdsArray, + emailsArray, + circleIdsArray, + Parcels.wrap(roomType) + ) createConversationDialog.show( supportFragmentManager, TAG @@ -319,14 +328,15 @@ class ContactsActivity : private fun createRoom(roomType: String, sourceType: String?, userId: String) { val apiVersion: Int = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1)) - val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( - apiVersion, - currentUser!!.baseUrl, - roomType, - sourceType, - userId, - null - ) + val retrofitBucket: RetrofitBucket = + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + currentUser!!.baseUrl, + roomType, + sourceType, + userId, + null + ) ncApi.createRoom( credentials, retrofitBucket.url, @@ -334,30 +344,32 @@ class ContactsActivity : ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(roomOverall: RoomOverall) { - val bundle = Bundle() - bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) - bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId) + override fun onNext(roomOverall: RoomOverall) { + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) + bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId) - val chatIntent = Intent(context, ChatActivity::class.java) - chatIntent.putExtras(bundle) - chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - startActivity(chatIntent) - } + val chatIntent = Intent(context, ChatActivity::class.java) + chatIntent.putExtras(bundle) + chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(chatIntent) + } - override fun onError(e: Throwable) { - // unused atm - } + override fun onError(e: Throwable) { + // unused atm + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun addParticipantsToConversation() { @@ -372,9 +384,10 @@ class ContactsActivity : data.putStringArray(BundleKeys.KEY_SELECTED_GROUPS, groupIdsArray) data.putStringArray(BundleKeys.KEY_SELECTED_EMAILS, emailsArray) data.putStringArray(BundleKeys.KEY_SELECTED_CIRCLES, circleIdsArray) - val addParticipantsToConversationWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder( - AddParticipantsToConversation::class.java - ).setInputData(data.build()).build() + val addParticipantsToConversationWorker: OneTimeWorkRequest = + OneTimeWorkRequest.Builder( + AddParticipantsToConversation::class.java + ).setInputData(data.build()).build() WorkManager.getInstance().enqueue(addParticipantsToConversationWorker) WorkManager.getInstance(context).getWorkInfoByIdLiveData(addParticipantsToConversationWorker.id) @@ -470,44 +483,47 @@ class ContactsActivity : .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(RETRIES) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - contactsQueryDisposable = d - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + contactsQueryDisposable = d + } - override fun onNext(responseBody: ResponseBody) { - val newUserItemList = processAutocompleteUserList(responseBody) + override fun onNext(responseBody: ResponseBody) { + val newUserItemList = processAutocompleteUserList(responseBody) - userHeaderItems = HashMap() - contactItems!!.addAll(newUserItemList) + userHeaderItems = HashMap() + contactItems!!.addAll(newUserItemList) - sortUserItems(newUserItemList) + sortUserItems(newUserItemList) - if (newUserItemList.size > 0) { - adapter?.updateDataSet(newUserItemList as List?) - } else { - adapter?.filterItems() + if (newUserItemList.size > 0) { + adapter?.updateDataSet(newUserItemList as List?) + } else { + adapter?.filterItems() + } } - } - override fun onError(e: Throwable) { - dispose(contactsQueryDisposable) - } + override fun onError(e: Throwable) { + dispose(contactsQueryDisposable) + } - override fun onComplete() { - dispose(contactsQueryDisposable) - alreadyFetching = false - disengageProgressBar() + override fun onComplete() { + dispose(contactsQueryDisposable) + alreadyFetching = false + disengageProgressBar() + } } - }) + ) } private fun processAutocompleteUserList(responseBody: ResponseBody): MutableList> { try { - val autocompleteOverall: AutocompleteOverall = LoganSquare.parse( - responseBody.string(), - AutocompleteOverall::class.java - ) + val autocompleteOverall: AutocompleteOverall = + LoganSquare.parse( + responseBody.string(), + AutocompleteOverall::class.java + ) val autocompleteUsersList: ArrayList = ArrayList() autocompleteUsersList.addAll(autocompleteOverall.ocs!!.data!!) return processAutocompleteUserList(autocompleteUsersList) @@ -536,12 +552,13 @@ class ContactsActivity : genericTextHeaderItem = GenericTextHeaderItem(headerTitle, viewThemeUtils) userHeaderItems.put(headerTitle, genericTextHeaderItem) } - val newContactItem = ContactItem( - participant, - currentUser, - userHeaderItems[headerTitle], - viewThemeUtils - ) + val newContactItem = + ContactItem( + participant, + currentUser, + userHeaderItems[headerTitle], + viewThemeUtils + ) if (!contactItems!!.contains(newContactItem)) { newUserItemList.add(newContactItem) } @@ -555,9 +572,11 @@ class ContactsActivity : participant.calculatedActorType == Participant.ActorType.GROUPS -> { resources!!.getString(R.string.nc_groups) } + participant.calculatedActorType == Participant.ActorType.CIRCLES -> { resources!!.getString(R.string.nc_circles) } + else -> { participant.displayName!!.substring(0, 1).uppercase(Locale.getDefault()) } @@ -580,16 +599,18 @@ class ContactsActivity : @Suppress("LongMethod") private fun sortUserItems(newUserItemList: MutableList>) { newUserItemList.sortWith sort@{ o1: AbstractFlexibleItem<*>, o2: AbstractFlexibleItem<*> -> - val firstName: String = if (o1 is ContactItem) { - o1.model.displayName!! - } else { - (o1 as GenericTextHeaderItem).model - } - val secondName: String = if (o2 is ContactItem) { - o2.model.displayName!! - } else { - (o2 as GenericTextHeaderItem).model - } + val firstName: String = + if (o1 is ContactItem) { + o1.model.displayName!! + } else { + (o1 as GenericTextHeaderItem).model + } + val secondName: String = + if (o2 is ContactItem) { + o2.model.displayName!! + } else { + (o2 as GenericTextHeaderItem).model + } if (o1 is ContactItem && o2 is ContactItem) { val firstSource: String = o1.model.source!! val secondSource: String = o2.model.source!! @@ -625,16 +646,18 @@ class ContactsActivity : } contactItems?.sortWith sort@{ o1: AbstractFlexibleItem<*>, o2: AbstractFlexibleItem<*> -> - val firstName: String = if (o1 is ContactItem) { - o1.model.displayName!! - } else { - (o1 as GenericTextHeaderItem).model - } - val secondName: String = if (o2 is ContactItem) { - o2.model.displayName!! - } else { - (o2 as GenericTextHeaderItem).model - } + val firstName: String = + if (o1 is ContactItem) { + o1.model.displayName!! + } else { + (o1 as GenericTextHeaderItem).model + } + val secondName: String = + if (o2 is ContactItem) { + o2.model.displayName!! + } else { + (o2 as GenericTextHeaderItem).model + } if (o1 is ContactItem && o2 is ContactItem) { if ("groups" == o1.model.source && "groups" == o2.model.source @@ -772,41 +795,44 @@ class ContactsActivity : roomType = "2" } val apiVersion: Int = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, 1)) - val retrofitBucket: RetrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( - apiVersion, - currentUser!!.baseUrl, - roomType, - null, - contactItem.model.calculatedActorId, - null - ) + val retrofitBucket: RetrofitBucket = + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + currentUser!!.baseUrl, + roomType, + null, + contactItem.model.calculatedActorId, + null + ) ncApi.createRoom(credentials, retrofitBucket.url, retrofitBucket.queryMap) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(roomOverall: RoomOverall) { - val bundle = Bundle() - bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) - bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId) + override fun onNext(roomOverall: RoomOverall) { + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) + bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId) - val chatIntent = Intent(context, ChatActivity::class.java) - chatIntent.putExtras(bundle) - chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - startActivity(chatIntent) - } + val chatIntent = Intent(context, ChatActivity::class.java) + chatIntent.putExtras(bundle) + chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + startActivity(chatIntent) + } - override fun onError(e: Throwable) { - // unused atm - } + override fun onError(e: Throwable) { + // unused atm + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun updateSelectionLists(participant: Participant) { diff --git a/app/src/main/java/com/nextcloud/talk/conversation/CreateConversationDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/conversation/CreateConversationDialogFragment.kt index c46ab719759..dc21bfe83dc 100644 --- a/app/src/main/java/com/nextcloud/talk/conversation/CreateConversationDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/conversation/CreateConversationDialogFragment.kt @@ -113,13 +113,14 @@ class CreateConversationDialogFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { binding = DialogCreateConversationBinding.inflate(LayoutInflater.from(context)) - val dialogBuilder = MaterialAlertDialogBuilder(binding.root.context) - .setTitle(resources.getString(R.string.create_conversation)) - // listener is null for now to avoid closing after button was clicked. - // listener is set later in onStart - .setPositiveButton(R.string.nc_common_create, null) - .setNegativeButton(R.string.nc_common_dismiss, null) - .setView(binding.root) + val dialogBuilder = + MaterialAlertDialogBuilder(binding.root.context) + .setTitle(resources.getString(R.string.create_conversation)) + // listener is null for now to avoid closing after button was clicked. + // listener is set later in onStart + .setPositiveButton(R.string.nc_common_create, null) + .setNegativeButton(R.string.nc_common_dismiss, null) + .setView(binding.root) viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.root.context, dialogBuilder) return dialogBuilder.create() @@ -161,54 +162,58 @@ class CreateConversationDialogFragment : DialogFragment() { } private fun setupEmojiPopup() { - emojiPopup = binding.let { - EmojiPopup( - rootView = requireView(), - editText = it.textEdit, - onEmojiPopupShownListener = { - viewThemeUtils.platform.colorImageView(it.smileyButton, ColorRole.PRIMARY) - }, - onEmojiPopupDismissListener = { - it.smileyButton.imageTintList = ColorStateList.valueOf( - ResourcesCompat.getColor( - resources, - R.color.medium_emphasis_text, - context?.theme - ) - ) - }, - onEmojiClickListener = { - binding.textEdit.editableText?.append(" ") - } - ) - } + emojiPopup = + binding.let { + EmojiPopup( + rootView = requireView(), + editText = it.textEdit, + onEmojiPopupShownListener = { + viewThemeUtils.platform.colorImageView(it.smileyButton, ColorRole.PRIMARY) + }, + onEmojiPopupDismissListener = { + it.smileyButton.imageTintList = + ColorStateList.valueOf( + ResourcesCompat.getColor( + resources, + R.color.medium_emphasis_text, + context?.theme + ) + ) + }, + onEmojiClickListener = { + binding.textEdit.editableText?.append(" ") + } + ) + } } private fun setupListeners() { binding.smileyButton.setOnClickListener { emojiPopup?.toggle() } - binding.textEdit.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - // unused atm - } + binding.textEdit.addTextChangedListener( + object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // unused atm + } - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - // unused atm - } + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + // unused atm + } - override fun afterTextChanged(s: Editable) { - val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE) + override fun afterTextChanged(s: Editable) { + val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE) - if (!TextUtils.isEmpty(s)) { - if (!positiveButton.isEnabled) { - positiveButton.isEnabled = true - } - } else { - if (positiveButton.isEnabled) { - positiveButton.isEnabled = false + if (!TextUtils.isEmpty(s)) { + if (!positiveButton.isEnabled) { + positiveButton.isEnabled = true + } + } else { + if (positiveButton.isEnabled) { + positiveButton.isEnabled = false + } } } } - }) + ) } private fun setupStateObserver() { @@ -221,6 +226,7 @@ class CreateConversationDialogFragment : DialogFragment() { Log.e(TAG, "Failed to create conversation") showError() } + else -> {} } } @@ -235,11 +241,12 @@ class CreateConversationDialogFragment : DialogFragment() { data.putStringArray(BundleKeys.KEY_SELECTED_EMAILS, emailsToInvite.toTypedArray()) data.putStringArray(BundleKeys.KEY_SELECTED_CIRCLES, circlesToInvite.toTypedArray()) - val addParticipantsToConversationWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder( - AddParticipantsToConversation::class.java - ) - .setInputData(data.build()) - .build() + val addParticipantsToConversationWorker: OneTimeWorkRequest = + OneTimeWorkRequest.Builder( + AddParticipantsToConversation::class.java + ) + .setInputData(data.build()) + .build() WorkManager.getInstance(requireContext()).enqueue(addParticipantsToConversationWorker) diff --git a/app/src/main/java/com/nextcloud/talk/conversation/RenameConversationDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/conversation/RenameConversationDialogFragment.kt index 9fa6d5ebf83..440612e5fac 100644 --- a/app/src/main/java/com/nextcloud/talk/conversation/RenameConversationDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/conversation/RenameConversationDialogFragment.kt @@ -83,13 +83,14 @@ class RenameConversationDialogFragment : DialogFragment() { override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { binding = DialogRenameConversationBinding.inflate(LayoutInflater.from(context)) - val dialogBuilder = MaterialAlertDialogBuilder(binding.root.context) - .setTitle(resources.getString(R.string.nc_rename)) - // listener is null for now to avoid closing after button was clicked. - // listener is set later in onStart - .setPositiveButton(R.string.nc_rename_confirm, null) - .setNegativeButton(R.string.nc_common_dismiss, null) - .setView(binding.root) + val dialogBuilder = + MaterialAlertDialogBuilder(binding.root.context) + .setTitle(resources.getString(R.string.nc_rename)) + // listener is null for now to avoid closing after button was clicked. + // listener is set later in onStart + .setPositiveButton(R.string.nc_rename_confirm, null) + .setNegativeButton(R.string.nc_common_dismiss, null) + .setView(binding.root) viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.root.context, dialogBuilder) return dialogBuilder.create() @@ -129,56 +130,60 @@ class RenameConversationDialogFragment : DialogFragment() { } private fun setupEmojiPopup() { - emojiPopup = binding.let { - EmojiPopup( - rootView = requireView(), - editText = it.textEdit, - onEmojiPopupShownListener = { - viewThemeUtils.platform.colorImageView(it.smileyButton, ColorRole.PRIMARY) - }, - onEmojiPopupDismissListener = { - it.smileyButton.imageTintList = ColorStateList.valueOf( - ResourcesCompat.getColor( - resources, - R.color.medium_emphasis_text, - context?.theme - ) - ) - }, - onEmojiClickListener = { - binding.textEdit.editableText?.append(" ") - } - ) - } + emojiPopup = + binding.let { + EmojiPopup( + rootView = requireView(), + editText = it.textEdit, + onEmojiPopupShownListener = { + viewThemeUtils.platform.colorImageView(it.smileyButton, ColorRole.PRIMARY) + }, + onEmojiPopupDismissListener = { + it.smileyButton.imageTintList = + ColorStateList.valueOf( + ResourcesCompat.getColor( + resources, + R.color.medium_emphasis_text, + context?.theme + ) + ) + }, + onEmojiClickListener = { + binding.textEdit.editableText?.append(" ") + } + ) + } } private fun setupListeners() { binding.smileyButton.setOnClickListener { emojiPopup?.toggle() } - binding.textEdit.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - // unused atm - } - - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - // unused atm - } + binding.textEdit.addTextChangedListener( + object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // unused atm + } - override fun afterTextChanged(s: Editable) { - val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE) + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + // unused atm + } - if (!TextUtils.isEmpty(s)) { - if (initialName == s.toString()) { - positiveButton.isEnabled = false - } else if (!positiveButton.isEnabled) { - positiveButton.isEnabled = true - } - } else { - if (positiveButton.isEnabled) { - positiveButton.isEnabled = false + override fun afterTextChanged(s: Editable) { + val positiveButton = (dialog as AlertDialog).getButton(AlertDialog.BUTTON_POSITIVE) + + if (!TextUtils.isEmpty(s)) { + if (initialName == s.toString()) { + positiveButton.isEnabled = false + } else if (!positiveButton.isEnabled) { + positiveButton.isEnabled = true + } + } else { + if (positiveButton.isEnabled) { + positiveButton.isEnabled = false + } } } } - }) + ) } private fun setupStateObserver() { diff --git a/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepository.kt b/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepository.kt index 873dcf4ccf5..39d2f1a846c 100644 --- a/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepository.kt @@ -26,13 +26,7 @@ import com.nextcloud.talk.models.json.generic.GenericOverall import io.reactivex.Observable interface ConversationRepository { - fun renameConversation( - roomToken: String, - roomNameNew: String - ): Observable + fun renameConversation(roomToken: String, roomNameNew: String): Observable - fun createConversation( - roomName: String, - conversationType: Conversation.ConversationType? - ): Observable + fun createConversation(roomName: String, conversationType: Conversation.ConversationType?): Observable } diff --git a/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepositoryImpl.kt index 18f597b3098..ca9e0856139 100644 --- a/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversation/repository/ConversationRepositoryImpl.kt @@ -37,10 +37,7 @@ class ConversationRepositoryImpl(private val ncApi: NcApi, currentUserProvider: val currentUser: User = currentUserProvider.currentUser.blockingGet() val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) - override fun renameConversation( - roomToken: String, - roomNameNew: String - ): Observable { + override fun renameConversation(roomToken: String, roomNameNew: String): Observable { val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) return ncApi.renameRoom( @@ -63,25 +60,26 @@ class ConversationRepositoryImpl(private val ncApi: NcApi, currentUserProvider: ): Observable { val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv1)) - val retrofitBucket: RetrofitBucket = if (conversationType == Conversation.ConversationType.ROOM_PUBLIC_CALL) { - ApiUtils.getRetrofitBucketForCreateRoom( - apiVersion, - currentUser.baseUrl, - ROOM_TYPE_PUBLIC, - null, - null, - roomName - ) - } else { - ApiUtils.getRetrofitBucketForCreateRoom( - apiVersion, - currentUser.baseUrl, - ROOM_TYPE_GROUP, - null, - null, - roomName - ) - } + val retrofitBucket: RetrofitBucket = + if (conversationType == Conversation.ConversationType.ROOM_PUBLIC_CALL) { + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + currentUser.baseUrl, + ROOM_TYPE_PUBLIC, + null, + null, + roomName + ) + } else { + ApiUtils.getRetrofitBucketForCreateRoom( + apiVersion, + currentUser.baseUrl, + ROOM_TYPE_GROUP, + null, + null, + roomName + ) + } return ncApi.createRoom(credentials, retrofitBucket.url, retrofitBucket.queryMap) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) diff --git a/app/src/main/java/com/nextcloud/talk/conversation/viewmodel/ConversationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversation/viewmodel/ConversationViewModel.kt index a40e3c75ec4..7e3217d3115 100644 --- a/app/src/main/java/com/nextcloud/talk/conversation/viewmodel/ConversationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversation/viewmodel/ConversationViewModel.kt @@ -32,17 +32,23 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class ConversationViewModel @Inject constructor(private val repository: ConversationRepository) : ViewModel() { +class ConversationViewModel +@Inject +constructor(private val repository: ConversationRepository) : ViewModel() { sealed class ViewState + object InitialState : ViewState() object CreatingState : ViewState() + class CreatingSuccessState(val roomToken: String) : ViewState() + object CreatingFailedState : ViewState() - private val _viewState: MutableLiveData = MutableLiveData( - InitialState - ) + private val _viewState: MutableLiveData = + MutableLiveData( + InitialState + ) val viewState: LiveData get() = _viewState @@ -53,10 +59,7 @@ class ConversationViewModel @Inject constructor(private val repository: Conversa disposable?.dispose() } - fun createConversation( - roomName: String, - conversationType: Conversation.ConversationType? - ) { + fun createConversation(roomName: String, conversationType: Conversation.ConversationType?) { _viewState.value = CreatingState repository.createConversation( diff --git a/app/src/main/java/com/nextcloud/talk/conversation/viewmodel/RenameConversationViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversation/viewmodel/RenameConversationViewModel.kt index 6bd4a454358..03ea68aef6e 100644 --- a/app/src/main/java/com/nextcloud/talk/conversation/viewmodel/RenameConversationViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversation/viewmodel/RenameConversationViewModel.kt @@ -32,16 +32,23 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class RenameConversationViewModel @Inject constructor(private val repository: ConversationRepository) : ViewModel() { +class RenameConversationViewModel +@Inject +constructor(private val repository: ConversationRepository) : ViewModel() { sealed class ViewState + object InitialState : ViewState() + object RenamingState : ViewState() + object RenamingSuccessState : ViewState() + object RenamingFailedState : ViewState() - private val _viewState: MutableLiveData = MutableLiveData( - InitialState - ) + private val _viewState: MutableLiveData = + MutableLiveData( + InitialState + ) val viewState: LiveData get() = _viewState @@ -58,7 +65,6 @@ class RenameConversationViewModel @Inject constructor(private val repository: Co } inner class RenameConversationObserver : Observer { - lateinit var genericOverall: GenericOverall override fun onSubscribe(d: Disposable) = Unit diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt index e04bd2ac6f6..cb1c6600fff 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/ConversationInfoActivity.kt @@ -200,11 +200,12 @@ class ConversationInfoActivity : supportActionBar?.setDisplayHomeAsUpEnabled(true) supportActionBar?.setDisplayShowHomeEnabled(true) supportActionBar?.setIcon(ColorDrawable(resources!!.getColor(android.R.color.transparent, null))) - supportActionBar?.title = if (hasAvatarSpacing) { - " " + resources!!.getString(R.string.nc_conversation_menu_conversation_info) - } else { - resources!!.getString(R.string.nc_conversation_menu_conversation_info) - } + supportActionBar?.title = + if (hasAvatarSpacing) { + " " + resources!!.getString(R.string.nc_conversation_menu_conversation_info) + } else { + resources!!.getString(R.string.nc_conversation_menu_conversation_info) + } viewThemeUtils.material.themeToolbar(binding.conversationInfoToolbar) } @@ -336,11 +337,12 @@ class ConversationInfoActivity : conversation!!.lobbyTimer = 0 } - conversation!!.lobbyState = if (isChecked) { - Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY - } else { - Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS - } + conversation!!.lobbyState = + if (isChecked) { + Conversation.LobbyState.LOBBY_STATE_MODERATORS_ONLY + } else { + Conversation.LobbyState.LOBBY_STATE_ALL_PARTICIPANTS + } if ( conversation!!.lobbyTimer != null && @@ -364,11 +366,12 @@ class ConversationInfoActivity : } private fun submitLobbyChanges() { - val state = if (binding.webinarInfoView.lobbySwitch.isChecked) { - 1 - } else { - 0 - } + val state = + if (binding.webinarInfoView.lobbySwitch.isChecked) { + 1 + } else { + 0 + } val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) @@ -380,24 +383,26 @@ class ConversationInfoActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onComplete() { - // unused atm - } + ?.subscribe( + object : Observer { + override fun onComplete() { + // unused atm + } - override fun onSubscribe(d: Disposable) { - // unused atm - } + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(t: GenericOverall) { - // unused atm - } + override fun onNext(t: GenericOverall) { + // unused atm + } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to setLobbyForConversation", e) - Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to setLobbyForConversation", e) + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + } } - }) + ) } @Subscribe(threadMode = ThreadMode.MAIN) @@ -407,21 +412,22 @@ class ConversationInfoActivity : private fun showDeleteConversationDialog() { binding.conversationInfoName.let { - val dialogBuilder = MaterialAlertDialogBuilder(it.context) - .setIcon( - viewThemeUtils.dialog.colorMaterialAlertDialogIcon( - context, - R.drawable.ic_delete_black_24dp + val dialogBuilder = + MaterialAlertDialogBuilder(it.context) + .setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_delete_black_24dp + ) ) - ) - .setTitle(R.string.nc_delete_call) - .setMessage(R.string.nc_delete_conversation_more) - .setPositiveButton(R.string.nc_delete) { _, _ -> - deleteConversation() - } - .setNegativeButton(R.string.nc_cancel) { _, _ -> - // unused atm - } + .setTitle(R.string.nc_delete_call) + .setMessage(R.string.nc_delete_conversation_more) + .setPositiveButton(R.string.nc_delete) { _, _ -> + deleteConversation() + } + .setNegativeButton(R.string.nc_cancel) { _, _ -> + // unused atm + } viewThemeUtils.dialog .colorMaterialAlertDialogBackground(it.context, dialogBuilder) @@ -503,24 +509,26 @@ class ConversationInfoActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - participantsDisposable = d - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + participantsDisposable = d + } - @Suppress("Detekt.TooGenericExceptionCaught") - override fun onNext(participantsOverall: ParticipantsOverall) { - handleParticipants(participantsOverall.ocs!!.data!!) - } + @Suppress("Detekt.TooGenericExceptionCaught") + override fun onNext(participantsOverall: ParticipantsOverall) { + handleParticipants(participantsOverall.ocs!!.data!!) + } - override fun onError(e: Throwable) { - // unused atm - } + override fun onError(e: Throwable) { + // unused atm + } - override fun onComplete() { - participantsDisposable!!.dispose() + override fun onComplete() { + participantsDisposable!!.dispose() + } } - }) + ) } private fun addParticipants() { @@ -559,21 +567,22 @@ class ConversationInfoActivity : private fun showClearHistoryDialog() { binding.conversationInfoName.context.let { - val dialogBuilder = MaterialAlertDialogBuilder(it) - .setIcon( - viewThemeUtils.dialog.colorMaterialAlertDialogIcon( - context, - R.drawable.ic_delete_black_24dp + val dialogBuilder = + MaterialAlertDialogBuilder(it) + .setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_delete_black_24dp + ) ) - ) - .setTitle(R.string.nc_clear_history) - .setMessage(R.string.nc_clear_history_warning) - .setPositiveButton(R.string.nc_delete_all) { _, _ -> - clearHistory() - } - .setNegativeButton(R.string.nc_cancel) { _, _ -> - // unused atm - } + .setTitle(R.string.nc_clear_history) + .setMessage(R.string.nc_clear_history_warning) + .setPositiveButton(R.string.nc_delete_all) { _, _ -> + clearHistory() + } + .setNegativeButton(R.string.nc_cancel) { _, _ -> + // unused atm + } viewThemeUtils.dialog .colorMaterialAlertDialogBackground(it, dialogBuilder) @@ -594,28 +603,30 @@ class ConversationInfoActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - Snackbar.make( - binding.root, - context.getString(R.string.nc_clear_history_success), - Snackbar.LENGTH_LONG - ).show() - } + override fun onNext(genericOverall: GenericOverall) { + Snackbar.make( + binding.root, + context.getString(R.string.nc_clear_history_success), + Snackbar.LENGTH_LONG + ).show() + } - override fun onError(e: Throwable) { - Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - Log.e(TAG, "failed to clear chat history", e) - } + override fun onError(e: Throwable) { + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + Log.e(TAG, "failed to clear chat history", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun deleteConversation() { @@ -639,115 +650,117 @@ class ConversationInfoActivity : ncApi.getRoom(credentials, ApiUtils.getUrlForRoom(apiVersion, conversationUser.baseUrl, conversationToken)) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - roomDisposable = d - } - - @Suppress("Detekt.TooGenericExceptionCaught") - override fun onNext(roomOverall: RoomOverall) { - conversation = roomOverall.ocs!!.data - - val conversationCopy = conversation - - if (conversationCopy!!.canModerate(conversationUser)) { - binding.addParticipantsAction.visibility = VISIBLE - if (CapabilitiesUtilNew.hasSpreedFeatureCapability( - conversationUser, - "clear-history" - ) - ) { - binding.clearConversationHistory.visibility = VISIBLE - } else { - binding.clearConversationHistory.visibility = GONE - } - showOptionsMenu() - } else { - binding.addParticipantsAction.visibility = GONE - - if (ConversationUtils.isNoteToSelfConversation( - ConversationModel.mapToConversationModel(conversation!!) - ) - ) { - binding.notificationSettingsView.notificationSettings.visibility = VISIBLE - } else { - binding.clearConversationHistory.visibility = GONE - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + roomDisposable = d } - if (!isDestroyed) { - binding.dangerZoneOptions.visibility = VISIBLE - - setupWebinaryView() - - if (!conversation!!.canLeave()) { - binding.leaveConversationAction.visibility = GONE + @Suppress("Detekt.TooGenericExceptionCaught") + override fun onNext(roomOverall: RoomOverall) { + conversation = roomOverall.ocs!!.data + + val conversationCopy = conversation + + if (conversationCopy!!.canModerate(conversationUser)) { + binding.addParticipantsAction.visibility = VISIBLE + if (CapabilitiesUtilNew.hasSpreedFeatureCapability( + conversationUser, + "clear-history" + ) + ) { + binding.clearConversationHistory.visibility = VISIBLE + } else { + binding.clearConversationHistory.visibility = GONE + } + showOptionsMenu() } else { - binding.leaveConversationAction.visibility = VISIBLE + binding.addParticipantsAction.visibility = GONE + + if (ConversationUtils.isNoteToSelfConversation( + ConversationModel.mapToConversationModel(conversation!!) + ) + ) { + binding.notificationSettingsView.notificationSettings.visibility = VISIBLE + } else { + binding.clearConversationHistory.visibility = GONE + } } - if (!conversation!!.canDelete(conversationUser)) { - binding.deleteConversationAction.visibility = GONE - } else { - binding.deleteConversationAction.visibility = VISIBLE - } - - if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) { - binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE - } + if (!isDestroyed) { + binding.dangerZoneOptions.visibility = VISIBLE - if (conversation!!.notificationCalls === null) { - binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE - } else { - binding.notificationSettingsView.callNotificationsSwitch.isChecked = - (conversationCopy.notificationCalls == 1) - } + setupWebinaryView() - getListOfParticipants() + if (!conversation!!.canLeave()) { + binding.leaveConversationAction.visibility = GONE + } else { + binding.leaveConversationAction.visibility = VISIBLE + } - binding.progressBar.visibility = GONE + if (!conversation!!.canDelete(conversationUser)) { + binding.deleteConversationAction.visibility = GONE + } else { + binding.deleteConversationAction.visibility = VISIBLE + } - binding.conversationInfoName.visibility = VISIBLE + if (Conversation.ConversationType.ROOM_SYSTEM == conversation!!.type) { + binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE + } - binding.displayNameText.text = conversation!!.displayName + if (conversation!!.notificationCalls === null) { + binding.notificationSettingsView.callNotificationsSwitch.visibility = GONE + } else { + binding.notificationSettingsView.callNotificationsSwitch.isChecked = + (conversationCopy.notificationCalls == 1) + } - if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) { - binding.descriptionText.text = conversation!!.description - binding.conversationDescription.visibility = VISIBLE - } + getListOfParticipants() - loadConversationAvatar() - adjustNotificationLevelUI() - initRecordingConsentOption() - initExpiringMessageOption() - - binding.let { - GuestAccessHelper( - this@ConversationInfoActivity, - it, - conversation!!, - conversationUser - ).setupGuestAccess() - } - if (ConversationUtils.isNoteToSelfConversation( - ConversationModel.mapToConversationModel(conversation!!) - ) - ) { - binding.notificationSettingsView.notificationSettings.visibility = GONE - } else { - binding.notificationSettingsView.notificationSettings.visibility = VISIBLE + binding.progressBar.visibility = GONE + + binding.conversationInfoName.visibility = VISIBLE + + binding.displayNameText.text = conversation!!.displayName + + if (conversation!!.description != null && conversation!!.description!!.isNotEmpty()) { + binding.descriptionText.text = conversation!!.description + binding.conversationDescription.visibility = VISIBLE + } + + loadConversationAvatar() + adjustNotificationLevelUI() + initRecordingConsentOption() + initExpiringMessageOption() + + binding.let { + GuestAccessHelper( + this@ConversationInfoActivity, + it, + conversation!!, + conversationUser + ).setupGuestAccess() + } + if (ConversationUtils.isNoteToSelfConversation( + ConversationModel.mapToConversationModel(conversation!!) + ) + ) { + binding.notificationSettingsView.notificationSettings.visibility = GONE + } else { + binding.notificationSettingsView.notificationSettings.visibility = VISIBLE + } } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "failed to fetch room info", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "failed to fetch room info", e) + } - override fun onComplete() { - roomDisposable!!.dispose() + override fun onComplete() { + roomDisposable!!.dispose() + } } - }) + ) } private fun initRecordingConsentOption() { @@ -795,11 +808,12 @@ class ConversationInfoActivity : } private fun submitRecordingConsentChanges() { - val state = if (binding.recordingConsentView.recordingConsentForConversationSwitch.isChecked) { - RECORDING_CONSENT_REQUIRED_FOR_CONVERSATION - } else { - RECORDING_CONSENT_NOT_REQUIRED_FOR_CONVERSATION - } + val state = + if (binding.recordingConsentView.recordingConsentForConversationSwitch.isChecked) { + RECORDING_CONSENT_REQUIRED_FOR_CONVERSATION + } else { + RECORDING_CONSENT_NOT_REQUIRED_FOR_CONVERSATION + } val apiVersion = ApiUtils.getConversationApiVersion(conversationUser, intArrayOf(ApiUtils.APIv4, 1)) @@ -810,24 +824,26 @@ class ConversationInfoActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onComplete() { - // unused atm - } + ?.subscribe( + object : Observer { + override fun onComplete() { + // unused atm + } - override fun onSubscribe(d: Disposable) { - // unused atm - } + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(t: GenericOverall) { - // unused atm - } + override fun onNext(t: GenericOverall) { + // unused atm + } - override fun onError(e: Throwable) { - Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - Log.e(TAG, "Error when setting recording consent option for conversation", e) + override fun onError(e: Throwable) { + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + Log.e(TAG, "Error when setting recording consent option for conversation", e) + } } - }) + ) } private fun initExpiringMessageOption() { @@ -905,16 +921,17 @@ class ConversationInfoActivity : private fun loadConversationAvatar() { when (conversation!!.type) { - Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) { - conversation!!.name?.let { - binding.avatarImage.loadUserAvatar( - conversationUser, - it, - true, - false - ) + Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> + if (!TextUtils.isEmpty(conversation!!.name)) { + conversation!!.name?.let { + binding.avatarImage.loadUserAvatar( + conversationUser, + it, + true, + false + ) + } } - } Conversation.ConversationType.ROOM_GROUP_CALL, Conversation.ConversationType.ROOM_PUBLIC_CALL -> { binding.avatarImage.loadConversationAvatar( @@ -945,24 +962,25 @@ class ConversationInfoActivity : } private fun toggleModeratorStatus(apiVersion: Int, participant: Participant) { - val subscriber = object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + val subscriber = + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - getListOfParticipants() - } + override fun onNext(genericOverall: GenericOverall) { + getListOfParticipants() + } - @SuppressLint("LongLogTag") - override fun onError(e: Throwable) { - Log.e(TAG, "Error toggling moderator status", e) - } + @SuppressLint("LongLogTag") + override fun onError(e: Throwable) { + Log.e(TAG, "Error toggling moderator status", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - } if (participant.type == Participant.ParticipantType.MODERATOR || participant.type == Participant.ParticipantType.GUEST_MODERATOR @@ -998,24 +1016,25 @@ class ConversationInfoActivity : } private fun toggleModeratorStatusLegacy(apiVersion: Int, participant: Participant) { - val subscriber = object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + val subscriber = + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - getListOfParticipants() - } + override fun onNext(genericOverall: GenericOverall) { + getListOfParticipants() + } - @SuppressLint("LongLogTag") - override fun onError(e: Throwable) { - Log.e(TAG, "Error toggling moderator status", e) - } + @SuppressLint("LongLogTag") + override fun onError(e: Throwable) { + Log.e(TAG, "Error toggling moderator status", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - } if (participant.type == Participant.ParticipantType.MODERATOR) { ncApi.demoteModeratorToUser( @@ -1059,24 +1078,26 @@ class ConversationInfoActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - getListOfParticipants() - } + override fun onNext(genericOverall: GenericOverall) { + getListOfParticipants() + } - @SuppressLint("LongLogTag") - override fun onError(e: Throwable) { - Log.e(TAG, "Error removing attendee from conversation", e) - } + @SuppressLint("LongLogTag") + override fun onError(e: Throwable) { + Log.e(TAG, "Error removing attendee from conversation", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } else { if (participant.type == Participant.ParticipantType.GUEST || participant.type == Participant.ParticipantType.USER_FOLLOWING_LINK @@ -1092,24 +1113,26 @@ class ConversationInfoActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - getListOfParticipants() + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + getListOfParticipants() + } + + @SuppressLint("LongLogTag") + override fun onError(e: Throwable) { + Log.e(TAG, "Error removing guest from conversation", e) + } + + override fun onComplete() { + // unused atm + } } - - @SuppressLint("LongLogTag") - override fun onError(e: Throwable) { - Log.e(TAG, "Error removing guest from conversation", e) - } - - override fun onComplete() { - // unused atm - } - }) + ) } else { ncApi.removeParticipantFromConversation( credentials, @@ -1122,24 +1145,26 @@ class ConversationInfoActivity : ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - getListOfParticipants() - } - - @SuppressLint("LongLogTag") - override fun onError(e: Throwable) { - Log.e(TAG, "Error removing user from conversation", e) - } - - override fun onComplete() { - // unused atm + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + getListOfParticipants() + } + + @SuppressLint("LongLogTag") + override fun onError(e: Throwable) { + Log.e(TAG, "Error removing user from conversation", e) + } + + override fun onComplete() { + // unused atm + } } - }) + ) } } } @@ -1157,12 +1182,13 @@ class ConversationInfoActivity : if (participant.calculatedActorType == USERS && participant.calculatedActorId == conversationUser.userId) { if (participant.attendeePin?.isNotEmpty() == true) { - val items = mutableListOf( - BasicListItemWithImage( - R.drawable.ic_lock_grey600_24px, - context.getString(R.string.nc_attendee_pin, participant.attendeePin) + val items = + mutableListOf( + BasicListItemWithImage( + R.drawable.ic_lock_grey600_24px, + context.getString(R.string.nc_attendee_pin, participant.attendeePin) + ) ) - ) MaterialDialog(this, BottomSheet(WRAP_CONTENT)).show { cornerRadius(res = R.dimen.corner_radius) @@ -1183,12 +1209,13 @@ class ConversationInfoActivity : } if (participant.calculatedActorType == GROUPS) { - val items = mutableListOf( - BasicListItemWithImage( - R.drawable.ic_delete_grey600_24dp, - context.getString(R.string.nc_remove_group_and_members) + val items = + mutableListOf( + BasicListItemWithImage( + R.drawable.ic_delete_grey600_24dp, + context.getString(R.string.nc_remove_group_and_members) + ) ) - ) MaterialDialog(this, BottomSheet(WRAP_CONTENT)).show { cornerRadius(res = R.dimen.corner_radius) @@ -1203,12 +1230,13 @@ class ConversationInfoActivity : } if (participant.calculatedActorType == CIRCLES) { - val items = mutableListOf( - BasicListItemWithImage( - R.drawable.ic_delete_grey600_24dp, - context.getString(R.string.nc_remove_circle_and_members) + val items = + mutableListOf( + BasicListItemWithImage( + R.drawable.ic_delete_grey600_24dp, + context.getString(R.string.nc_remove_circle_and_members) + ) ) - ) MaterialDialog(this, BottomSheet(WRAP_CONTENT)).show { cornerRadius(res = R.dimen.corner_radius) @@ -1222,24 +1250,25 @@ class ConversationInfoActivity : return true } - val items = mutableListOf( - BasicListItemWithImage( - R.drawable.ic_lock_grey600_24px, - context.getString(R.string.nc_attendee_pin, participant.attendeePin) - ), - BasicListItemWithImage( - R.drawable.ic_pencil_grey600_24dp, - context.getString(R.string.nc_promote) - ), - BasicListItemWithImage( - R.drawable.ic_pencil_grey600_24dp, - context.getString(R.string.nc_demote) - ), - BasicListItemWithImage( - R.drawable.ic_delete_grey600_24dp, - context.getString(R.string.nc_remove_participant) + val items = + mutableListOf( + BasicListItemWithImage( + R.drawable.ic_lock_grey600_24px, + context.getString(R.string.nc_attendee_pin, participant.attendeePin) + ), + BasicListItemWithImage( + R.drawable.ic_pencil_grey600_24dp, + context.getString(R.string.nc_promote) + ), + BasicListItemWithImage( + R.drawable.ic_pencil_grey600_24dp, + context.getString(R.string.nc_demote) + ), + BasicListItemWithImage( + R.drawable.ic_delete_grey600_24dp, + context.getString(R.string.nc_remove_participant) + ) ) - ) if (participant.type == Participant.ParticipantType.MODERATOR || participant.type == Participant.ParticipantType.GUEST_MODERATOR @@ -1310,11 +1339,11 @@ class ConversationInfoActivity : module.saveString("conversation_info_message_notifications_dropdown", value) } - binding.notificationSettingsView.importantConversationSwitch.isChecked = module - .getBoolean("important_conversation_switch", false) + binding.notificationSettingsView.importantConversationSwitch.isChecked = + module.getBoolean("important_conversation_switch", false) - binding.notificationSettingsView.callNotificationsSwitch.isChecked = module - .getBoolean("call_notifications_switch", true) + binding.notificationSettingsView.callNotificationsSwitch.isChecked = + module.getBoolean("call_notifications_switch", true) } companion object { diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt b/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt index 0c0cc9a387a..112e7773507 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfo/GuestAccessHelper.kt @@ -111,22 +111,23 @@ class GuestAccessHelper( } private fun shareUrl() { - val sendIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - type = Mimetype.TEXT_PLAIN - putExtra( - Intent.EXTRA_SUBJECT, - String.format( - activity.resources.getString(R.string.nc_share_subject), - activity.resources.getString(R.string.nc_app_product_name) + val sendIntent: Intent = + Intent().apply { + action = Intent.ACTION_SEND + type = Mimetype.TEXT_PLAIN + putExtra( + Intent.EXTRA_SUBJECT, + String.format( + activity.resources.getString(R.string.nc_share_subject), + activity.resources.getString(R.string.nc_app_product_name) + ) ) - ) - putExtra( - Intent.EXTRA_TEXT, - ShareUtils.getStringForIntent(activity, conversationUser, conversation) - ) - } + putExtra( + Intent.EXTRA_TEXT, + ShareUtils.getStringForIntent(activity, conversationUser, conversation) + ) + } val shareIntent = Intent.createChooser(sendIntent, null) activity.startActivity(shareIntent) @@ -159,8 +160,8 @@ class GuestAccessHelper( } inner class AllowGuestsResultObserver : Observer { - private lateinit var allowGuestsResult: ConversationsRepository.AllowGuestsResult + override fun onNext(t: ConversationsRepository.AllowGuestsResult) { allowGuestsResult = t } @@ -199,8 +200,8 @@ class GuestAccessHelper( inner class PasswordResultObserver(private val setPassword: Boolean) : Observer { - private lateinit var passwordResult: ConversationsRepository.PasswordResult + override fun onSubscribe(d: Disposable) = Unit override fun onNext(t: ConversationsRepository.PasswordResult) { diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt index 35d209cde5a..6b86637bcc0 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/ConversationInfoEditActivity.kt @@ -234,32 +234,34 @@ class ConversationInfoEditActivity : .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(1) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - if (CapabilitiesUtilNew.isConversationDescriptionEndpointAvailable(conversationUser)) { - saveConversationDescription() - } else { - finish() + override fun onNext(genericOverall: GenericOverall) { + if (CapabilitiesUtilNew.isConversationDescriptionEndpointAvailable(conversationUser)) { + saveConversationDescription() + } else { + finish() + } } - } - override fun onError(e: Throwable) { - Snackbar.make( - binding.root, - context.getString(R.string.default_error_msg), - Snackbar.LENGTH_LONG - ).show() - Log.e(TAG, "Error while saving conversation name", e) - } + override fun onError(e: Throwable) { + Snackbar.make( + binding.root, + context.getString(R.string.default_error_msg), + Snackbar.LENGTH_LONG + ).show() + Log.e(TAG, "Error while saving conversation name", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } fun saveConversationDescription() { @@ -278,28 +280,30 @@ class ConversationInfoEditActivity : .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(1) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - finish() - } + override fun onNext(genericOverall: GenericOverall) { + finish() + } - override fun onError(e: Throwable) { - Snackbar.make( - binding.root, - context.getString(R.string.default_error_msg), - Snackbar.LENGTH_LONG - ).show() - Log.e(TAG, "Error while saving conversation description", e) - } + override fun onError(e: Throwable) { + Snackbar.make( + binding.root, + context.getString(R.string.default_error_msg), + Snackbar.LENGTH_LONG + ).show() + Log.e(TAG, "Error while saving conversation description", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { @@ -335,9 +339,10 @@ class ConversationInfoEditActivity : setupAvatarOptions() when (conversation!!.type) { - ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> if (!TextUtils.isEmpty(conversation!!.name)) { - conversation!!.name?.let { binding.avatarImage.loadUserAvatar(conversationUser, it, true, false) } - } + ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL -> + if (!TextUtils.isEmpty(conversation!!.name)) { + conversation!!.name?.let { binding.avatarImage.loadUserAvatar(conversationUser, it, true, false) } + } ConversationType.ROOM_GROUP_CALL, ConversationType.ROOM_PUBLIC_CALL -> { binding.avatarImage.loadConversationAvatar(conversationUser, conversation!!, false, viewThemeUtils) diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/data/ConversationInfoEditRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/data/ConversationInfoEditRepositoryImpl.kt index 5a35288f4c2..7be9366abfb 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/data/ConversationInfoEditRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/data/ConversationInfoEditRepositoryImpl.kt @@ -47,11 +47,12 @@ class ConversationInfoEditRepositoryImpl(private val ncApi: NcApi, currentUserPr file!!.name, file.asRequestBody(Mimetype.IMAGE_PREFIX_GENERIC.toMediaTypeOrNull()) ) - val filePart: MultipartBody.Part = MultipartBody.Part.createFormData( - "file", - file.name, - file.asRequestBody(Mimetype.IMAGE_JPG.toMediaTypeOrNull()) - ) + val filePart: MultipartBody.Part = + MultipartBody.Part.createFormData( + "file", + file.name, + file.asRequestBody(Mimetype.IMAGE_JPG.toMediaTypeOrNull()) + ) return ncApi.uploadConversationAvatar( credentials, diff --git a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/viewmodel/ConversationInfoEditViewModel.kt b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/viewmodel/ConversationInfoEditViewModel.kt index 057102a5bd3..59dd37b03c2 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationinfoedit/viewmodel/ConversationInfoEditViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationinfoedit/viewmodel/ConversationInfoEditViewModel.kt @@ -35,20 +35,26 @@ import io.reactivex.schedulers.Schedulers import java.io.File import javax.inject.Inject -class ConversationInfoEditViewModel @Inject constructor( +class ConversationInfoEditViewModel +@Inject +constructor( private val repository: ChatRepository, private val conversationInfoEditRepository: ConversationInfoEditRepository ) : ViewModel() { sealed interface ViewState object GetRoomStartState : ViewState + object GetRoomErrorState : ViewState + open class GetRoomSuccessState(val conversationModel: ConversationModel) : ViewState object UploadAvatarErrorState : ViewState + open class UploadAvatarSuccessState(val conversationModel: ConversationModel) : ViewState object DeleteAvatarErrorState : ViewState + open class DeleteAvatarSuccessState(val conversationModel: ConversationModel) : ViewState private val _viewState: MutableLiveData = MutableLiveData(GetRoomStartState) diff --git a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt index f586cae847f..75c3ac07b61 100644 --- a/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/conversationlist/ConversationsListActivity.kt @@ -203,11 +203,12 @@ class ConversationsListActivity : ) val searchBehaviorSubject = BehaviorSubject.createDefault(false) - private val onBackPressedCallback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - finishAffinity() + private val onBackPressedCallback = + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + finishAffinity() + } } - } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -321,15 +322,15 @@ class ConversationsListActivity : for ((k, v) in filterState) { if (v) { when (k) { - FilterConversationFragment.MENTION -> result = (result && conversation.unreadMention) || - ( - result && - ( - conversation.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL || - conversation.type == Conversation.ConversationType.FORMER_ONE_TO_ONE - ) && - (conversation.unreadMessages > 0) - ) + FilterConversationFragment.MENTION -> + result = (result && conversation.unreadMention) || + ( + result && + ( + conversation.type == Conversation.ConversationType.ROOM_TYPE_ONE_TO_ONE_CALL || + conversation.type == Conversation.ConversationType.FORMER_ONE_TO_ONE + ) && (conversation.unreadMessages > 0) + ) FilterConversationFragment.UNREAD -> result = result && (conversation.unreadMessages > 0) } @@ -351,15 +352,14 @@ class ConversationsListActivity : viewThemeUtils.material.themeToolbar(binding.conversationListToolbar) } - private fun loadUserAvatar( - target: Target - ) { + private fun loadUserAvatar(target: Target) { if (currentUser != null) { - val url = ApiUtils.getUrlForAvatar( - currentUser!!.baseUrl, - currentUser!!.userId, - true - ) + val url = + ApiUtils.getUrlForAvatar( + currentUser!!.baseUrl, + currentUser!!.userId, + true + ) val credentials = ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token) @@ -380,29 +380,31 @@ class ConversationsListActivity : } private fun loadUserAvatar(button: MaterialButton) { - val target = object : Target { - override fun onStart(placeholder: Drawable?) { - button.icon = placeholder - } + val target = + object : Target { + override fun onStart(placeholder: Drawable?) { + button.icon = placeholder + } - override fun onSuccess(result: Drawable) { - button.icon = result + override fun onSuccess(result: Drawable) { + button.icon = result + } } - } loadUserAvatar(target) } private fun loadUserAvatar(menuItem: MenuItem) { - val target = object : Target { - override fun onStart(placeholder: Drawable?) { - menuItem.icon = placeholder - } + val target = + object : Target { + override fun onStart(placeholder: Drawable?) { + menuItem.icon = placeholder + } - override fun onSuccess(result: Drawable) { - menuItem.icon = result + override fun onSuccess(result: Drawable) { + menuItem.icon = result + } } - } loadUserAvatar(target) } @@ -422,21 +424,22 @@ class ConversationsListActivity : if (searchManager != null) { searchView!!.setSearchableInfo(searchManager.getSearchableInfo(componentName)) } - searchViewDisposable = observeSearchView(searchView!!) - .debounce { query: String? -> - if (TextUtils.isEmpty(query)) { - return@debounce Observable.empty() - } else { - return@debounce Observable.timer( - SEARCH_DEBOUNCE_INTERVAL_MS.toLong(), - TimeUnit.MILLISECONDS - ) + searchViewDisposable = + observeSearchView(searchView!!) + .debounce { query: String? -> + if (TextUtils.isEmpty(query)) { + return@debounce Observable.empty() + } else { + return@debounce Observable.timer( + SEARCH_DEBOUNCE_INTERVAL_MS.toLong(), + TimeUnit.MILLISECONDS + ) + } } - } - .distinctUntilChanged() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { newText: String? -> onQueryTextChange(newText) } + .distinctUntilChanged() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { newText: String? -> onQueryTextChange(newText) } } } @@ -495,46 +498,49 @@ class ConversationsListActivity : } true } - searchItem!!.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { - override fun onMenuItemActionExpand(item: MenuItem): Boolean { - adapter!!.setHeadersShown(true) - if (!filterState.containsValue(true)) filterableConversationItems = searchableConversationItems - adapter!!.updateDataSet(filterableConversationItems, false) - adapter!!.showAllHeaders() - binding?.swipeRefreshLayoutView?.isEnabled = false - searchBehaviorSubject.onNext(true) - return true - } - - override fun onMenuItemActionCollapse(item: MenuItem): Boolean { - adapter!!.setHeadersShown(false) - if (!filterState.containsValue(true)) filterableConversationItems = searchableConversationItems - adapter!!.updateDataSet(filterableConversationItems, false) - adapter!!.hideAllHeaders() - if (searchHelper != null) { - // cancel any pending searches - searchHelper!!.cancelSearch() - binding?.swipeRefreshLayoutView?.isRefreshing = false - searchBehaviorSubject.onNext(false) + searchItem!!.setOnActionExpandListener( + object : MenuItem.OnActionExpandListener { + override fun onMenuItemActionExpand(item: MenuItem): Boolean { + adapter!!.setHeadersShown(true) + if (!filterState.containsValue(true)) filterableConversationItems = searchableConversationItems + adapter!!.updateDataSet(filterableConversationItems, false) + adapter!!.showAllHeaders() + binding?.swipeRefreshLayoutView?.isEnabled = false + searchBehaviorSubject.onNext(true) + return true } - binding?.swipeRefreshLayoutView?.isEnabled = true - searchView!!.onActionViewCollapsed() - binding.conversationListAppbar.stateListAnimator = AnimatorInflater.loadStateListAnimator( - binding.conversationListAppbar.context, - R.animator.appbar_elevation_off - ) - binding.conversationListToolbar.visibility = View.GONE - binding.searchToolbar.visibility = View.VISIBLE - if (resources != null) { - viewThemeUtils.platform.resetStatusBar(this@ConversationsListActivity) - } + override fun onMenuItemActionCollapse(item: MenuItem): Boolean { + adapter!!.setHeadersShown(false) + if (!filterState.containsValue(true)) filterableConversationItems = searchableConversationItems + adapter!!.updateDataSet(filterableConversationItems, false) + adapter!!.hideAllHeaders() + if (searchHelper != null) { + // cancel any pending searches + searchHelper!!.cancelSearch() + binding?.swipeRefreshLayoutView?.isRefreshing = false + searchBehaviorSubject.onNext(false) + } + binding?.swipeRefreshLayoutView?.isEnabled = true + searchView!!.onActionViewCollapsed() + + binding.conversationListAppbar.stateListAnimator = + AnimatorInflater.loadStateListAnimator( + binding.conversationListAppbar.context, + R.animator.appbar_elevation_off + ) + binding.conversationListToolbar.visibility = View.GONE + binding.searchToolbar.visibility = View.VISIBLE + if (resources != null) { + viewThemeUtils.platform.resetStatusBar(this@ConversationsListActivity) + } - val layoutManager = binding?.recyclerView?.layoutManager as SmoothScrollLinearLayoutManager? - layoutManager?.scrollToPositionWithOffset(0, 0) - return true + val layoutManager = binding?.recyclerView?.layoutManager as SmoothScrollLinearLayoutManager? + layoutManager?.scrollToPositionWithOffset(0, 0) + return true + } } - }) + ) } return true } @@ -559,10 +565,11 @@ class ConversationsListActivity : // layoutParams.setScrollFlags(AppBarLayout.LayoutParams.SCROLL_FLAG_SCROLL | AppBarLayout // .LayoutParams.SCROLL_FLAG_SNAP | AppBarLayout.LayoutParams.SCROLL_FLAG_ENTER_ALWAYS); layoutParams.scrollFlags = 0 - binding.conversationListAppbar.stateListAnimator = AnimatorInflater.loadStateListAnimator( - binding.conversationListAppbar.context, - R.animator.appbar_elevation_off - ) + binding.conversationListAppbar.stateListAnimator = + AnimatorInflater.loadStateListAnimator( + binding.conversationListAppbar.context, + R.animator.appbar_elevation_off + ) binding.searchToolbar.layoutParams = layoutParams } @@ -572,10 +579,11 @@ class ConversationsListActivity : binding.conversationListToolbar.visibility = View.VISIBLE viewThemeUtils.material.colorToolbarOverflowIcon(binding.conversationListToolbar) layoutParams.scrollFlags = 0 - binding.conversationListAppbar.stateListAnimator = AnimatorInflater.loadStateListAnimator( - binding.conversationListAppbar.context, - R.animator.appbar_elevation_on - ) + binding.conversationListAppbar.stateListAnimator = + AnimatorInflater.loadStateListAnimator( + binding.conversationListAppbar.context, + R.animator.appbar_elevation_on + ) binding.conversationListToolbar.layoutParams = layoutParams } @@ -584,10 +592,11 @@ class ConversationsListActivity : binding.searchToolbar.visibility = View.GONE binding.conversationListToolbar.visibility = View.VISIBLE layoutParams.scrollFlags = 0 - binding.conversationListAppbar.stateListAnimator = AnimatorInflater.loadStateListAnimator( - binding.conversationListAppbar.context, - R.animator.appbar_elevation_on - ) + binding.conversationListAppbar.stateListAnimator = + AnimatorInflater.loadStateListAnimator( + binding.conversationListAppbar.context, + R.animator.appbar_elevation_on + ) } private fun hasActivityActionSendIntent(): Boolean { @@ -595,10 +604,11 @@ class ConversationsListActivity : } private fun showSearchView(searchView: SearchView?, searchItem: MenuItem?) { - binding.conversationListAppbar.stateListAnimator = AnimatorInflater.loadStateListAnimator( - binding.conversationListAppbar.context, - R.animator.appbar_elevation_on - ) + binding.conversationListAppbar.stateListAnimator = + AnimatorInflater.loadStateListAnimator( + binding.conversationListAppbar.context, + R.animator.appbar_elevation_on + ) binding.conversationListToolbar.visibility = View.VISIBLE binding.searchToolbar.visibility = View.GONE searchItem!!.expandActionView() @@ -618,52 +628,53 @@ class ConversationsListActivity : val apiVersion = ApiUtils.getConversationApiVersion(currentUser, intArrayOf(ApiUtils.APIv4, ApiUtils.APIv3, 1)) val startNanoTime = System.nanoTime() Log.d(TAG, "fetchData - getRooms - calling: $startNanoTime") - roomsQueryDisposable = ncApi.getRooms( - credentials, - ApiUtils.getUrlForRooms( - apiVersion, - currentUser!!.baseUrl - ), - includeStatus - ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ (ocs): RoomsOverall -> - Log.d(TAG, "fetchData - getRooms - got response: $startNanoTime") - - // This is invoked asynchronously, when server returns a response the view might have been - // unbound in the meantime. Check if the view is still there. - // FIXME - does it make sense to update internal data structures even when view has been unbound? - // if (view == null) { - // Log.d(TAG, "fetchData - getRooms - view is not bound: $startNanoTime") - // return@subscribe - // } - - if (adapterWasNull) { - adapterWasNull = false - binding?.loadingContent?.visibility = View.GONE - } - initOverallLayout(ocs!!.data!!.isNotEmpty()) - for (conversation in ocs.data!!) { - addToConversationItems(conversation) + roomsQueryDisposable = + ncApi.getRooms( + credentials, + ApiUtils.getUrlForRooms( + apiVersion, + currentUser!!.baseUrl + ), + includeStatus + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ (ocs): RoomsOverall -> + Log.d(TAG, "fetchData - getRooms - got response: $startNanoTime") + + // This is invoked asynchronously, when server returns a response the view might have been + // unbound in the meantime. Check if the view is still there. + // FIXME - does it make sense to update internal data structures even when view has been unbound? + // if (view == null) { + // Log.d(TAG, "fetchData - getRooms - view is not bound: $startNanoTime") + // return@subscribe + // } + + if (adapterWasNull) { + adapterWasNull = false + binding?.loadingContent?.visibility = View.GONE + } + initOverallLayout(ocs!!.data!!.isNotEmpty()) + for (conversation in ocs.data!!) { + addToConversationItems(conversation) + } + sortConversations(conversationItems) + sortConversations(conversationItemsWithHeader) + if (!filterState.containsValue(true)) filterableConversationItems = conversationItems + filterConversation() + adapter!!.updateDataSet(filterableConversationItems, false) + Handler().postDelayed({ checkToShowUnreadBubble() }, UNREAD_BUBBLE_DELAY.toLong()) + fetchOpenConversations(apiVersion) + binding?.swipeRefreshLayoutView?.isRefreshing = false + }, { throwable: Throwable -> + handleHttpExceptions(throwable) + binding?.swipeRefreshLayoutView?.isRefreshing = false + dispose(roomsQueryDisposable) + }) { + dispose(roomsQueryDisposable) + binding?.swipeRefreshLayoutView?.isRefreshing = false + isRefreshing = false } - sortConversations(conversationItems) - sortConversations(conversationItemsWithHeader) - if (!filterState.containsValue(true)) filterableConversationItems = conversationItems - filterConversation() - adapter!!.updateDataSet(filterableConversationItems, false) - Handler().postDelayed({ checkToShowUnreadBubble() }, UNREAD_BUBBLE_DELAY.toLong()) - fetchOpenConversations(apiVersion) - binding?.swipeRefreshLayoutView?.isRefreshing = false - }, { throwable: Throwable -> - handleHttpExceptions(throwable) - binding?.swipeRefreshLayoutView?.isRefreshing = false - dispose(roomsQueryDisposable) - }) { - dispose(roomsQueryDisposable) - binding?.swipeRefreshLayoutView?.isRefreshing = false - isRefreshing = false - } } private fun initOverallLayout(isConversationListNotEmpty: Boolean) { @@ -704,35 +715,38 @@ class ConversationsListActivity : callHeaderItems[headerTitle] = genericTextHeaderItem } - val conversationItem = ConversationItem( - conversation, - currentUser!!, - this, - viewThemeUtils - ) + val conversationItem = + ConversationItem( + conversation, + currentUser!!, + this, + viewThemeUtils + ) conversationItems.add(conversationItem) - val conversationItemWithHeader = ConversationItem( - conversation, - currentUser!!, - this, - callHeaderItems[headerTitle], - viewThemeUtils - ) + val conversationItemWithHeader = + ConversationItem( + conversation, + currentUser!!, + this, + callHeaderItems[headerTitle], + viewThemeUtils + ) conversationItemsWithHeader.add(conversationItemWithHeader) } private fun showErrorDialog() { binding.floatingActionButton.let { - val dialogBuilder = MaterialAlertDialogBuilder(it.context) - .setIcon( - viewThemeUtils.dialog.colorMaterialAlertDialogIcon( - context, - R.drawable.ic_baseline_error_outline_24dp + val dialogBuilder = + MaterialAlertDialogBuilder(it.context) + .setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_baseline_error_outline_24dp + ) ) - ) - .setTitle(R.string.error_loading_chats) - .setCancelable(false) - .setNegativeButton(R.string.close, null) + .setTitle(R.string.error_loading_chats) + .setCancelable(false) + .setNegativeButton(R.string.close, null) if (resources!!.getBoolean(R.bool.multiaccount_support) && userManager.users.blockingGet().size > 1) { dialogBuilder.setPositiveButton(R.string.nc_switch_account) { _, _ -> @@ -775,35 +789,37 @@ class ConversationsListActivity : searchableConversationItems.addAll(conversationItemsWithHeader) if (hasSpreedFeatureCapability(currentUser, "listable-rooms")) { val openConversationItems: MutableList> = ArrayList() - openConversationsQueryDisposable = ncApi.getOpenConversations( - credentials, - ApiUtils.getUrlForOpenConversations(apiVersion, currentUser!!.baseUrl) - ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe({ (ocs): RoomsOverall -> - for (conversation in ocs!!.data!!) { - val headerTitle = resources!!.getString(R.string.openConversations) - var genericTextHeaderItem: GenericTextHeaderItem - if (!callHeaderItems.containsKey(headerTitle)) { - genericTextHeaderItem = GenericTextHeaderItem(headerTitle, viewThemeUtils) - callHeaderItems[headerTitle] = genericTextHeaderItem + openConversationsQueryDisposable = + ncApi.getOpenConversations( + credentials, + ApiUtils.getUrlForOpenConversations(apiVersion, currentUser!!.baseUrl) + ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe({ (ocs): RoomsOverall -> + for (conversation in ocs!!.data!!) { + val headerTitle = resources!!.getString(R.string.openConversations) + var genericTextHeaderItem: GenericTextHeaderItem + if (!callHeaderItems.containsKey(headerTitle)) { + genericTextHeaderItem = GenericTextHeaderItem(headerTitle, viewThemeUtils) + callHeaderItems[headerTitle] = genericTextHeaderItem + } + val conversationItem = + ConversationItem( + conversation, + currentUser!!, + this, + callHeaderItems[headerTitle], + viewThemeUtils + ) + openConversationItems.add(conversationItem) } - val conversationItem = ConversationItem( - conversation, - currentUser!!, - this, - callHeaderItems[headerTitle], - viewThemeUtils - ) - openConversationItems.add(conversationItem) - } - searchableConversationItems.addAll(openConversationItems) - }, { throwable: Throwable -> - Log.e(TAG, "fetchData - getRooms - ERROR", throwable) - handleHttpExceptions(throwable) - dispose(openConversationsQueryDisposable) - }) { dispose(openConversationsQueryDisposable) } + searchableConversationItems.addAll(openConversationItems) + }, { throwable: Throwable -> + Log.e(TAG, "fetchData - getRooms - ERROR", throwable) + handleHttpExceptions(throwable) + dispose(openConversationsQueryDisposable) + }) { dispose(openConversationsQueryDisposable) } } else { Log.d(TAG, "no open conversations fetched because of missing capability") } @@ -832,17 +848,19 @@ class ConversationsListActivity : binding.recyclerView.layoutManager = layoutManager binding.recyclerView.setHasFixedSize(true) binding.recyclerView.adapter = adapter - binding.recyclerView.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - super.onScrollStateChanged(recyclerView, newState) - if (newState == RecyclerView.SCROLL_STATE_IDLE) { - val isSearchActive = searchBehaviorSubject.value - if (!isSearchActive!!) { - checkToShowUnreadBubble() + binding.recyclerView.addOnScrollListener( + object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (newState == RecyclerView.SCROLL_STATE_IDLE) { + val isSearchActive = searchBehaviorSubject.value + if (!isSearchActive!!) { + checkToShowUnreadBubble() + } } } } - }) + ) binding?.recyclerView?.setOnTouchListener { v: View, _: MotionEvent? -> if (!isDestroyed) { val imm = getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager @@ -872,10 +890,11 @@ class ConversationsListActivity : updateFilterConversationButtonColor() binding.filterConversationsButton.setOnClickListener { - val newFragment: DialogFragment = FilterConversationFragment.newInstance( - filterState, - this - ) + val newFragment: DialogFragment = + FilterConversationFragment.newInstance( + filterState, + this + ) newFragment.show(supportFragmentManager, FilterConversationFragment.TAG) } @@ -1130,29 +1149,31 @@ class ConversationsListActivity : val filename = FileUtils.getFileName(Uri.parse(file), context) fileNamesWithLineBreaks.append(filename).append("\n") } - val confirmationQuestion: String = if (filesToShare!!.size == 1) { - String.format( - resources!!.getString(R.string.nc_upload_confirm_send_single), - selectedConversation!!.displayName - ) - } else { - String.format( - resources!!.getString(R.string.nc_upload_confirm_send_multiple), - selectedConversation!!.displayName - ) - } + val confirmationQuestion: String = + if (filesToShare!!.size == 1) { + String.format( + resources!!.getString(R.string.nc_upload_confirm_send_single), + selectedConversation!!.displayName + ) + } else { + String.format( + resources!!.getString(R.string.nc_upload_confirm_send_multiple), + selectedConversation!!.displayName + ) + } binding.floatingActionButton.let { - val dialogBuilder = MaterialAlertDialogBuilder(it.context) - .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.upload)) - .setTitle(confirmationQuestion) - .setMessage(fileNamesWithLineBreaks.toString()) - .setPositiveButton(R.string.nc_yes) { _, _ -> - upload() - openConversation() - } - .setNegativeButton(R.string.nc_no) { _, _ -> - Log.d(TAG, "sharing files aborted, going back to share-to screen") - } + val dialogBuilder = + MaterialAlertDialogBuilder(it.context) + .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.upload)) + .setTitle(confirmationQuestion) + .setMessage(fileNamesWithLineBreaks.toString()) + .setPositiveButton(R.string.nc_yes) { _, _ -> + upload() + openConversation() + } + .setNegativeButton(R.string.nc_no) { _, _ -> + Log.d(TAG, "sharing files aborted, going back to share-to screen") + } viewThemeUtils.dialog .colorMaterialAlertDialogBackground(it.context, dialogBuilder) @@ -1178,11 +1199,12 @@ class ConversationsListActivity : val clickedItem: Any? = adapter!!.getItem(position) if (clickedItem != null && clickedItem is ConversationItem) { val conversation = clickedItem.model - conversationsListBottomDialog = ConversationsListBottomDialog( - this, - userManager.currentUser.blockingGet(), - conversation - ) + conversationsListBottomDialog = + ConversationsListBottomDialog( + this, + userManager.currentUser.blockingGet(), + conversation + ) conversationsListBottomDialog!!.show() } } @@ -1312,9 +1334,10 @@ class ConversationsListActivity : fun onMessageEvent(eventStatus: EventStatus) { if (currentUser != null && eventStatus.userId == currentUser!!.id) { when (eventStatus.eventType) { - EventStatus.EventType.CONVERSATION_UPDATE -> if (eventStatus.isAllGood && !isRefreshing) { - fetchRooms() - } + EventStatus.EventType.CONVERSATION_UPDATE -> + if (eventStatus.isAllGood && !isRefreshing) { + fetchRooms() + } else -> {} } @@ -1333,18 +1356,19 @@ class ConversationsListActivity : fun showDeleteConversationDialog(conversation: Conversation) { binding.floatingActionButton.let { - val dialogBuilder = MaterialAlertDialogBuilder(it.context) - .setIcon( - viewThemeUtils.dialog - .colorMaterialAlertDialogIcon(context, R.drawable.ic_delete_black_24dp) - ) - .setTitle(R.string.nc_delete_call) - .setMessage(R.string.nc_delete_conversation_more) - .setPositiveButton(R.string.nc_delete) { _, _ -> - deleteConversation(conversation) - } - .setNegativeButton(R.string.nc_cancel) { _, _ -> - } + val dialogBuilder = + MaterialAlertDialogBuilder(it.context) + .setIcon( + viewThemeUtils.dialog + .colorMaterialAlertDialogIcon(context, R.drawable.ic_delete_black_24dp) + ) + .setTitle(R.string.nc_delete_call) + .setMessage(R.string.nc_delete_conversation_more) + .setPositiveButton(R.string.nc_delete) { _, _ -> + deleteConversation(conversation) + } + .setNegativeButton(R.string.nc_cancel) { _, _ -> + } viewThemeUtils.dialog .colorMaterialAlertDialogBackground(it.context, dialogBuilder) @@ -1358,27 +1382,28 @@ class ConversationsListActivity : private fun showUnauthorizedDialog() { binding.floatingActionButton.let { - val dialogBuilder = MaterialAlertDialogBuilder(it.context) - .setIcon( - viewThemeUtils.dialog.colorMaterialAlertDialogIcon( - context, - R.drawable.ic_delete_black_24dp + val dialogBuilder = + MaterialAlertDialogBuilder(it.context) + .setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_delete_black_24dp + ) ) - ) - .setTitle(R.string.nc_dialog_invalid_password) - .setMessage(R.string.nc_dialog_reauth_or_delete) - .setCancelable(false) - .setPositiveButton(R.string.nc_settings_remove_account) { _, _ -> - deleteUserAndRestartApp() - } - .setNegativeButton(R.string.nc_settings_reauthorize) { _, _ -> - val intent = Intent(context, WebViewLoginActivity::class.java) - val bundle = Bundle() - bundle.putString(BundleKeys.KEY_BASE_URL, currentUser!!.baseUrl) - bundle.putBoolean(BundleKeys.KEY_REAUTHORIZE_ACCOUNT, true) - intent.putExtras(bundle) - startActivity(intent) - } + .setTitle(R.string.nc_dialog_invalid_password) + .setMessage(R.string.nc_dialog_reauth_or_delete) + .setCancelable(false) + .setPositiveButton(R.string.nc_settings_remove_account) { _, _ -> + deleteUserAndRestartApp() + } + .setNegativeButton(R.string.nc_settings_reauthorize) { _, _ -> + val intent = Intent(context, WebViewLoginActivity::class.java) + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_BASE_URL, currentUser!!.baseUrl) + bundle.putBoolean(BundleKeys.KEY_REAUTHORIZE_ACCOUNT, true) + intent.putExtras(bundle) + startActivity(intent) + } viewThemeUtils.dialog.colorMaterialAlertDialogBackground(it.context, dialogBuilder) val dialog = dialogBuilder.show() @@ -1400,10 +1425,12 @@ class ConversationsListActivity : when (workInfo.state) { WorkInfo.State.SUCCEEDED -> { - val text = String.format( - context.resources.getString(R.string.nc_deleted_user), - currentUser!!.displayName - ) + val text = + String + .format( + context.resources.getString(R.string.nc_deleted_user), + currentUser!!.displayName + ) Toast.makeText( context, text, @@ -1435,27 +1462,28 @@ class ConversationsListActivity : private fun showOutdatedClientDialog() { binding.floatingActionButton.let { - val dialogBuilder = MaterialAlertDialogBuilder(it.context) - .setIcon( - viewThemeUtils.dialog.colorMaterialAlertDialogIcon( - context, - R.drawable.ic_info_white_24dp - ) - ) - .setTitle(R.string.nc_dialog_outdated_client) - .setMessage(R.string.nc_dialog_outdated_client_description) - .setCancelable(false) - .setPositiveButton(R.string.nc_dialog_outdated_client_option_update) { _, _ -> - try { - startActivity( - Intent(Intent.ACTION_VIEW, Uri.parse(CLIENT_UPGRADE_MARKET_LINK + packageName)) - ) - } catch (e: ActivityNotFoundException) { - startActivity( - Intent(Intent.ACTION_VIEW, Uri.parse(CLIENT_UPGRADE_GPLAY_LINK + packageName)) + val dialogBuilder = + MaterialAlertDialogBuilder(it.context) + .setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_info_white_24dp ) + ) + .setTitle(R.string.nc_dialog_outdated_client) + .setMessage(R.string.nc_dialog_outdated_client_description) + .setCancelable(false) + .setPositiveButton(R.string.nc_dialog_outdated_client_option_update) { _, _ -> + try { + startActivity( + Intent(Intent.ACTION_VIEW, Uri.parse(CLIENT_UPGRADE_MARKET_LINK + packageName)) + ) + } catch (e: ActivityNotFoundException) { + startActivity( + Intent(Intent.ACTION_VIEW, Uri.parse(CLIENT_UPGRADE_GPLAY_LINK + packageName)) + ) + } } - } if (resources!!.getBoolean(R.bool.multiaccount_support) && userManager.users.blockingGet().size > 1) { dialogBuilder.setNegativeButton(R.string.nc_switch_account) { _, _ -> @@ -1485,19 +1513,20 @@ class ConversationsListActivity : private fun showServiceUnavailableDialog(httpException: HttpException) { if (httpException.response()?.headers()?.get(MAINTENANCE_MODE_HEADER_KEY) == "1") { binding.floatingActionButton.let { - val dialogBuilder = MaterialAlertDialogBuilder(it.context) - .setIcon( - viewThemeUtils.dialog.colorMaterialAlertDialogIcon( - context, - R.drawable.ic_info_white_24dp + val dialogBuilder = + MaterialAlertDialogBuilder(it.context) + .setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_info_white_24dp + ) ) - ) - .setTitle(R.string.nc_dialog_maintenance_mode) - .setMessage(R.string.nc_dialog_maintenance_mode_description) - .setCancelable(false) - .setNegativeButton(R.string.nc_settings_remove_account) { _, _ -> - deleteUserAndRestartApp() - } + .setTitle(R.string.nc_dialog_maintenance_mode) + .setMessage(R.string.nc_dialog_maintenance_mode_description) + .setCancelable(false) + .setNegativeButton(R.string.nc_settings_remove_account) { _, _ -> + deleteUserAndRestartApp() + } if (resources!!.getBoolean(R.bool.multiaccount_support) && userManager.users.blockingGet().size > 1) { dialogBuilder.setPositiveButton(R.string.nc_switch_account) { _, _ -> @@ -1529,14 +1558,15 @@ class ConversationsListActivity : private fun showServerEOLDialog() { binding.floatingActionButton.let { - val dialogBuilder = MaterialAlertDialogBuilder(it.context) - .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.ic_warning_white)) - .setTitle(R.string.nc_settings_server_eol_title) - .setMessage(R.string.nc_settings_server_eol) - .setCancelable(false) - .setPositiveButton(R.string.nc_settings_remove_account) { _, _ -> - deleteUserAndRestartApp() - } + val dialogBuilder = + MaterialAlertDialogBuilder(it.context) + .setIcon(viewThemeUtils.dialog.colorMaterialAlertDialogIcon(context, R.drawable.ic_warning_white)) + .setTitle(R.string.nc_settings_server_eol_title) + .setMessage(R.string.nc_settings_server_eol) + .setCancelable(false) + .setPositiveButton(R.string.nc_settings_remove_account) { _, _ -> + deleteUserAndRestartApp() + } if (resources!!.getBoolean(R.bool.multiaccount_support) && userManager.users.blockingGet().size > 1) { dialogBuilder.setNegativeButton(R.string.nc_switch_account) { _, _ -> diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt index 9be54fd4fce..670c2c882aa 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/RepositoryModule.kt @@ -86,8 +86,10 @@ class RepositoryModule { } @Provides - fun provideRemoteFileBrowserItemsRepository(okHttpClient: OkHttpClient, userProvider: CurrentUserProviderNew): - RemoteFileBrowserItemsRepository { + fun provideRemoteFileBrowserItemsRepository( + okHttpClient: OkHttpClient, + userProvider: CurrentUserProviderNew + ): RemoteFileBrowserItemsRepository { return RemoteFileBrowserItemsRepositoryImpl(okHttpClient, userProvider) } @@ -112,38 +114,41 @@ class RepositoryModule { } @Provides - fun provideRequestAssistanceRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): - RequestAssistanceRepository { + fun provideRequestAssistanceRepository( + ncApi: NcApi, + userProvider: CurrentUserProviderNew + ): RequestAssistanceRepository { return RequestAssistanceRepositoryImpl(ncApi, userProvider) } @Provides - fun provideOpenConversationsRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): - OpenConversationsRepository { + fun provideOpenConversationsRepository( + ncApi: NcApi, + userProvider: CurrentUserProviderNew + ): OpenConversationsRepository { return OpenConversationsRepositoryImpl(ncApi, userProvider) } @Provides - fun translateRepository(ncApi: NcApi): - TranslateRepository { + fun translateRepository(ncApi: NcApi): TranslateRepository { return TranslateRepositoryImpl(ncApi) } @Provides - fun provideChatRepository(ncApi: NcApi): - ChatRepository { + fun provideChatRepository(ncApi: NcApi): ChatRepository { return ChatRepositoryImpl(ncApi) } @Provides - fun provideConversationInfoEditRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): - ConversationInfoEditRepository { + fun provideConversationInfoEditRepository( + ncApi: NcApi, + userProvider: CurrentUserProviderNew + ): ConversationInfoEditRepository { return ConversationInfoEditRepositoryImpl(ncApi, userProvider) } @Provides - fun provideConversationRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): - ConversationRepository { + fun provideConversationRepository(ncApi: NcApi, userProvider: CurrentUserProviderNew): ConversationRepository { return ConversationRepositoryImpl(ncApi, userProvider) } } diff --git a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt index 596fb30d232..acad0aba2c9 100644 --- a/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt +++ b/app/src/main/java/com/nextcloud/talk/dagger/modules/ViewModelModule.kt @@ -47,7 +47,9 @@ import javax.inject.Inject import javax.inject.Provider import kotlin.reflect.KClass -class ViewModelFactory @Inject constructor( +class ViewModelFactory +@Inject +constructor( private val viewModels: MutableMap, Provider> ) : ViewModelProvider.Factory { override fun create(modelClass: Class): T = viewModels[modelClass]?.get() as T @@ -61,7 +63,6 @@ internal annotation class ViewModelKey(val value: KClass) @Module @Suppress("TooManyFunctions") abstract class ViewModelModule { - @Binds abstract fun bindViewModelFactory(factory: ViewModelFactory): ViewModelProvider.Factory diff --git a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt index aca16d0a6b9..d760e2d0ad1 100644 --- a/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt +++ b/app/src/main/java/com/nextcloud/talk/data/source/local/Migrations.kt @@ -26,26 +26,29 @@ import androidx.sqlite.db.SupportSQLiteDatabase @Suppress("MagicNumber") object Migrations { - val MIGRATION_6_8 = object : Migration(6, 8) { - override fun migrate(db: SupportSQLiteDatabase) { - Log.i("Migrations", "Migrating 6 to 8") - migrateToRoom(db) + val MIGRATION_6_8 = + object : Migration(6, 8) { + override fun migrate(db: SupportSQLiteDatabase) { + Log.i("Migrations", "Migrating 6 to 8") + migrateToRoom(db) + } } - } - val MIGRATION_7_8 = object : Migration(7, 8) { - override fun migrate(db: SupportSQLiteDatabase) { - Log.i("Migrations", "Migrating 7 to 8") - migrateToRoom(db) + val MIGRATION_7_8 = + object : Migration(7, 8) { + override fun migrate(db: SupportSQLiteDatabase) { + Log.i("Migrations", "Migrating 7 to 8") + migrateToRoom(db) + } } - } - val MIGRATION_8_9 = object : Migration(8, 9) { - override fun migrate(db: SupportSQLiteDatabase) { - Log.i("Migrations", "Migrating 8 to 9") - migrateToDualPrimaryKeyArbitraryStorage(db) + val MIGRATION_8_9 = + object : Migration(8, 9) { + override fun migrate(db: SupportSQLiteDatabase) { + Log.i("Migrations", "Migrating 8 to 9") + migrateToDualPrimaryKeyArbitraryStorage(db) + } } - } fun migrateToRoom(db: SupportSQLiteDatabase) { db.execSQL( diff --git a/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt b/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt index 4db51a3e736..1f1d103bcee 100644 --- a/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt +++ b/app/src/main/java/com/nextcloud/talk/data/source/local/TalkDatabase.kt @@ -56,41 +56,43 @@ import java.util.Locale HashMapHashMapConverter::class ) abstract class TalkDatabase : RoomDatabase() { - abstract fun usersDao(): UsersDao + abstract fun arbitraryStoragesDao(): ArbitraryStoragesDao companion object { const val TAG = "TalkDatabase" @Volatile - private var INSTANCE: TalkDatabase? = null + private var instance: TalkDatabase? = null @JvmStatic fun getInstance(context: Context, appPreferences: AppPreferences): TalkDatabase = - INSTANCE ?: synchronized(this) { - INSTANCE ?: build(context, appPreferences).also { INSTANCE = it } + instance ?: synchronized(this) { + instance ?: build(context, appPreferences).also { instance = it } } private fun build(context: Context, appPreferences: AppPreferences): TalkDatabase { val passCharArray = context.getString(R.string.nc_talk_database_encryption_key).toCharArray() val passphrase: ByteArray = SQLiteDatabase.getBytes(passCharArray) - val factory = if (appPreferences.isDbRoomMigrated) { - Log.i(TAG, "No cipher migration needed") - SupportFactory(passphrase) - } else { - Log.i(TAG, "Add cipher migration hook") - SupportFactory(passphrase, getCipherMigrationHook()) - } + val factory = + if (appPreferences.isDbRoomMigrated) { + Log.i(TAG, "No cipher migration needed") + SupportFactory(passphrase) + } else { + Log.i(TAG, "Add cipher migration hook") + SupportFactory(passphrase, getCipherMigrationHook()) + } - val dbName = context - .resources - .getString(R.string.nc_app_product_name) - .lowercase(Locale.getDefault()) - .replace(" ", "_") - .trim { it <= ' ' } + - ".sqlite" + val dbName = + context + .resources + .getString(R.string.nc_app_product_name) + .lowercase(Locale.getDefault()) + .replace(" ", "_") + .trim { it <= ' ' } + + ".sqlite" return Room .databaseBuilder(context.applicationContext, TalkDatabase::class.java, dbName) diff --git a/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepository.kt b/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepository.kt index ea5c3c9f8e6..1e8de3aaa63 100644 --- a/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/data/storage/ArbitraryStoragesRepository.kt @@ -26,7 +26,10 @@ import io.reactivex.Maybe interface ArbitraryStoragesRepository { fun getStorageSetting(accountIdentifier: Long, key: String, objectString: String): Maybe + fun deleteArbitraryStorage(accountIdentifier: Long): Int + fun saveArbitraryStorage(arbitraryStorage: ArbitraryStorage): Long + fun getAll(): Maybe> } diff --git a/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorageEntity.kt b/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorageEntity.kt index ab5f7dc6261..86de3500c7a 100644 --- a/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorageEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/data/storage/model/ArbitraryStorageEntity.kt @@ -30,13 +30,10 @@ import kotlinx.parcelize.Parcelize data class ArbitraryStorageEntity( @ColumnInfo(name = "accountIdentifier") var accountIdentifier: Long = 0, - @ColumnInfo(name = "key") var key: String = "", - @ColumnInfo(name = "object") var storageObject: String? = null, - @ColumnInfo(name = "value") var value: String? = null ) : Parcelable diff --git a/app/src/main/java/com/nextcloud/talk/data/user/UserMapper.kt b/app/src/main/java/com/nextcloud/talk/data/user/UserMapper.kt index f6a88ac773e..8527b53abf2 100644 --- a/app/src/main/java/com/nextcloud/talk/data/user/UserMapper.kt +++ b/app/src/main/java/com/nextcloud/talk/data/user/UserMapper.kt @@ -50,10 +50,11 @@ object UserMapper { } fun toEntity(model: User): UserEntity { - val userEntity = when (val id = model.id) { - null -> UserEntity(userId = model.userId, username = model.username, baseUrl = model.baseUrl) - else -> UserEntity(id, model.userId, model.username, model.baseUrl) - } + val userEntity = + when (val id = model.id) { + null -> UserEntity(userId = model.userId, username = model.username, baseUrl = model.baseUrl) + else -> UserEntity(id, model.userId, model.username, model.baseUrl) + } userEntity.apply { token = model.token displayName = model.displayName diff --git a/app/src/main/java/com/nextcloud/talk/data/user/UsersRepository.kt b/app/src/main/java/com/nextcloud/talk/data/user/UsersRepository.kt index e8470b59f4e..d3795e00caf 100644 --- a/app/src/main/java/com/nextcloud/talk/data/user/UsersRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/data/user/UsersRepository.kt @@ -31,17 +31,30 @@ import io.reactivex.Single @Suppress("TooManyFunctions") interface UsersRepository { fun getActiveUser(): Maybe + fun getActiveUserObservable(): Observable + fun getUsers(): Single> + fun getUserWithId(id: Long): Maybe + fun getUserWithIdNotScheduledForDeletion(id: Long): Maybe + fun getUserWithUserId(userId: String): Maybe + fun getUsersScheduledForDeletion(): Single> + fun getUsersNotScheduledForDeletion(): Single> + fun getUserWithUsernameAndServer(username: String, server: String): Maybe + fun updateUser(user: User): Int + fun insertUser(user: User): Long + fun setUserAsActiveWithId(id: Long): Single + fun deleteUser(user: User): Int + fun updatePushState(id: Long, state: PushConfigurationState): Single } diff --git a/app/src/main/java/com/nextcloud/talk/data/user/UsersRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/data/user/UsersRepositoryImpl.kt index d6723d7ee03..3db646d43c5 100644 --- a/app/src/main/java/com/nextcloud/talk/data/user/UsersRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/data/user/UsersRepositoryImpl.kt @@ -30,7 +30,6 @@ import io.reactivex.Single @Suppress("TooManyFunctions") class UsersRepositoryImpl(private val usersDao: UsersDao) : UsersRepository { - override fun getActiveUser(): Maybe { return usersDao.getActiveUser().map { UserMapper.toModel(it) } } diff --git a/app/src/main/java/com/nextcloud/talk/data/user/model/User.kt b/app/src/main/java/com/nextcloud/talk/data/user/model/User.kt index 30b8906e15e..f3593d076cb 100644 --- a/app/src/main/java/com/nextcloud/talk/data/user/model/User.kt +++ b/app/src/main/java/com/nextcloud/talk/data/user/model/User.kt @@ -42,7 +42,6 @@ data class User( var current: Boolean = FALSE, var scheduledForDeletion: Boolean = FALSE ) : Parcelable { - fun getCredentials(): String = ApiUtils.getCredentials(username, token) fun hasSpreedFeatureCapability(capabilityName: String): Boolean { diff --git a/app/src/main/java/com/nextcloud/talk/data/user/model/UserEntity.kt b/app/src/main/java/com/nextcloud/talk/data/user/model/UserEntity.kt index 33144dbe430..a32e8599009 100644 --- a/app/src/main/java/com/nextcloud/talk/data/user/model/UserEntity.kt +++ b/app/src/main/java/com/nextcloud/talk/data/user/model/UserEntity.kt @@ -38,37 +38,26 @@ data class UserEntity( @PrimaryKey(autoGenerate = true) @ColumnInfo(name = "id") var id: Long = 0, - @ColumnInfo(name = "userId") var userId: String? = null, - @ColumnInfo(name = "username") var username: String? = null, - @ColumnInfo(name = "baseUrl") var baseUrl: String? = null, - @ColumnInfo(name = "token") var token: String? = null, - @ColumnInfo(name = "displayName") var displayName: String? = null, - @ColumnInfo(name = "pushConfigurationState") var pushConfigurationState: PushConfigurationState? = null, - @ColumnInfo(name = "capabilities") var capabilities: Capabilities? = null, - @ColumnInfo(name = "clientCertificate") var clientCertificate: String? = null, - @ColumnInfo(name = "externalSignalingServer") var externalSignalingServer: ExternalSignalingServer? = null, - @ColumnInfo(name = "current") var current: Boolean = FALSE, - @ColumnInfo(name = "scheduledForDeletion") var scheduledForDeletion: Boolean = FALSE ) : Parcelable diff --git a/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt b/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt index b16e3041900..8807243f759 100644 --- a/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt +++ b/app/src/main/java/com/nextcloud/talk/extensions/ImageViewExtensions.kt @@ -75,13 +75,14 @@ fun ImageView.loadConversationAvatar( ignoreCache: Boolean, viewThemeUtils: ViewThemeUtils? ): io.reactivex.disposables.Disposable { - val imageRequestUri = ApiUtils.getUrlForConversationAvatarWithVersion( - 1, - user.baseUrl, - conversation.token, - DisplayUtils.isDarkModeOn(this.context), - conversation.avatarVersion - ) + val imageRequestUri = + ApiUtils.getUrlForConversationAvatarWithVersion( + 1, + user.baseUrl, + conversation.token, + DisplayUtils.isDarkModeOn(this.context), + conversation.avatarVersion + ) if (conversation.avatarVersion.isNullOrEmpty() && viewThemeUtils != null) { when (conversation.type) { @@ -117,11 +118,12 @@ fun ImageView.loadUserAvatar( requestBigSize: Boolean = true, ignoreCache: Boolean ): io.reactivex.disposables.Disposable { - val imageRequestUri = ApiUtils.getUrlForAvatar( - user.baseUrl, - avatarId, - requestBigSize - ) + val imageRequestUri = + ApiUtils.getUrlForAvatar( + user.baseUrl, + avatarId, + requestBigSize + ) return loadAvatarInternal(user, imageRequestUri, ignoreCache, null) } @@ -133,11 +135,12 @@ private fun ImageView.loadAvatarInternal( ignoreCache: Boolean, errorPlaceholder: Drawable? ): io.reactivex.disposables.Disposable { - val cachePolicy = if (ignoreCache) { - CachePolicy.WRITE_ONLY - } else { - CachePolicy.ENABLED - } + val cachePolicy = + if (ignoreCache) { + CachePolicy.WRITE_ONLY + } else { + CachePolicy.ENABLED + } if (ignoreCache && this.result is SuccessResult) { val result = this.result as SuccessResult @@ -176,11 +179,12 @@ fun ImageView.loadAvatarWithUrl(user: User? = null, url: String): io.reactivex.d } fun ImageView.loadThumbnail(url: String, user: User): io.reactivex.disposables.Disposable { - val requestBuilder = ImageRequest.Builder(context) - .data(url) - .crossfade(true) - .target(this) - .transformations(CircleCropTransformation()) + val requestBuilder = + ImageRequest.Builder(context) + .data(url) + .crossfade(true) + .target(this) + .transformations(CircleCropTransformation()) if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val layers = arrayOfNulls(2) @@ -209,13 +213,21 @@ fun ImageView.loadImage(url: String, user: User, placeholder: Drawable? = null): finalPlaceholder = ContextCompat.getDrawable(context!!, R.drawable.ic_mimetype_file) } - val requestBuilder = ImageRequest.Builder(context) - .data(url) - .crossfade(true) - .target(this) - .placeholder(finalPlaceholder) - .error(finalPlaceholder) - .transformations(RoundedCornersTransformation(ROUNDING_PIXEL, ROUNDING_PIXEL, ROUNDING_PIXEL, ROUNDING_PIXEL)) + val requestBuilder = + ImageRequest.Builder(context) + .data(url) + .crossfade(true) + .target(this) + .placeholder(finalPlaceholder) + .error(finalPlaceholder) + .transformations( + RoundedCornersTransformation( + ROUNDING_PIXEL, + ROUNDING_PIXEL, + ROUNDING_PIXEL, + ROUNDING_PIXEL + ) + ) if (url.startsWith(user.baseUrl!!) && (url.contains("index.php/core/preview") || url.contains("/avatar/")) @@ -250,15 +262,16 @@ fun ImageView.loadUserAvatar(any: Any?): io.reactivex.disposables.Disposable { } fun ImageView.loadSystemAvatar(): io.reactivex.disposables.Disposable { - val data: Any = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val layers = arrayOfNulls(2) - layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background) - layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground) - val layerDrawable = LayerDrawable(layers) - layerDrawable - } else { - R.mipmap.ic_launcher - } + val data: Any = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val layers = arrayOfNulls(2) + layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background) + layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_foreground) + val layerDrawable = LayerDrawable(layers) + layerDrawable + } else { + R.mipmap.ic_launcher + } return DisposableWrapper( load(data) { @@ -268,15 +281,16 @@ fun ImageView.loadSystemAvatar(): io.reactivex.disposables.Disposable { } fun ImageView.loadNoteToSelfAvatar(): io.reactivex.disposables.Disposable { - val data: Any = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - val layers = arrayOfNulls(2) - layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background) - layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_note_to_self) - val layerDrawable = LayerDrawable(layers) - layerDrawable - } else { - R.mipmap.ic_launcher - } + val data: Any = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + val layers = arrayOfNulls(2) + layers[0] = ContextCompat.getDrawable(context, R.drawable.ic_launcher_background) + layers[1] = ContextCompat.getDrawable(context, R.drawable.ic_note_to_self) + val layerDrawable = LayerDrawable(layers) + layerDrawable + } else { + R.mipmap.ic_launcher + } return DisposableWrapper( load(data) { @@ -303,29 +317,32 @@ fun ImageView.loadBotsAvatar(): io.reactivex.disposables.Disposable { } fun ImageView.loadDefaultGroupCallAvatar(viewThemeUtils: ViewThemeUtils): io.reactivex.disposables.Disposable { - val data: Any = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.ic_avatar_group) as Any - } else { - R.drawable.ic_circular_group - } + val data: Any = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.ic_avatar_group) as Any + } else { + R.drawable.ic_circular_group + } return loadUserAvatar(data) } fun ImageView.loadDefaultPublicCallAvatar(viewThemeUtils: ViewThemeUtils): io.reactivex.disposables.Disposable { - val data: Any = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.ic_avatar_link) as Any - } else { - R.drawable.ic_circular_link - } + val data: Any = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.ic_avatar_link) as Any + } else { + R.drawable.ic_circular_link + } return loadUserAvatar(data) } fun ImageView.loadMailAvatar(viewThemeUtils: ViewThemeUtils): io.reactivex.disposables.Disposable { - val data: Any = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.ic_avatar_mail) as Any - } else { - R.drawable.ic_circular_mail - } + val data: Any = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { + viewThemeUtils.talk.themePlaceholderAvatar(this, R.drawable.ic_avatar_mail) as Any + } else { + R.drawable.ic_circular_mail + } return loadUserAvatar(data) } @@ -334,11 +351,12 @@ fun ImageView.loadGuestAvatar(user: User, name: String, big: Boolean): io.reacti } fun ImageView.loadGuestAvatar(baseUrl: String, name: String, big: Boolean): io.reactivex.disposables.Disposable { - val imageRequestUri = ApiUtils.getUrlForGuestAvatar( - baseUrl, - name, - big - ) + val imageRequestUri = + ApiUtils.getUrlForGuestAvatar( + baseUrl, + name, + big + ) return DisposableWrapper( load(imageRequestUri) { transformations(CircleCropTransformation()) @@ -349,9 +367,9 @@ fun ImageView.loadGuestAvatar(baseUrl: String, name: String, big: Boolean): io.r ) } -private class DisposableWrapper(private val disposable: coil.request.Disposable) : io.reactivex.disposables - .Disposable { - +private class DisposableWrapper( + private val disposable: coil.request.Disposable +) : io.reactivex.disposables.Disposable { override fun dispose() { disposable.dispose() } diff --git a/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenImageActivity.kt b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenImageActivity.kt index ccdfec5a93b..69cb331e42f 100644 --- a/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenImageActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenImageActivity.kt @@ -74,27 +74,30 @@ class FullScreenImageActivity : AppCompatActivity() { } R.id.share -> { - val shareUri = FileProvider.getUriForFile( - this, - BuildConfig.APPLICATION_ID, - File(path) - ) - - val shareIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_STREAM, shareUri) - type = IMAGE_PREFIX_GENERIC - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - } + val shareUri = + FileProvider.getUriForFile( + this, + BuildConfig.APPLICATION_ID, + File(path) + ) + + val shareIntent: Intent = + Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, shareUri) + type = IMAGE_PREFIX_GENERIC + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.send_to))) true } R.id.save -> { - val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance( - intent.getStringExtra("FILE_NAME").toString() - ) + val saveFragment: DialogFragment = + SaveToStorageDialogFragment.newInstance( + intent.getStringExtra("FILE_NAME").toString() + ) saveFragment.show( supportFragmentManager, SaveToStorageDialogFragment.TAG diff --git a/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt index 1d6796b5224..7ca6c7da843 100644 --- a/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenMediaActivity.kt @@ -82,27 +82,30 @@ class FullScreenMediaActivity : AppCompatActivity() { } R.id.share -> { - val shareUri = FileProvider.getUriForFile( - this, - BuildConfig.APPLICATION_ID, - File(path) - ) - - val shareIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_STREAM, shareUri) - type = VIDEO_PREFIX_GENERIC - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - } + val shareUri = + FileProvider.getUriForFile( + this, + BuildConfig.APPLICATION_ID, + File(path) + ) + + val shareIntent: Intent = + Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, shareUri) + type = VIDEO_PREFIX_GENERIC + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.send_to))) true } R.id.save -> { - val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance( - intent.getStringExtra("FILE_NAME").toString() - ) + val saveFragment: DialogFragment = + SaveToStorageDialogFragment.newInstance( + intent.getStringExtra("FILE_NAME").toString() + ) saveFragment.show( supportFragmentManager, SaveToStorageDialogFragment.TAG @@ -202,30 +205,32 @@ class FullScreenMediaActivity : AppCompatActivity() { windowInsetsController.show(WindowInsetsCompat.Type.systemBars()) supportActionBar?.show() } + private fun applyWindowInsets() { val playerView = binding.playerView val exoControls = playerView.findViewById(R.id.exo_bottom_bar) val exoProgress = playerView.findViewById(R.id.exo_progress) val progressBottomMargin = exoProgress.marginBottom - ViewCompat.setOnApplyWindowInsetsListener(binding.root) { _, windowInsets -> - val insets = windowInsets.getInsets( - WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type - .displayCutout() - ) - binding.mediaviewToolbar.updateLayoutParams { - topMargin = insets.top - } - exoControls.updateLayoutParams { - bottomMargin = insets.bottom - } - exoProgress.updateLayoutParams { - bottomMargin = insets.bottom + progressBottomMargin + ViewCompat + .setOnApplyWindowInsetsListener(binding.root) { _, windowInsets -> + val insets = + windowInsets.getInsets( + WindowInsetsCompat.Type.systemBars() or WindowInsetsCompat.Type.displayCutout() + ) + binding.mediaviewToolbar.updateLayoutParams { + topMargin = insets.top + } + exoControls.updateLayoutParams { + bottomMargin = insets.bottom + } + exoProgress.updateLayoutParams { + bottomMargin = insets.bottom + progressBottomMargin + } + exoControls.updatePadding(left = insets.left, right = insets.right) + exoProgress.updatePadding(left = insets.left, right = insets.right) + binding.mediaviewToolbar.updatePadding(left = insets.left, right = insets.right) + WindowInsetsCompat.CONSUMED } - exoControls.updatePadding(left = insets.left, right = insets.right) - exoProgress.updatePadding(left = insets.left, right = insets.right) - binding.mediaviewToolbar.updatePadding(left = insets.left, right = insets.right) - WindowInsetsCompat.CONSUMED - } } } diff --git a/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenTextViewerActivity.kt b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenTextViewerActivity.kt index d78ff54c384..1b49fb0f76e 100644 --- a/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenTextViewerActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/fullscreenfile/FullScreenTextViewerActivity.kt @@ -67,27 +67,31 @@ class FullScreenTextViewerActivity : AppCompatActivity() { } R.id.share -> { - val shareUri = FileProvider.getUriForFile( - this, - BuildConfig.APPLICATION_ID, - File(path) - ) - - val shareIntent: Intent = Intent().apply { - action = Intent.ACTION_SEND - putExtra(Intent.EXTRA_STREAM, shareUri) - type = TEXT_PREFIX_GENERIC - addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) - } + val shareUri = + FileProvider.getUriForFile( + this, + BuildConfig.APPLICATION_ID, + File(path) + ) + + val shareIntent: Intent = + Intent().apply { + action = Intent.ACTION_SEND + putExtra(Intent.EXTRA_STREAM, shareUri) + type = TEXT_PREFIX_GENERIC + addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION) + } startActivity(Intent.createChooser(shareIntent, resources.getText(R.string.send_to))) true } R.id.save -> { - val saveFragment: DialogFragment = SaveToStorageDialogFragment.newInstance( - intent.getStringExtra("FILE_NAME").toString() - ) + val saveFragment: DialogFragment = + SaveToStorageDialogFragment + .newInstance( + intent.getStringExtra("FILE_NAME").toString() + ) saveFragment.show( supportFragmentManager, SaveToStorageDialogFragment.TAG diff --git a/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt b/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt index 4544679d291..3e7232b3846 100644 --- a/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt +++ b/app/src/main/java/com/nextcloud/talk/interfaces/ClosedInterface.kt @@ -24,6 +24,8 @@ package com.nextcloud.talk.interfaces interface ClosedInterface { val isGooglePlayServicesAvailable: Boolean + fun providerInstallerInstallIfNeededAsync() + fun setUpPushTokenRegistration() } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt index c55949eecc3..911fd3a115c 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/ContactAddressBookWorker.kt @@ -64,7 +64,6 @@ import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) class ContactAddressBookWorker(val context: Context, workerParameters: WorkerParameters) : Worker(context, workerParameters) { - @Inject lateinit var ncApi: NcApi @@ -134,38 +133,41 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onComplete() { - // unused atm - } + .subscribe( + object : Observer { + override fun onComplete() { + // unused atm + } - override fun onSubscribe(d: Disposable) { - // unused atm - } + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(foundContacts: ContactsByNumberOverall) { - when (foundContacts.ocs?.meta?.statusCode) { - HTTP_CODE_TOO_MANY_REQUESTS -> { - Toast.makeText( - context, - context.resources.getString( - R.string.nc_settings_phone_book_integration_phone_number_dialog_429 - ), - Toast.LENGTH_SHORT - ).show() - } - else -> { - val contactsWithAssociatedPhoneNumbers = foundContacts.ocs!!.map - deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers) - createLinkedAccounts(contactsWithAssociatedPhoneNumbers) + override fun onNext(foundContacts: ContactsByNumberOverall) { + when (foundContacts.ocs?.meta?.statusCode) { + HTTP_CODE_TOO_MANY_REQUESTS -> { + Toast.makeText( + context, + context.resources.getString( + R.string.nc_settings_phone_book_integration_phone_number_dialog_429 + ), + Toast.LENGTH_SHORT + ).show() + } + + else -> { + val contactsWithAssociatedPhoneNumbers = foundContacts.ocs!!.map + deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers) + createLinkedAccounts(contactsWithAssociatedPhoneNumbers) + } } } - } - override fun onError(e: Throwable) { - Log.e(javaClass.simpleName, "Failed to searchContactsByPhoneNumber", e) + override fun onError(e: Throwable) { + Log.e(javaClass.simpleName, "Failed to searchContactsByPhoneNumber", e) + } } - }) + ) } // store timestamp @@ -177,13 +179,14 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar private fun collectContactsWithPhoneNumbersFromDevice(): MutableMap> { val deviceContactsWithNumbers: MutableMap> = mutableMapOf() - val contactCursor = context.contentResolver.query( - ContactsContract.Contacts.CONTENT_URI, - null, - null, - null, - null - ) + val contactCursor = + context.contentResolver.query( + ContactsContract.Contacts.CONTENT_URI, + null, + null, + null, + null + ) if (contactCursor != null) { if (contactCursor.count > 0) { @@ -206,39 +209,44 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar private fun deleteLinkedAccounts(contactsWithAssociatedPhoneNumbers: Map?) { Log.d(TAG, "deleteLinkedAccount") + fun deleteLinkedAccount(id: String) { - val rawContactUri = ContactsContract.RawContacts.CONTENT_URI + val rawContactUri = + ContactsContract.RawContacts.CONTENT_URI + .buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") + .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) + .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) + .build() + val count = + context.contentResolver.delete( + rawContactUri, + ContactsContract.RawContacts.CONTACT_ID + " " + "LIKE \"" + id + "\"", + null + ) + Log.d(TAG, "deleted $count linked accounts for id $id") + } + + val rawContactUri = + ContactsContract.Data.CONTENT_URI .buildUpon() .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) + .appendQueryParameter( + ContactsContract.Data.MIMETYPE, + "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat" + ) .build() - val count = context.contentResolver.delete( + + val rawContactsCursor = + context.contentResolver.query( rawContactUri, - ContactsContract.RawContacts.CONTACT_ID + " " + "LIKE \"" + id + "\"", + null, + null, + null, null ) - Log.d(TAG, "deleted $count linked accounts for id $id") - } - - val rawContactUri = ContactsContract.Data.CONTENT_URI - .buildUpon() - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") - .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) - .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) - .appendQueryParameter( - ContactsContract.Data.MIMETYPE, - "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat" - ) - .build() - - val rawContactsCursor = context.contentResolver.query( - rawContactUri, - null, - null, - null, - null - ) if (rawContactsCursor != null) { if (rawContactsCursor.count > 0) { @@ -275,24 +283,26 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar " = ?" val params = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id) - val rawContactUri = ContactsContract.Data.CONTENT_URI - .buildUpon() - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") - .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) - .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) - .appendQueryParameter( - ContactsContract.Data.MIMETYPE, - "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat" + val rawContactUri = + ContactsContract.Data.CONTENT_URI + .buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") + .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) + .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) + .appendQueryParameter( + ContactsContract.Data.MIMETYPE, + "vnd.android.cursor.item/vnd.com.nextcloud.talk2.chat" + ) + .build() + + val rawContactsCursor = + context.contentResolver.query( + rawContactUri, + null, + where, + params, + null ) - .build() - - val rawContactsCursor = context.contentResolver.query( - rawContactUri, - null, - where, - params, - null - ) if (rawContactsCursor != null) { if (rawContactsCursor.count > 0) { @@ -309,13 +319,14 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar fun createLinkedAccount(lookupKey: String, cloudId: String) { val lookupUri = Uri.withAppendedPath(ContactsContract.Contacts.CONTENT_LOOKUP_URI, lookupKey) val lookupContactUri = ContactsContract.Contacts.lookupContact(context.contentResolver, lookupUri) - val contactCursor = context.contentResolver.query( - lookupContactUri, - null, - null, - null, - null - ) + val contactCursor = + context.contentResolver.query( + lookupContactUri, + null, + null, + null, + null + ) if (contactCursor != null) { if (contactCursor.count > 0) { @@ -423,13 +434,14 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar private fun getPhoneNumbersFromDeviceContact(id: String?): MutableList { val numbers = mutableListOf() - val phonesNumbersCursor = context.contentResolver.query( - ContactsContract.CommonDataKinds.Phone.CONTENT_URI, - null, - ContactsContract.Data.CONTACT_ID + " = " + id, - null, - null - ) + val phonesNumbersCursor = + context.contentResolver.query( + ContactsContract.CommonDataKinds.Phone.CONTENT_URI, + null, + ContactsContract.Data.CONTACT_ID + " = " + id, + null, + null + ) if (phonesNumbersCursor != null) { while (phonesNumbersCursor.moveToNext()) { @@ -448,12 +460,13 @@ class ContactAddressBookWorker(val context: Context, workerParameters: WorkerPar } fun deleteAllLinkedAccounts() { - val rawContactUri = ContactsContract.RawContacts.CONTENT_URI - .buildUpon() - .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") - .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) - .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) - .build() + val rawContactUri = + ContactsContract.RawContacts.CONTENT_URI + .buildUpon() + .appendQueryParameter(ContactsContract.CALLER_IS_SYNCADAPTER, "true") + .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_NAME, accountName) + .appendQueryParameter(ContactsContract.RawContacts.ACCOUNT_TYPE, accountType) + .build() context.contentResolver.delete(rawContactUri, null, null) Log.d(TAG, "deleted all linked accounts") } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/DownloadFileToCacheWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/DownloadFileToCacheWorker.kt index 29ca6750aa0..143c56fde6f 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/DownloadFileToCacheWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/DownloadFileToCacheWorker.kt @@ -89,11 +89,9 @@ class DownloadFileToCacheWorker(val context: Context, workerParameters: WorkerPa } private fun downloadFile(currentUser: User, url: String, fileName: String): Result { - val downloadCall = ncApi.downloadFile( - ApiUtils.getCredentials(currentUser.username, currentUser.token), - url - ) - + val downloadCall = + ncApi + .downloadFile(ApiUtils.getCredentials(currentUser.username, currentUser.token), url) return executeDownload(downloadCall.execute().body(), fileName) } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt index 74e57b4ff57..c16d2efedf2 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/NotificationWorker.kt @@ -205,16 +205,17 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val requestCode = System.currentTimeMillis().toInt() - val fullScreenPendingIntent = PendingIntent.getActivity( - context, - requestCode, - fullScreenIntent, - if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - } else { - PendingIntent.FLAG_UPDATE_CURRENT - } - ) + val fullScreenPendingIntent = + PendingIntent.getActivity( + context, + requestCode, + fullScreenIntent, + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_IMMUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } + ) val soundUri = getCallRingtoneUri(applicationContext, appPreferences) val notificationChannelId = NotificationUtils.NotificationChannels.NOTIFICATION_CHANNEL_CALLS_V4.name @@ -246,19 +247,21 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor } private fun initNcApiAndCredentials() { - credentials = ApiUtils.getCredentials( - signatureVerification.user!!.username, - signatureVerification.user!!.token - ) - ncApi = retrofit!!.newBuilder().client( - okHttpClient!!.newBuilder().cookieJar( - JavaNetCookieJar( - CookieManager() - ) - ).build() - ).build().create( - NcApi::class.java - ) + credentials = + ApiUtils.getCredentials( + signatureVerification.user!!.username, + signatureVerification.user!!.token + ) + ncApi = + retrofit!!.newBuilder().client( + okHttpClient!!.newBuilder().cookieJar( + JavaNetCookieJar( + CookieManager() + ) + ).build() + ).build().create( + NcApi::class.java + ) } @Suppress("TooGenericExceptionCaught", "NestedBlockDepth", "ComplexMethod", "LongMethod") @@ -271,19 +274,21 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val pushUtils = PushUtils() val privateKey = pushUtils.readKeyFromFile(false) as PrivateKey try { - signatureVerification = pushUtils.verifySignature( - base64DecodedSignature, - base64DecodedSubject - ) + signatureVerification = + pushUtils.verifySignature( + base64DecodedSignature, + base64DecodedSubject + ) if (signatureVerification.signatureValid) { val cipher = Cipher.getInstance("RSA/None/PKCS1Padding") cipher.init(Cipher.DECRYPT_MODE, privateKey) val decryptedSubject = cipher.doFinal(base64DecodedSubject) - pushMessage = LoganSquare.parse( - String(decryptedSubject), - DecryptedPushMessage::class.java - ) + pushMessage = + LoganSquare.parse( + String(decryptedSubject), + DecryptedPushMessage::class.java + ) } } catch (e: NoSuchAlgorithmException) { Log.e(TAG, "No proper algorithm to decrypt the message ", e) @@ -310,32 +315,34 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor (pushMessage.notificationId!!).toString() ) ) - .blockingSubscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .blockingSubscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(notificationOverall: NotificationOverall) { - val ncNotification = notificationOverall.ocs!!.notification - if (ncNotification != null) { - enrichPushMessageByNcNotificationData(ncNotification) - showNotification(intent, ncNotification) + override fun onNext(notificationOverall: NotificationOverall) { + val ncNotification = notificationOverall.ocs!!.notification + if (ncNotification != null) { + enrichPushMessageByNcNotificationData(ncNotification) + showNotification(intent, ncNotification) + } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to get NC notification", e) - if (BuildConfig.DEBUG) { - Handler(Looper.getMainLooper()).post { - Toast.makeText(context, "Failed to get NC notification", Toast.LENGTH_LONG).show() + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to get NC notification", e) + if (BuildConfig.DEBUG) { + Handler(Looper.getMainLooper()).post { + Toast.makeText(context, "Failed to get NC notification", Toast.LENGTH_LONG).show() + } } } - } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun enrichPushMessageByNcNotificationData( @@ -347,10 +354,11 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor if (ncNotification.messageRichParameters != null && ncNotification.messageRichParameters!!.size > 0 ) { - pushMessage.text = getParsedMessage( - ncNotification.messageRich, - ncNotification.messageRichParameters - ) + pushMessage.text = + getParsedMessage( + ncNotification.messageRich, + ncNotification.messageRichParameters + ) } else { pushMessage.text = ncNotification.message } @@ -409,11 +417,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor // Use unique request code to make sure that a new PendingIntent gets created for each notification // See https://github.com/nextcloud/talk-android/issues/2111 val requestCode = System.currentTimeMillis().toInt() - val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_MUTABLE - } else { - 0 - } + val intentFlag: Int = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_MUTABLE + } else { + 0 + } val pendingIntent = PendingIntent.getActivity(context, requestCode, intent, intentFlag) val uri = Uri.parse(signatureVerification.user!!.baseUrl) val baseUrl = uri.host @@ -430,19 +439,20 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val autoCancelOnClick = TYPE_RECORDING != pushMessage.type - val notificationBuilder = NotificationCompat.Builder(context!!, "1") - .setPriority(NotificationCompat.PRIORITY_HIGH) - .setCategory(category) - .setLargeIcon(getLargeIcon()) - .setSmallIcon(R.drawable.ic_logo) - .setContentTitle(contentTitle) - .setContentText(contentText) - .setSubText(baseUrl) - .setWhen(pushMessage.timestamp) - .setShowWhen(true) - .setContentIntent(pendingIntent) - .setAutoCancel(autoCancelOnClick) - .setColor(context!!.resources.getColor(R.color.colorPrimary, null)) + val notificationBuilder = + NotificationCompat.Builder(context!!, "1") + .setPriority(NotificationCompat.PRIORITY_HIGH) + .setCategory(category) + .setLargeIcon(getLargeIcon()) + .setSmallIcon(R.drawable.ic_logo) + .setContentTitle(contentTitle) + .setContentText(contentText) + .setSubText(baseUrl) + .setWhen(pushMessage.timestamp) + .setShowWhen(true) + .setContentIntent(pendingIntent) + .setAutoCancel(autoCancelOnClick) + .setColor(context!!.resources.getColor(R.color.colorPrimary, null)) val notificationInfoBundle = Bundle() notificationInfoBundle.putLong(KEY_INTERNAL_USER_ID, signatureVerification.user!!.id!!) @@ -472,11 +482,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor notificationBuilder.setContentIntent(pendingIntent) val groupName = signatureVerification.user!!.id.toString() + "@" + pushMessage.id notificationBuilder.setGroup(calculateCRC32(groupName).toString()) - val activeStatusBarNotification = findNotificationForRoom( - context, - signatureVerification.user!!, - pushMessage.id!! - ) + val activeStatusBarNotification = + findNotificationForRoom( + context, + signatureVerification.user!!, + pushMessage.id!! + ) // NOTE - systemNotificationId is an internal ID used on the device only. // It is NOT the same as the notification ID used in communication with the server. @@ -509,20 +520,24 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_people_group_black_24px)?.toBitmap()!! } + "group" -> largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_people_group_black_24px)?.toBitmap()!! + "public" -> largeIcon = ContextCompat.getDrawable(context!!, R.drawable.ic_link_black_24px)?.toBitmap()!! + else -> // assuming one2one - largeIcon = if (TYPE_CHAT == pushMessage.type || TYPE_ROOM == pushMessage.type) { - ContextCompat.getDrawable(context!!, R.drawable.ic_comment)?.toBitmap()!! - } else if (TYPE_REMINDER == pushMessage.type) { - ContextCompat.getDrawable(context!!, R.drawable.ic_timer_black_24dp)?.toBitmap()!! - } else { - ContextCompat.getDrawable(context!!, R.drawable.ic_call_black_24dp)?.toBitmap()!! - } + largeIcon = + if (TYPE_CHAT == pushMessage.type || TYPE_ROOM == pushMessage.type) { + ContextCompat.getDrawable(context!!, R.drawable.ic_comment)?.toBitmap()!! + } else if (TYPE_REMINDER == pushMessage.type) { + ContextCompat.getDrawable(context!!, R.drawable.ic_timer_black_24dp)?.toBitmap()!! + } else { + ContextCompat.getDrawable(context!!, R.drawable.ic_call_black_24dp)?.toBitmap()!! + } } } return largeIcon @@ -543,27 +558,30 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor val userType = notificationUser!!.type var style: NotificationCompat.MessagingStyle? = null if (activeStatusBarNotification != null) { - style = NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification( - activeStatusBarNotification.notification - ) + style = + NotificationCompat.MessagingStyle.extractMessagingStyleFromNotification( + activeStatusBarNotification.notification + ) } - val person = Person.Builder() - .setKey(signatureVerification.user!!.id.toString() + "@" + notificationUser.id) - .setName(EmojiCompat.get().process(notificationUser.name!!)) - .setBot("bot" == userType) + val person = + Person.Builder() + .setKey(signatureVerification.user!!.id.toString() + "@" + notificationUser.id) + .setName(EmojiCompat.get().process(notificationUser.name!!)) + .setBot("bot" == userType) notificationBuilder.setOnlyAlertOnce(true) if ("user" == userType || "guest" == userType) { val baseUrl = signatureVerification.user!!.baseUrl - val avatarUrl = if ("user" == userType) { - ApiUtils.getUrlForAvatar( - baseUrl, - notificationUser.id, - false - ) - } else { - ApiUtils.getUrlForGuestAvatar(baseUrl, notificationUser.name, false) - } + val avatarUrl = + if ("user" == userType) { + ApiUtils.getUrlForAvatar( + baseUrl, + notificationUser.id, + false + ) + } else { + ApiUtils.getUrlForGuestAvatar(baseUrl, notificationUser.name, false) + } person.setIcon(loadAvatarSync(avatarUrl, context!!)) } notificationBuilder.setStyle(getStyle(person.build(), style)) @@ -579,57 +597,64 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor actualIntent.putExtra(KEY_ROOM_TOKEN, pushMessage.id) actualIntent.putExtra(KEY_MESSAGE_ID, messageId) - val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - } else { - PendingIntent.FLAG_UPDATE_CURRENT - } + val intentFlag: Int = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } return PendingIntent.getBroadcast(context, systemNotificationId, actualIntent, intentFlag) } private fun addMarkAsReadAction(notificationBuilder: NotificationCompat.Builder, systemNotificationId: Int) { if (pushMessage.objectId != null) { - val messageId: Int = try { - parseMessageId(pushMessage.objectId!!) - } catch (nfe: NumberFormatException) { - Log.e(TAG, "Failed to parse messageId from objectId, skip adding mark-as-read action.", nfe) - return - } + val messageId: Int = + try { + parseMessageId(pushMessage.objectId!!) + } catch (nfe: NumberFormatException) { + Log.e(TAG, "Failed to parse messageId from objectId, skip adding mark-as-read action.", nfe) + return + } - val pendingIntent = buildIntentForAction( - MarkAsReadReceiver::class.java, - systemNotificationId, - messageId - ) - val markAsReadAction = NotificationCompat.Action.Builder( - R.drawable.ic_eye, - context!!.resources.getString(R.string.nc_mark_as_read), - pendingIntent - ) - .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ) - .setShowsUserInterface(false) - .build() + val pendingIntent = + buildIntentForAction( + MarkAsReadReceiver::class.java, + systemNotificationId, + messageId + ) + val markAsReadAction = + NotificationCompat.Action.Builder( + R.drawable.ic_eye, + context!!.resources.getString(R.string.nc_mark_as_read), + pendingIntent + ) + .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_MARK_AS_READ) + .setShowsUserInterface(false) + .build() notificationBuilder.addAction(markAsReadAction) } } private fun addReplyAction(notificationBuilder: NotificationCompat.Builder, systemNotificationId: Int) { val replyLabel = context!!.resources.getString(R.string.nc_reply) - val remoteInput = RemoteInput.Builder(NotificationUtils.KEY_DIRECT_REPLY) - .setLabel(replyLabel) - .build() - - val replyPendingIntent = buildIntentForAction( - DirectReplyReceiver::class.java, - systemNotificationId, - 0 - ) - val replyAction = NotificationCompat.Action.Builder(R.drawable.ic_reply, replyLabel, replyPendingIntent) - .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) - .setShowsUserInterface(false) - .setAllowGeneratedReplies(true) - .addRemoteInput(remoteInput) - .build() + val remoteInput = + RemoteInput.Builder(NotificationUtils.KEY_DIRECT_REPLY) + .setLabel(replyLabel) + .build() + + val replyPendingIntent = + buildIntentForAction( + DirectReplyReceiver::class.java, + systemNotificationId, + 0 + ) + val replyAction = + NotificationCompat.Action.Builder(R.drawable.ic_reply, replyLabel, replyPendingIntent) + .setSemanticAction(NotificationCompat.Action.SEMANTIC_ACTION_REPLY) + .setShowsUserInterface(false) + .setAllowGeneratedReplies(true) + .addRemoteInput(remoteInput) + .build() notificationBuilder.addAction(replyAction) } @@ -652,17 +677,19 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor dismissIntent.putExtra(KEY_SYSTEM_NOTIFICATION_ID, systemNotificationId) dismissIntent.putExtra(KEY_DISMISS_RECORDING_URL, dismissRecordingUrl) - val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - } else { - PendingIntent.FLAG_UPDATE_CURRENT - } + val intentFlag: Int = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } val dismissPendingIntent = PendingIntent.getBroadcast(context, systemNotificationId, dismissIntent, intentFlag) - val dismissAction = NotificationCompat.Action.Builder(R.drawable.ic_delete, dismissLabel, dismissPendingIntent) - .setShowsUserInterface(false) - .setAllowGeneratedReplies(true) - .build() + val dismissAction = + NotificationCompat.Action.Builder(R.drawable.ic_delete, dismissLabel, dismissPendingIntent) + .setShowsUserInterface(false) + .setAllowGeneratedReplies(true) + .build() notificationBuilder.addAction(dismissAction) } @@ -686,26 +713,30 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor shareRecordingIntent.putExtra(KEY_SHARE_RECORDING_TO_CHAT_URL, shareToChatUrl) shareRecordingIntent.putExtra(KEY_ROOM_TOKEN, pushMessage.id) - val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT - } else { - PendingIntent.FLAG_UPDATE_CURRENT - } - val shareRecordingPendingIntent = PendingIntent.getBroadcast( - context, - systemNotificationId, - shareRecordingIntent, - intentFlag - ) + val intentFlag: Int = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_MUTABLE or PendingIntent.FLAG_UPDATE_CURRENT + } else { + PendingIntent.FLAG_UPDATE_CURRENT + } + val shareRecordingPendingIntent = + PendingIntent + .getBroadcast( + context, + systemNotificationId, + shareRecordingIntent, + intentFlag + ) - val shareRecordingAction = NotificationCompat.Action.Builder( - R.drawable.ic_delete, - shareToChatLabel, - shareRecordingPendingIntent - ) - .setShowsUserInterface(false) - .setAllowGeneratedReplies(true) - .build() + val shareRecordingAction = + NotificationCompat.Action.Builder( + R.drawable.ic_delete, + shareToChatLabel, + shareRecordingPendingIntent + ) + .setShowsUserInterface(false) + .setAllowGeneratedReplies(true) + .build() notificationBuilder.addAction(shareRecordingAction) } @@ -795,10 +826,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor var hasParticipantsInCall = true var inCallOnDifferentDevice = false - val apiVersion = ApiUtils.getConversationApiVersion( - signatureVerification.user, - intArrayOf(ApiUtils.APIv4, 1) - ) + val apiVersion = + ApiUtils + .getConversationApiVersion( + signatureVerification.user, + intArrayOf(ApiUtils.APIv4, 1) + ) var isCallNotificationVisible = true @@ -816,73 +849,81 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor .takeWhile { isCallNotificationVisible && hasParticipantsInCall && !inCallOnDifferentDevice } } .subscribeOn(Schedulers.io()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) = Unit - - override fun onNext(participantsOverall: ParticipantsOverall) { - val participantList: List = participantsOverall.ocs!!.data!! - hasParticipantsInCall = participantList.isNotEmpty() - if (hasParticipantsInCall) { - for (participant in participantList) { - if (participant.actorId == signatureVerification.user!!.userId && - participant.actorType == Participant.ActorType.USERS - ) { - inCallOnDifferentDevice = true - break + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) = Unit + + override fun onNext(participantsOverall: ParticipantsOverall) { + val participantList: List = participantsOverall.ocs!!.data!! + hasParticipantsInCall = participantList.isNotEmpty() + if (hasParticipantsInCall) { + for (participant in participantList) { + if (participant.actorId == signatureVerification.user!!.userId && + participant.actorType == Participant.ActorType.USERS + ) { + inCallOnDifferentDevice = true + break + } } } - } - if (inCallOnDifferentDevice) { - Log.d(TAG, "inCallOnDifferentDevice is true") - removeNotification(pushMessage.timestamp.toInt()) + if (inCallOnDifferentDevice) { + Log.d(TAG, "inCallOnDifferentDevice is true") + removeNotification(pushMessage.timestamp.toInt()) + } + + if (!hasParticipantsInCall) { + showMissedCallNotification() + Log.d(TAG, "no participants in call") + removeNotification(pushMessage.timestamp.toInt()) + } + + isCallNotificationVisible = + NotificationUtils + .isNotificationVisible( + context, + pushMessage.timestamp.toInt() + ) } - if (!hasParticipantsInCall) { - showMissedCallNotification() - Log.d(TAG, "no participants in call") + override fun onError(e: Throwable) { + Log.e(TAG, "Error in getPeersForCall", e) + if (isCallNotificationVisible) { + showMissedCallNotification() + } removeNotification(pushMessage.timestamp.toInt()) } - isCallNotificationVisible = NotificationUtils.isNotificationVisible( - context, - pushMessage.timestamp.toInt() - ) - } - - override fun onError(e: Throwable) { - Log.e(TAG, "Error in getPeersForCall", e) - if (isCallNotificationVisible) { - showMissedCallNotification() - } - removeNotification(pushMessage.timestamp.toInt()) - } + override fun onComplete() { + if (isCallNotificationVisible) { + // this state can be reached when call timeout is reached. + showMissedCallNotification() + } - override fun onComplete() { - if (isCallNotificationVisible) { - // this state can be reached when call timeout is reached. - showMissedCallNotification() + removeNotification(pushMessage.timestamp.toInt()) } - - removeNotification(pushMessage.timestamp.toInt()) } - }) + ) } fun showMissedCallNotification() { - val isOngoingCallNotificationVisible = NotificationUtils.isNotificationVisible( - context, - pushMessage.timestamp.toInt() - ) + val isOngoingCallNotificationVisible = + NotificationUtils + .isNotificationVisible( + context, + pushMessage.timestamp.toInt() + ) if (isOngoingCallNotificationVisible) { - val apiVersion = ApiUtils.getConversationApiVersion( - signatureVerification.user, - intArrayOf( - ApiUtils.APIv4, - ApiUtils.APIv3, - 1 - ) - ) + val apiVersion = + ApiUtils + .getConversationApiVersion( + signatureVerification.user, + intArrayOf( + ApiUtils.APIv4, + ApiUtils.APIv3, + 1 + ) + ) ncApi.getRoom( credentials, ApiUtils.getUrlForRoom( @@ -894,60 +935,65 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor .subscribeOn(Schedulers.io()) .retry(GET_ROOM_RETRY_COUNT) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(roomOverall: RoomOverall) { - val currentConversation = roomOverall.ocs!!.data - val notificationBuilder: NotificationCompat.Builder? - - notificationBuilder = NotificationCompat.Builder( - context!!, - NotificationUtils.NotificationChannels - .NOTIFICATION_CHANNEL_MESSAGES_V4.name - ) + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - val notification: Notification = notificationBuilder - .setContentTitle( - String.format( - context!!.resources.getString(R.string.nc_missed_call), - currentConversation!!.displayName - ) - ) - .setSmallIcon(R.drawable.ic_baseline_phone_missed_24) - .setOngoing(false) - .setAutoCancel(true) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setContentIntent(getIntentToOpenConversation()) - .build() - - val notificationId: Int = SystemClock.uptimeMillis().toInt() - if (ActivityCompat.checkSelfPermission( - applicationContext, - Manifest.permission.POST_NOTIFICATIONS - ) != PackageManager.PERMISSION_GRANTED - ) { - // here to request the missing permissions, and then overriding - // public void onRequestPermissionsResult(int requestCode, String[] permissions, - // int[] grantResults) - // to handle the case where the user grants the permission. See the documentation - // for ActivityCompat#requestPermissions for more details. - return + override fun onNext(roomOverall: RoomOverall) { + val currentConversation = roomOverall.ocs!!.data + val notificationBuilder: NotificationCompat.Builder? + + notificationBuilder = + NotificationCompat + .Builder( + context!!, + NotificationUtils.NotificationChannels + .NOTIFICATION_CHANNEL_MESSAGES_V4.name + ) + + val notification: Notification = + notificationBuilder + .setContentTitle( + String.format( + context!!.resources.getString(R.string.nc_missed_call), + currentConversation!!.displayName + ) + ) + .setSmallIcon(R.drawable.ic_baseline_phone_missed_24) + .setOngoing(false) + .setAutoCancel(true) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setContentIntent(getIntentToOpenConversation()) + .build() + + val notificationId: Int = SystemClock.uptimeMillis().toInt() + if (ActivityCompat.checkSelfPermission( + applicationContext, + Manifest.permission.POST_NOTIFICATIONS + ) != PackageManager.PERMISSION_GRANTED + ) { + // here to request the missing permissions, and then overriding + // public void onRequestPermissionsResult(int requestCode, String[] permissions, + // int[] grantResults) + // to handle the case where the user grants the permission. See the documentation + // for ActivityCompat#requestPermissions for more details. + return + } + notificationManager.notify(notificationId, notification) + Log.d(TAG, "'you missed a call' notification was created") } - notificationManager.notify(notificationId, notification) - Log.d(TAG, "'you missed a call' notification was created") - } - override fun onError(e: Throwable) { - Log.e(TAG, "An error occurred while fetching room for the 'missed call' notification", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "An error occurred while fetching room for the 'missed call' notification", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } } @@ -972,11 +1018,12 @@ class NotificationWorker(context: Context, workerParams: WorkerParameters) : Wor intent.putExtras(bundle) val requestCode = System.currentTimeMillis().toInt() - val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_MUTABLE - } else { - 0 - } + val intentFlag: Int = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_MUTABLE + } else { + 0 + } return PendingIntent.getActivity(context, requestCode, intent, intentFlag) } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/SaveFileToStorageWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/SaveFileToStorageWorker.kt index 777106bb336..33e3cdeeca3 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/SaveFileToStorageWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/SaveFileToStorageWorker.kt @@ -60,16 +60,17 @@ class SaveFileToStorageWorker(val context: Context, workerParameters: WorkerPara val appName = applicationContext.resources!!.getString(R.string.nc_app_product_name) - val values = ContentValues().apply { - if (mimeType.startsWith(IMAGE_PREFIX) || mimeType.startsWith(VIDEO_PREFIX)) { - put(FileColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/" + appName) - } - put(FileColumns.DISPLAY_NAME, cacheFile.name) + val values = + ContentValues().apply { + if (mimeType.startsWith(IMAGE_PREFIX) || mimeType.startsWith(VIDEO_PREFIX)) { + put(FileColumns.RELATIVE_PATH, Environment.DIRECTORY_DCIM + "/" + appName) + } + put(FileColumns.DISPLAY_NAME, cacheFile.name) - if (mimeType != null) { - put(FileColumns.MIME_TYPE, mimeType) + if (mimeType != null) { + put(FileColumns.MIME_TYPE, mimeType) + } } - } val collectionUri = getUriByType(mimeType) @@ -132,11 +133,12 @@ class SaveFileToStorageWorker(val context: Context, workerParameters: WorkerPara mimeType.startsWith(VIDEO_PREFIX) -> MediaStore.Video.Media.EXTERNAL_CONTENT_URI mimeType.startsWith(AUDIO_PREFIX) -> MediaStore.Audio.Media.EXTERNAL_CONTENT_URI mimeType.startsWith(IMAGE_PREFIX) -> MediaStore.Images.Media.EXTERNAL_CONTENT_URI - else -> if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { - Uri.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)) - } else { - MediaStore.Downloads.EXTERNAL_CONTENT_URI - } + else -> + if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) { + Uri.fromFile(Environment.getExternalStoragePublicDirectory(Environment.DIRECTORY_DOWNLOADS)) + } else { + MediaStore.Downloads.EXTERNAL_CONTENT_URI + } } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt index 106e151103a..f84029a0cb0 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/ShareOperationWorker.kt @@ -92,24 +92,23 @@ class ShareOperationWorker(context: Context, workerParams: WorkerParameters) : W companion object { private val TAG = ShareOperationWorker::class.simpleName - fun shareFile( - roomToken: String?, - currentUser: User, - remotePath: String, - metaData: String? - ) { + fun shareFile(roomToken: String?, currentUser: User, remotePath: String, metaData: String?) { val paths: MutableList = ArrayList() paths.add(remotePath) - val data = Data.Builder() - .putLong(KEY_INTERNAL_USER_ID, currentUser.id!!) - .putString(KEY_ROOM_TOKEN, roomToken) - .putStringArray(KEY_FILE_PATHS, paths.toTypedArray()) - .putString(KEY_META_DATA, metaData) - .build() - val shareWorker = OneTimeWorkRequest.Builder(ShareOperationWorker::class.java) - .setInputData(data) - .build() + val data = + Data + .Builder() + .putLong(KEY_INTERNAL_USER_ID, currentUser.id!!) + .putString(KEY_ROOM_TOKEN, roomToken) + .putStringArray(KEY_FILE_PATHS, paths.toTypedArray()) + .putString(KEY_META_DATA, metaData) + .build() + val shareWorker = + OneTimeWorkRequest + .Builder(ShareOperationWorker::class.java) + .setInputData(data) + .build() WorkManager.getInstance().enqueue(shareWorker) } } diff --git a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt index eb61a7f1e6f..8bd5200b6d0 100644 --- a/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt +++ b/app/src/main/java/com/nextcloud/talk/jobs/UploadAndShareFilesWorker.kt @@ -133,31 +133,33 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa initNotificationWithPercentage() val mimeType = context.contentResolver.getType(sourceFileUri)?.toMediaTypeOrNull() - uploadSuccess = ChunkedFileUploader( - okHttpClient, - currentUser, - roomToken, - metaData, - this - ).upload( - file, - mimeType, - remotePath - ) + uploadSuccess = + ChunkedFileUploader( + okHttpClient, + currentUser, + roomToken, + metaData, + this + ).upload( + file, + mimeType, + remotePath + ) } else { Log.d(TAG, "starting normal upload (not chunked)") - uploadSuccess = FileUploader( - context, - currentUser, - roomToken, - ncApi - ).upload( - sourceFileUri, - fileName, - remotePath, - metaData - ).blockingFirst() + uploadSuccess = + FileUploader( + context, + currentUser, + roomToken, + ncApi + ).upload( + sourceFileUri, + fileName, + remotePath, + metaData + ).blockingFirst() } if (uploadSuccess) { @@ -177,44 +179,47 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa private fun getRemotePath(currentUser: User): String { var remotePath = CapabilitiesUtilNew.getAttachmentFolder(currentUser)!! + "/" + fileName - remotePath = RemoteFileUtils.getNewPathIfFileExists( - ncApi, - currentUser, - remotePath - ) + remotePath = + RemoteFileUtils + .getNewPathIfFileExists( + ncApi, + currentUser, + remotePath + ) return remotePath } - override fun onTransferProgress( - percentage: Int - ) { - notification = mBuilder!! - .setProgress(HUNDRED_PERCENT, percentage, false) - .setContentText(getNotificationContentText(percentage)) - .build() + override fun onTransferProgress(percentage: Int) { + notification = + mBuilder!! + .setProgress(HUNDRED_PERCENT, percentage, false) + .setContentText(getNotificationContentText(percentage)) + .build() mNotifyManager!!.notify(notificationId, notification) } private fun initNotificationSetup() { mNotifyManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager - mBuilder = NotificationCompat.Builder( - context, - NotificationUtils.NotificationChannels - .NOTIFICATION_CHANNEL_UPLOADS.name - ) + mBuilder = + NotificationCompat.Builder( + context, + NotificationUtils.NotificationChannels + .NOTIFICATION_CHANNEL_UPLOADS.name + ) } private fun initNotificationWithPercentage() { - notification = mBuilder!! - .setContentTitle(context.resources.getString(R.string.nc_upload_in_progess)) - .setContentText(getNotificationContentText(ZERO_PERCENT)) - .setSmallIcon(R.drawable.upload_white) - .setOngoing(true) - .setProgress(HUNDRED_PERCENT, ZERO_PERCENT, false) - .setPriority(NotificationCompat.PRIORITY_LOW) - .setContentIntent(getIntentToOpenConversation()) - .build() + notification = + mBuilder!! + .setContentTitle(context.resources.getString(R.string.nc_upload_in_progess)) + .setContentText(getNotificationContentText(ZERO_PERCENT)) + .setSmallIcon(R.drawable.upload_white) + .setOngoing(true) + .setProgress(HUNDRED_PERCENT, ZERO_PERCENT, false) + .setPriority(NotificationCompat.PRIORITY_LOW) + .setContentIntent(getIntentToOpenConversation()) + .build() notificationId = SystemClock.uptimeMillis().toInt() mNotifyManager!!.notify(notificationId, notification) @@ -249,26 +254,30 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa intent.putExtras(bundle) val requestCode = System.currentTimeMillis().toInt() - val intentFlag: Int = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { - PendingIntent.FLAG_MUTABLE - } else { - 0 - } + val intentFlag: Int = + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) { + PendingIntent.FLAG_MUTABLE + } else { + 0 + } return PendingIntent.getActivity(context, requestCode, intent, intentFlag) } private fun showFailedToUploadNotification() { val failureTitle = context.resources.getString(R.string.nc_upload_failed_notification_title) - val failureText = String.format( - context.resources.getString(R.string.nc_upload_failed_notification_text), - fileName - ) - notification = mBuilder!! - .setContentTitle(failureTitle) - .setContentText(failureText) - .setSmallIcon(R.drawable.baseline_error_24) - .setOngoing(false) - .build() + val failureText = + String + .format( + context.resources.getString(R.string.nc_upload_failed_notification_text), + fileName + ) + notification = + mBuilder!! + .setContentTitle(failureTitle) + .setContentText(failureText) + .setSmallIcon(R.drawable.baseline_error_24) + .setOngoing(false) + .build() // Cancel original notification mNotifyManager?.cancel(notificationId) @@ -302,6 +311,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa REQUEST_PERMISSION ) } + Build.VERSION.SDK_INT > Build.VERSION_CODES.Q -> { activity.requestPermissions( arrayOf( @@ -310,6 +320,7 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa REQUEST_PERMISSION ) } + else -> { activity.requestPermissions( arrayOf( @@ -321,21 +332,20 @@ class UploadAndShareFilesWorker(val context: Context, workerParameters: WorkerPa } } - fun upload( - fileUri: String, - roomToken: String, - conversationName: String, - metaData: String? - ) { - val data: Data = Data.Builder() - .putString(DEVICE_SOURCE_FILE, fileUri) - .putString(ROOM_TOKEN, roomToken) - .putString(CONVERSATION_NAME, conversationName) - .putString(META_DATA, metaData) - .build() - val uploadWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(UploadAndShareFilesWorker::class.java) - .setInputData(data) - .build() + fun upload(fileUri: String, roomToken: String, conversationName: String, metaData: String?) { + val data: Data = + Data + .Builder() + .putString(DEVICE_SOURCE_FILE, fileUri) + .putString(ROOM_TOKEN, roomToken) + .putString(CONVERSATION_NAME, conversationName) + .putString(META_DATA, metaData) + .build() + val uploadWorker: OneTimeWorkRequest = + OneTimeWorkRequest + .Builder(UploadAndShareFilesWorker::class.java) + .setInputData(data) + .build() WorkManager.getInstance().enqueueUniqueWork(fileUri, ExistingWorkPolicy.KEEP, uploadWorker) } } diff --git a/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt b/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt index 47856ba1ca5..ad7888d8b5e 100644 --- a/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/location/GeocodingActivity.kt @@ -122,17 +122,19 @@ class GeocodingActivity : } else { Log.e(TAG, "search string that was passed to GeocodingActivity was null or empty") } - adapter.setOnItemClickListener(object : GeocodingAdapter.OnItemClickListener { - override fun onItemClick(position: Int) { - val address: Address = adapter.getItem(position) as Address - val geocodingResult = GeocodingResult(address.latitude, address.longitude, address.displayName) - val intent = Intent(this@GeocodingActivity, LocationPickerActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) - intent.putExtra(BundleKeys.KEY_GEOCODING_RESULT, geocodingResult) - startActivity(intent) + adapter.setOnItemClickListener( + object : GeocodingAdapter.OnItemClickListener { + override fun onItemClick(position: Int) { + val address: Address = adapter.getItem(position) as Address + val geocodingResult = GeocodingResult(address.latitude, address.longitude, address.displayName) + val intent = Intent(this@GeocodingActivity, LocationPickerActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) + intent.putExtra(BundleKeys.KEY_GEOCODING_RESULT, geocodingResult) + startActivity(intent) + } } - }) + ) searchView?.setQuery(viewModel.getQuery(), false) } @@ -150,17 +152,19 @@ class GeocodingActivity : private fun initAdapter(addresses: List
) { adapter = GeocodingAdapter(binding.geocodingResults.context!!, addresses) - adapter.setOnItemClickListener(object : GeocodingAdapter.OnItemClickListener { - override fun onItemClick(position: Int) { - val address: Address = adapter.getItem(position) as Address - val geocodingResult = GeocodingResult(address.latitude, address.longitude, address.displayName) - val intent = Intent(this@GeocodingActivity, LocationPickerActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) - intent.putExtra(BundleKeys.KEY_GEOCODING_RESULT, geocodingResult) - startActivity(intent) + adapter.setOnItemClickListener( + object : GeocodingAdapter.OnItemClickListener { + override fun onItemClick(position: Int) { + val address: Address = adapter.getItem(position) as Address + val geocodingResult = GeocodingResult(address.latitude, address.longitude, address.displayName) + val intent = Intent(this@GeocodingActivity, LocationPickerActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) + intent.putExtra(BundleKeys.KEY_GEOCODING_RESULT, geocodingResult) + startActivity(intent) + } } - }) + ) binding.geocodingResults.adapter = adapter } @@ -189,37 +193,41 @@ class GeocodingActivity : searchView?.imeOptions = imeOptions searchView?.queryHint = resources!!.getString(R.string.nc_search) searchView?.setSearchableInfo(searchManager.getSearchableInfo(componentName)) - searchView?.setOnQueryTextListener(object : SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String): Boolean { - viewModel.setQuery(query) - viewModel.searchLocation() - searchView?.clearFocus() - return true - } - - override fun onQueryTextChange(query: String): Boolean { - // This is a workaround to not set viewModel data when onQueryTextChange is triggered on startup - // Otherwise it would be set to an empty string. - if (searchView?.width!! > 0) { + searchView?.setOnQueryTextListener( + object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { viewModel.setQuery(query) + viewModel.searchLocation() + searchView?.clearFocus() + return true } - return true - } - }) - searchItem?.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { - override fun onMenuItemActionExpand(menuItem: MenuItem): Boolean { - return true + override fun onQueryTextChange(query: String): Boolean { + // This is a workaround to not set viewModel data when onQueryTextChange is triggered on startup + // Otherwise it would be set to an empty string. + if (searchView?.width!! > 0) { + viewModel.setQuery(query) + } + return true + } } + ) - override fun onMenuItemActionCollapse(menuItem: MenuItem): Boolean { - val intent = Intent(context, LocationPickerActivity::class.java) - intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) - startActivity(intent) - return true + searchItem?.setOnActionExpandListener( + object : MenuItem.OnActionExpandListener { + override fun onMenuItemActionExpand(menuItem: MenuItem): Boolean { + return true + } + + override fun onMenuItemActionCollapse(menuItem: MenuItem): Boolean { + val intent = Intent(context, LocationPickerActivity::class.java) + intent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + intent.putExtra(BundleKeys.KEY_ROOM_TOKEN, roomToken) + startActivity(intent) + return true + } } - }) + ) } } diff --git a/app/src/main/java/com/nextcloud/talk/location/LocationPickerActivity.kt b/app/src/main/java/com/nextcloud/talk/location/LocationPickerActivity.kt index 30f65c01d79..f8e06de4bc6 100644 --- a/app/src/main/java/com/nextcloud/talk/location/LocationPickerActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/location/LocationPickerActivity.kt @@ -117,12 +117,13 @@ class LocationPickerActivity : var searchItem: MenuItem? = null var searchView: SearchView? = null - private val onBackPressedCallback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - setResult(Activity.RESULT_CANCELED) - finish() + private val onBackPressedCallback = + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + setResult(Activity.RESULT_CANCELED) + finish() + } } - } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -334,41 +335,42 @@ class LocationPickerActivity : ) } - private fun delayedMapListener() = DelayedMapListener( - object : MapListener { - @Suppress("Detekt.TooGenericExceptionCaught") - override fun onScroll(paramScrollEvent: ScrollEvent): Boolean { - try { - when { - moveToCurrentLocation -> { - setLocationDescription(isGpsLocation = true, isGeocodedResult = false) - moveToCurrentLocation = false - } - - geocodingResult != null -> { - binding.shareLocation.isClickable = true - setLocationDescription(isGpsLocation = false, isGeocodedResult = true) - geocodingResult = null - } - - else -> { - binding.shareLocation.isClickable = true - setLocationDescription(isGpsLocation = false, isGeocodedResult = false) + private fun delayedMapListener() = + DelayedMapListener( + object : MapListener { + @Suppress("Detekt.TooGenericExceptionCaught") + override fun onScroll(paramScrollEvent: ScrollEvent): Boolean { + try { + when { + moveToCurrentLocation -> { + setLocationDescription(isGpsLocation = true, isGeocodedResult = false) + moveToCurrentLocation = false + } + + geocodingResult != null -> { + binding.shareLocation.isClickable = true + setLocationDescription(isGpsLocation = false, isGeocodedResult = true) + geocodingResult = null + } + + else -> { + binding.shareLocation.isClickable = true + setLocationDescription(isGpsLocation = false, isGeocodedResult = false) + } } + } catch (e: NullPointerException) { + Log.d(TAG, "UI already closed") } - } catch (e: NullPointerException) { - Log.d(TAG, "UI already closed") - } - readyToShareLocation = true - return true - } + readyToShareLocation = true + return true + } - override fun onZoom(event: ZoomEvent): Boolean { - return false + override fun onZoom(event: ZoomEvent): Boolean { + return false + } } - } - ) + ) @Suppress("Detekt.TooGenericExceptionCaught") private fun requestLocationUpdates() { @@ -474,25 +476,27 @@ class LocationPickerActivity : ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(t: GenericOverall) { - finish() - } + override fun onNext(t: GenericOverall) { + finish() + } - override fun onError(e: Throwable) { - Log.e(TAG, "error when trying to share location", e) - Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() - finish() - } + override fun onError(e: Throwable) { + Log.e(TAG, "error when trying to share location", e) + Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() + finish() + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun isLocationPermissionsGranted(): Boolean { @@ -523,11 +527,7 @@ class LocationPickerActivity : ) } - override fun onRequestPermissionsResult( - requestCode: Int, - permissions: Array, - grantResults: IntArray - ) { + override fun onRequestPermissionsResult(requestCode: Int, permissions: Array, grantResults: IntArray) { super.onRequestPermissionsResult(requestCode, permissions, grantResults) fun areAllGranted(grantResults: IntArray): Boolean { diff --git a/app/src/main/java/com/nextcloud/talk/lock/LockedActivity.kt b/app/src/main/java/com/nextcloud/talk/lock/LockedActivity.kt index 2ab4223d68e..40ef5656161 100644 --- a/app/src/main/java/com/nextcloud/talk/lock/LockedActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/lock/LockedActivity.kt @@ -82,37 +82,41 @@ class LockedActivity : AppCompatActivity() { } private fun showBiometricDialog() { - val promptInfo = BiometricPrompt.PromptInfo.Builder() - .setTitle( - String.format( - context.getString(R.string.nc_biometric_unlock), - context.getString(R.string.nc_app_product_name) + val promptInfo = + BiometricPrompt + .PromptInfo + .Builder() + .setTitle( + String.format( + context.getString(R.string.nc_biometric_unlock), + context.getString(R.string.nc_app_product_name) + ) ) - ) - .setNegativeButtonText(context.getString(R.string.nc_cancel)) - .build() + .setNegativeButtonText(context.getString(R.string.nc_cancel)) + .build() val executor: Executor = Executors.newSingleThreadExecutor() - val biometricPrompt = BiometricPrompt( - this, - executor, - object : BiometricPrompt.AuthenticationCallback() { - override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { - super.onAuthenticationSucceeded(result) - Log.d(TAG, "Fingerprint recognised successfully") - finish() - } - - override fun onAuthenticationFailed() { - super.onAuthenticationFailed() - Log.d(TAG, "Fingerprint not recognised") + val biometricPrompt = + BiometricPrompt( + this, + executor, + object : BiometricPrompt.AuthenticationCallback() { + override fun onAuthenticationSucceeded(result: BiometricPrompt.AuthenticationResult) { + super.onAuthenticationSucceeded(result) + Log.d(TAG, "Fingerprint recognised successfully") + finish() + } + + override fun onAuthenticationFailed() { + super.onAuthenticationFailed() + Log.d(TAG, "Fingerprint not recognised") + } + + override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { + super.onAuthenticationError(errorCode, errString) + showAuthenticationScreen() + } } - - override fun onAuthenticationError(errorCode: Int, errString: CharSequence) { - super.onAuthenticationError(errorCode, errString) - showAuthenticationScreen() - } - } - ) + ) val cryptoObject = SecurityUtils.getCryptoObject() if (cryptoObject != null) { biometricPrompt.authenticate(promptInfo, cryptoObject) diff --git a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt index 642e7d33595..8dfc736b6e9 100644 --- a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchActivity.kt @@ -74,12 +74,13 @@ class MessageSearchActivity : BaseActivity() { private var searchViewDisposable: Disposable? = null private var adapter: FlexibleAdapter>? = null - private val onBackPressedCallback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - setResult(Activity.RESULT_CANCELED) - finish() + private val onBackPressedCallback = + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + setResult(Activity.RESULT_CANCELED) + finish() + } } - } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -145,11 +146,12 @@ class MessageSearchActivity : BaseActivity() { } private fun setAdapterItems(state: MessageSearchViewModel.LoadedState) { - val loadMoreItems = if (state.hasMore) { - listOf(LoadMoreResultsItem) - } else { - emptyList() - } + val loadMoreItems = + if (state.hasMore) { + listOf(LoadMoreResultsItem) + } else { + emptyList() + } val newItems = state.results.map { MessageResultItem(this, user, it, false, viewThemeUtils) } + loadMoreItems @@ -163,29 +165,33 @@ class MessageSearchActivity : BaseActivity() { private fun createAdapter(items: List>) { adapter = FlexibleAdapter(items) binding.messageSearchRecycler.adapter = adapter - adapter!!.addListener(object : FlexibleAdapter.OnItemClickListener { - override fun onItemClick(view: View?, position: Int): Boolean { - val item = adapter!!.getItem(position) - when (item?.itemViewType) { - LoadMoreResultsItem.VIEW_TYPE -> { - viewModel.loadMore() - } - MessageResultItem.VIEW_TYPE -> { - val messageItem = item as MessageResultItem - viewModel.selectMessage(messageItem.messageEntry) + adapter!!.addListener( + object : FlexibleAdapter.OnItemClickListener { + override fun onItemClick(view: View?, position: Int): Boolean { + val item = adapter!!.getItem(position) + when (item?.itemViewType) { + LoadMoreResultsItem.VIEW_TYPE -> { + viewModel.loadMore() + } + + MessageResultItem.VIEW_TYPE -> { + val messageItem = item as MessageResultItem + viewModel.selectMessage(messageItem.messageEntry) + } } + return false } - return false } - }) + ) } private fun onFinish() { val state = viewModel.state.value if (state is MessageSearchViewModel.FinishedState) { - val resultIntent = Intent().apply { - putExtra(RESULT_KEY_MESSAGE_ID, state.selectedMessageId) - } + val resultIntent = + Intent().apply { + putExtra(RESULT_KEY_MESSAGE_ID, state.selectedMessageId) + } setResult(Activity.RESULT_OK, resultIntent) finish() } @@ -214,37 +220,42 @@ class MessageSearchActivity : BaseActivity() { val menuItem = menu.findItem(R.id.action_search) searchView = menuItem.actionView as SearchView setupSearchView() - menuItem.setOnActionExpandListener(object : MenuItem.OnActionExpandListener { - override fun onMenuItemActionExpand(item: MenuItem): Boolean { - searchView.requestFocus() - return true - } + menuItem.setOnActionExpandListener( + object : MenuItem.OnActionExpandListener { + override fun onMenuItemActionExpand(item: MenuItem): Boolean { + searchView.requestFocus() + return true + } - override fun onMenuItemActionCollapse(item: MenuItem): Boolean { - onBackPressedDispatcher.onBackPressed() - return false + override fun onMenuItemActionCollapse(item: MenuItem): Boolean { + onBackPressedDispatcher.onBackPressed() + return false + } } - }) + ) menuItem.expandActionView() return true } private fun setupSearchView() { searchView.queryHint = getString(R.string.message_search_hint) - searchViewDisposable = observeSearchView(searchView) - .debounce { query -> - when { - TextUtils.isEmpty(query) -> Observable.empty() - else -> Observable.timer( - ConversationsListActivity.SEARCH_DEBOUNCE_INTERVAL_MS.toLong(), - TimeUnit.MILLISECONDS - ) + searchViewDisposable = + observeSearchView(searchView) + .debounce { query -> + when { + TextUtils.isEmpty(query) -> Observable.empty() + else -> + Observable + .timer( + ConversationsListActivity.SEARCH_DEBOUNCE_INTERVAL_MS.toLong(), + TimeUnit.MILLISECONDS + ) + } } - } - .distinctUntilChanged() - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe { newText -> viewModel.onQueryTextChange(newText) } + .distinctUntilChanged() + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe { newText -> viewModel.onQueryTextChange(newText) } } override fun onOptionsItemSelected(item: MenuItem): Boolean { @@ -253,6 +264,7 @@ class MessageSearchActivity : BaseActivity() { onBackPressedDispatcher.onBackPressed() true } + else -> super.onOptionsItemSelected(item) } } diff --git a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchHelper.kt b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchHelper.kt index 0a977aa4617..3244e26fc32 100644 --- a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchHelper.kt +++ b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchHelper.kt @@ -27,7 +27,9 @@ import com.nextcloud.talk.repositories.unifiedsearch.UnifiedSearchRepository import io.reactivex.Observable import io.reactivex.disposables.Disposable -class MessageSearchHelper @JvmOverloads constructor( +class MessageSearchHelper +@JvmOverloads +constructor( private val unifiedSearchRepository: UnifiedSearchRepository, private val fromRoom: String? = null ) { @@ -86,6 +88,7 @@ class MessageSearchHelper @JvmOverloads constructor( cursor = cursor ) } + else -> { unifiedSearchRepository.searchMessages( searchTerm = search, diff --git a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchViewModel.kt b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchViewModel.kt index 67accc29342..fd732c1fd94 100644 --- a/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/messagesearch/MessageSearchViewModel.kt @@ -47,14 +47,22 @@ import javax.inject.Inject * ErrorState --> LoadingState * @enduml */ -class MessageSearchViewModel @Inject constructor(private val unifiedSearchRepository: UnifiedSearchRepository) : +class MessageSearchViewModel +@Inject +constructor(private val unifiedSearchRepository: UnifiedSearchRepository) : ViewModel() { sealed class ViewState + object InitialState : ViewState() + object LoadingState : ViewState() + object EmptyState : ViewState() + object ErrorState : ViewState() + class LoadedState(val results: List, val hasMore: Boolean) : ViewState() + class FinishedState(val selectedMessageId: String) : ViewState() private lateinit var messageSearchHelper: MessageSearchHelper diff --git a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt index 1e85da1da41..f1e5877422e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt +++ b/app/src/main/java/com/nextcloud/talk/models/domain/ConversationModel.kt @@ -45,9 +45,9 @@ class ConversationModel( var hasCustomAvatar: Boolean? = null ) { companion object { - fun mapToConversationModel( - conversation: Conversation - ): ConversationModel { + fun mapToConversationModel(conversation: Conversation): ConversationModel { + val conversationReadOnlyState = + conversation.conversationReadOnlyState?.let { ConversationReadOnlyState.valueOf(it.name) } return ConversationModel( roomId = conversation.roomId, token = conversation.token, @@ -68,16 +68,8 @@ class ConversationModel( unreadMention = conversation.unreadMention, // lastMessage = conversation.lastMessage, to do... objectType = conversation.objectType?.let { ObjectType.valueOf(it.name) }, - notificationLevel = conversation.notificationLevel?.let { - NotificationLevel.valueOf( - it.name - ) - }, - conversationReadOnlyState = conversation.conversationReadOnlyState?.let { - ConversationReadOnlyState.valueOf( - it.name - ) - }, + notificationLevel = conversation.notificationLevel?.let { NotificationLevel.valueOf(it.name) }, + conversationReadOnlyState = conversationReadOnlyState, lobbyState = conversation.lobbyState?.let { LobbyState.valueOf(it.name) }, lobbyTimer = conversation.lobbyTimer, lastReadMessage = conversation.lastReadMessage, @@ -112,7 +104,13 @@ enum class ConversationType { } enum class ParticipantType { - DUMMY, OWNER, MODERATOR, USER, GUEST, USER_FOLLOWING_LINK, GUEST_MODERATOR + DUMMY, + OWNER, + MODERATOR, + USER, + GUEST, + USER_FOLLOWING_LINK, + GUEST_MODERATOR } enum class ObjectType { @@ -123,13 +121,18 @@ enum class ObjectType { } enum class NotificationLevel { - DEFAULT, ALWAYS, MENTION, NEVER + DEFAULT, + ALWAYS, + MENTION, + NEVER } enum class ConversationReadOnlyState { - CONVERSATION_READ_WRITE, CONVERSATION_READ_ONLY + CONVERSATION_READ_WRITE, + CONVERSATION_READ_ONLY } enum class LobbyState { - LOBBY_STATE_ALL_PARTICIPANTS, LOBBY_STATE_MODERATORS_ONLY + LOBBY_STATE_ALL_PARTICIPANTS, + LOBBY_STATE_MODERATORS_ONLY } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt index e44c59bfc5c..d3b49fb5905 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatMessage.kt @@ -49,121 +49,84 @@ import java.util.Date data class ChatMessage( @JsonIgnore var isGrouped: Boolean = false, - @JsonIgnore var isOneToOneConversation: Boolean = false, - @JsonIgnore var isFormerOneToOneConversation: Boolean = false, - @JsonIgnore var activeUser: User? = null, - @JsonIgnore var selectedIndividualHashMap: Map? = null, - @JsonIgnore var isDeleted: Boolean = false, - @JsonField(name = ["id"]) var jsonMessageId: Int = 0, - @JsonIgnore var previousMessageId: Int = -1, - @JsonField(name = ["token"]) var token: String? = null, - // guests or users @JsonField(name = ["actorType"]) var actorType: String? = null, - @JsonField(name = ["actorId"]) var actorId: String? = null, - // send when crafting a message @JsonField(name = ["actorDisplayName"]) var actorDisplayName: String? = null, - @JsonField(name = ["timestamp"]) var timestamp: Long = 0, - // send when crafting a message, max 1000 lines @JsonField(name = ["message"]) var message: String? = null, - @JsonField(name = ["messageParameters"]) var messageParameters: HashMap>? = null, - @JsonField(name = ["systemMessage"], typeConverter = EnumSystemMessageTypeConverter::class) var systemMessageType: SystemMessageType? = null, - @JsonField(name = ["isReplyable"]) var replyable: Boolean = false, - @JsonField(name = ["parent"]) var parentMessage: ChatMessage? = null, - var readStatus: Enum = ReadStatus.NONE, - @JsonField(name = ["messageType"]) var messageType: String? = null, - @JsonField(name = ["reactions"]) var reactions: LinkedHashMap? = null, - @JsonField(name = ["reactionsSelf"]) var reactionsSelf: ArrayList? = null, - @JsonField(name = ["expirationTimestamp"]) var expirationTimestamp: Int = 0, - @JsonField(name = ["markdown"]) var renderMarkdown: Boolean? = null, - var isDownloadingVoiceMessage: Boolean = false, - var resetVoiceMessage: Boolean = false, - var isPlayingVoiceMessage: Boolean = false, - var voiceMessageDuration: Int = 0, - var voiceMessagePlayedSeconds: Int = 0, - var voiceMessageDownloadProgress: Int = 0, - var voiceMessageSeekbarProgress: Int = 0, - var voiceMessageFloatArray: FloatArray? = null, - var expandableParent: Boolean = false, - var isExpanded: Boolean = false, - var lastItemOfExpandableGroup: Int = 0, - var expandableChildrenAmount: Int = 0, - var hiddenByCollapse: Boolean = false, - var openWhenDownloaded: Boolean = true - ) : Parcelable, MessageContentType, MessageContentType.Image { var extractedUrlToPreview: String? = null // messageTypesToIgnore is weird. must be deleted by refactoring!!! @JsonIgnore - var messageTypesToIgnore = listOf( - MessageType.REGULAR_TEXT_MESSAGE, - MessageType.SYSTEM_MESSAGE, - MessageType.SINGLE_LINK_VIDEO_MESSAGE, - MessageType.SINGLE_LINK_AUDIO_MESSAGE, - MessageType.SINGLE_LINK_MESSAGE, - MessageType.SINGLE_NC_GEOLOCATION_MESSAGE, - MessageType.VOICE_MESSAGE, - MessageType.POLL_MESSAGE - ) + var messageTypesToIgnore = + listOf( + MessageType.REGULAR_TEXT_MESSAGE, + MessageType.SYSTEM_MESSAGE, + MessageType.SINGLE_LINK_VIDEO_MESSAGE, + MessageType.SINGLE_LINK_AUDIO_MESSAGE, + MessageType.SINGLE_LINK_MESSAGE, + MessageType.SINGLE_NC_GEOLOCATION_MESSAGE, + MessageType.VOICE_MESSAGE, + MessageType.POLL_MESSAGE + ) fun hasFileAttachment(): Boolean { if (messageParameters != null && messageParameters!!.size > 0) { @@ -373,11 +336,12 @@ data class ChatMessage( return "" } - private fun getNullsafeActorDisplayName() = if (!TextUtils.isEmpty(actorDisplayName)) { - actorDisplayName - } else { - sharedApplication!!.getString(R.string.nc_guest) - } + private fun getNullsafeActorDisplayName() = + if (!TextUtils.isEmpty(actorDisplayName)) { + actorDisplayName + } else { + sharedApplication!!.getString(R.string.nc_guest) + } override fun getUser(): IUser { return object : IUser { @@ -398,9 +362,11 @@ data class ChatMessage( activeUser == null -> { null } + actorType == "users" -> { ApiUtils.getUrlForAvatar(activeUser!!.baseUrl, actorId, true) } + actorType == "bridged" -> { ApiUtils.getUrlForAvatar( activeUser!!.baseUrl, @@ -408,6 +374,7 @@ data class ChatMessage( true ) } + else -> { var apiId: String? = sharedApplication!!.getString(R.string.nc_guest) if (!TextUtils.isEmpty(actorDisplayName)) { @@ -470,7 +437,8 @@ data class ChatMessage( * see https://nextcloud-talk.readthedocs.io/en/latest/chat/#system-messages */ enum class SystemMessageType { - DUMMY, CONVERSATION_CREATED, + DUMMY, + CONVERSATION_CREATED, CONVERSATION_RENAMED, DESCRIPTION_REMOVED, DESCRIPTION_SET, @@ -504,7 +472,8 @@ data class ChatMessage( GUEST_MODERATOR_PROMOTED, GUEST_MODERATOR_DEMOTED, MESSAGE_DELETED, - FILE_SHARED, OBJECT_SHARED, + FILE_SHARED, + OBJECT_SHARED, MATTERBRIDGE_CONFIG_ADDED, MATTERBRIDGE_CONFIG_EDITED, MATTERBRIDGE_CONFIG_REMOVED, diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt index d6d1d6869d5..76df6483c0b 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ChatUtils.kt @@ -26,8 +26,10 @@ package com.nextcloud.talk.models.json.chat class ChatUtils { companion object { - fun getParsedMessage(message: String?, messageParameters: HashMap>?): - String? { + fun getParsedMessage( + message: String?, + messageParameters: HashMap>? + ): String? { if (messageParameters != null && messageParameters.size > 0) { return parse(messageParameters, message) } @@ -35,29 +37,27 @@ class ChatUtils { } @Suppress("Detekt.ComplexMethod") - private fun parse( - messageParameters: HashMap>, - message: String? - ): String? { + private fun parse(messageParameters: HashMap>, message: String?): String? { var resultMessage = message for (key in messageParameters.keys) { val individualHashMap = messageParameters[key] if (individualHashMap != null) { val type = individualHashMap["type"] - resultMessage = if (type == "user" || type == "guest" || type == "call") { - resultMessage?.replace("{$key}", "@" + individualHashMap["name"]) - } else if (type == "geo-location") { - individualHashMap["name"] - } else if (individualHashMap?.containsKey("link") == true) { - if (type == "file") { - resultMessage?.replace("{$key}", individualHashMap["name"].toString()) + resultMessage = + if (type == "user" || type == "guest" || type == "call") { + resultMessage?.replace("{$key}", "@" + individualHashMap["name"]) + } else if (type == "geo-location") { + individualHashMap["name"] + } else if (individualHashMap?.containsKey("link") == true) { + if (type == "file") { + resultMessage?.replace("{$key}", individualHashMap["name"].toString()) + } else { + individualHashMap["link"].toString() + } } else { - individualHashMap["link"].toString() + individualHashMap["name"]?.let { resultMessage?.replace("{$key}", it) } } - } else { - individualHashMap["name"]?.let { resultMessage?.replace("{$key}", it) } - } } } return resultMessage diff --git a/app/src/main/java/com/nextcloud/talk/models/json/chat/ReadStatus.kt b/app/src/main/java/com/nextcloud/talk/models/json/chat/ReadStatus.kt index d913689950f..07259466da5 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/chat/ReadStatus.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/chat/ReadStatus.kt @@ -20,5 +20,7 @@ package com.nextcloud.talk.models.json.chat enum class ReadStatus { - NONE, SENT, READ + NONE, + SENT, + READ } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt index e3dd2ea9205..5bae9f52294 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/Conversation.kt @@ -69,99 +69,68 @@ data class Conversation( var actorId: String? = null, @JsonField(name = ["actorType"]) var actorType: String? = null, - var password: String? = null, - @JsonField(name = ["isFavorite"]) var favorite: Boolean = false, - @JsonField(name = ["lastActivity"]) var lastActivity: Long = 0, - @JsonField(name = ["unreadMessages"]) var unreadMessages: Int = 0, - @JsonField(name = ["unreadMention"]) var unreadMention: Boolean = false, - @JsonField(name = ["lastMessage"]) var lastMessage: ChatMessage? = null, - @JsonField(name = ["objectType"], typeConverter = ConversationObjectTypeConverter::class) var objectType: ObjectType? = null, - @JsonField(name = ["notificationLevel"], typeConverter = EnumNotificationLevelConverter::class) var notificationLevel: NotificationLevel? = null, - @JsonField(name = ["readOnly"], typeConverter = EnumReadOnlyConversationConverter::class) var conversationReadOnlyState: ConversationReadOnlyState? = null, - @JsonField(name = ["lobbyState"], typeConverter = EnumLobbyStateConverter::class) var lobbyState: LobbyState? = null, - @JsonField(name = ["lobbyTimer"]) var lobbyTimer: Long? = null, - @JsonField(name = ["lastReadMessage"]) var lastReadMessage: Int = 0, - @JsonField(name = ["hasCall"]) var hasCall: Boolean = false, - @JsonField(name = ["callFlag"]) var callFlag: Int = 0, - @JsonField(name = ["canStartCall"]) var canStartCall: Boolean = false, - @JsonField(name = ["canLeaveConversation"]) var canLeaveConversation: Boolean? = null, - @JsonField(name = ["canDeleteConversation"]) var canDeleteConversation: Boolean? = null, - @JsonField(name = ["unreadMentionDirect"]) var unreadMentionDirect: Boolean? = null, - @JsonField(name = ["notificationCalls"]) var notificationCalls: Int? = null, - @JsonField(name = ["permissions"]) var permissions: Int = 0, - @JsonField(name = ["messageExpiration"]) var messageExpiration: Int = 0, - @JsonField(name = ["status"]) var status: String? = null, - @JsonField(name = ["statusIcon"]) var statusIcon: String? = null, - @JsonField(name = ["statusMessage"]) var statusMessage: String? = null, - @JsonField(name = ["statusClearAt"]) var statusClearAt: Long? = 0, - @JsonField(name = ["callRecording"]) var callRecording: Int = 0, - @JsonField(name = ["avatarVersion"]) var avatarVersion: String? = null, - // Be aware that variables with "is" at the beginning will lead to the error: // "@JsonField annotation can only be used on private fields if both getter and setter are present." // Instead, name it with "has" at the beginning: isCustomAvatar -> hasCustomAvatar @JsonField(name = ["isCustomAvatar"]) var hasCustomAvatar: Boolean? = null, - @JsonField(name = ["callStartTime"]) var callStartTime: Long? = null, - @JsonField(name = ["recordingConsent"]) var recordingConsentRequired: Int = 0 - ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' constructor() : this(null, null) @@ -172,15 +141,17 @@ data class Conversation( @Deprecated("Use ConversationUtil") val isGuest: Boolean - get() = ParticipantType.GUEST == participantType || - ParticipantType.GUEST_MODERATOR == participantType || - ParticipantType.USER_FOLLOWING_LINK == participantType + get() = + ParticipantType.GUEST == participantType || + ParticipantType.GUEST_MODERATOR == participantType || + ParticipantType.USER_FOLLOWING_LINK == participantType @Deprecated("Use ConversationUtil") val isParticipantOwnerOrModerator: Boolean - get() = ParticipantType.OWNER == participantType || - ParticipantType.GUEST_MODERATOR == participantType || - ParticipantType.MODERATOR == participantType + get() = + ParticipantType.OWNER == participantType || + ParticipantType.GUEST_MODERATOR == participantType || + ParticipantType.MODERATOR == participantType @Deprecated("Use ConversationUtil") private fun isLockedOneToOne(conversationUser: User): Boolean { @@ -229,15 +200,20 @@ data class Conversation( } enum class NotificationLevel { - DEFAULT, ALWAYS, MENTION, NEVER + DEFAULT, + ALWAYS, + MENTION, + NEVER } enum class LobbyState { - LOBBY_STATE_ALL_PARTICIPANTS, LOBBY_STATE_MODERATORS_ONLY + LOBBY_STATE_ALL_PARTICIPANTS, + LOBBY_STATE_MODERATORS_ONLY } enum class ConversationReadOnlyState { - CONVERSATION_READ_WRITE, CONVERSATION_READ_ONLY + CONVERSATION_READ_WRITE, + CONVERSATION_READ_ONLY } @Parcelize diff --git a/app/src/main/java/com/nextcloud/talk/models/json/conversations/password/PasswordOCS.kt b/app/src/main/java/com/nextcloud/talk/models/json/conversations/password/PasswordOCS.kt index d2c68ae15f9..ea3c2c679e9 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/conversations/password/PasswordOCS.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/conversations/password/PasswordOCS.kt @@ -31,7 +31,6 @@ import kotlinx.parcelize.Parcelize data class PasswordOCS( @JsonField(name = ["meta"]) var meta: GenericMeta? = null, - @JsonField(name = ["data"]) var data: PasswordData? = null ) : Parcelable { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/generic/Status.kt b/app/src/main/java/com/nextcloud/talk/models/json/generic/Status.kt index 0e2e4640288..10b19beed5e 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/generic/Status.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/generic/Status.kt @@ -31,22 +31,16 @@ import kotlinx.parcelize.Parcelize data class Status( @JsonField(name = ["installed"]) var installed: Boolean = false, - @JsonField(name = ["maintenance"]) var maintenance: Boolean = false, - @JsonField(name = ["upgrade"]) var needsUpgrade: Boolean = false, - @JsonField(name = ["version"]) var version: String? = null, - @JsonField(name = ["versionstring"]) var versionString: String? = null, - @JsonField(name = ["edition"]) var edition: String? = null, - @JsonField(name = ["productname"]) var productName: String? = null ) : Parcelable { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOCS.kt b/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOCS.kt index fef24a6e6af..515d90340fe 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOCS.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/AddParticipantOCS.kt @@ -33,7 +33,7 @@ import kotlinx.parcelize.Parcelize data class AddParticipantOCS( @JsonField(name = ["meta"]) var meta: GenericMeta?, - /* Returned room will have only type set, and sometimes even that will be null */ + // Returned room will have only type set, and sometimes even that will be null @JsonField(name = ["data"]) var data: Conversation? = null ) : Parcelable { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.kt b/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.kt index 3b085025b12..5fc2cd05bb2 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/participants/Participant.kt @@ -33,61 +33,43 @@ import kotlinx.parcelize.Parcelize data class Participant( @JsonField(name = ["attendeeId"]) var attendeeId: Long? = null, - @JsonField(name = ["actorType"], typeConverter = EnumActorTypeConverter::class) var actorType: ActorType? = null, - @JsonField(name = ["actorId"]) var actorId: String? = null, - @JsonField(name = ["attendeePin"]) var attendeePin: String? = null, - @Deprecated("") @JsonField(name = ["userId"]) var userId: String? = null, - @JsonField(name = ["internal"]) var internal: Boolean? = null, - @JsonField(name = ["type", "participantType"], typeConverter = EnumParticipantTypeConverter::class) var type: ParticipantType? = null, - @Deprecated("") @JsonField(name = ["name"]) var name: String? = null, - @JsonField(name = ["displayName"]) var displayName: String? = null, - @JsonField(name = ["lastPing"]) var lastPing: Long = 0, - @Deprecated("") @JsonField(name = ["sessionId"]) var sessionId: String? = null, - @JsonField(name = ["sessionIds"]) var sessionIds: ArrayList = ArrayList(0), - @Deprecated("") @JsonField(name = ["roomId"]) var roomId: Long = 0, - @JsonField(name = ["inCall"]) var inCall: Long = 0, - @JsonField(name = ["status"]) var status: String? = null, - @JsonField(name = ["statusIcon"]) var statusIcon: String? = null, - @JsonField(name = ["statusMessage"]) var statusMessage: String? = null, - var source: String? = null, - var selected: Boolean = false ) : Parcelable { // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' @@ -103,32 +85,45 @@ data class Participant( * https://github.com/nextcloud/spreed/blob/stable21/lib/Controller/RoomController.php#L1145-L1148 */ val calculatedActorType: ActorType - get() = if (actorType == null) { - if (userId != null) { - ActorType.USERS + get() = + if (actorType == null) { + if (userId != null) { + ActorType.USERS + } else { + ActorType.GUESTS + } } else { - ActorType.GUESTS + actorType!! } - } else { - actorType!! - } /** * actorId is only guaranteed in APIv3+ so use calculatedActorId. */ val calculatedActorId: String? - get() = if (actorId == null) { - userId - } else { - actorId - } + get() = + if (actorId == null) { + userId + } else { + actorId + } enum class ActorType { - DUMMY, EMAILS, GROUPS, GUESTS, USERS, CIRCLES + DUMMY, + EMAILS, + GROUPS, + GUESTS, + USERS, + CIRCLES } enum class ParticipantType { - DUMMY, OWNER, MODERATOR, USER, GUEST, USER_FOLLOWING_LINK, GUEST_MODERATOR + DUMMY, + OWNER, + MODERATOR, + USER, + GUEST, + USER_FOLLOWING_LINK, + GUEST_MODERATOR } object InCallFlags { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/push/DecryptedPushMessage.kt b/app/src/main/java/com/nextcloud/talk/models/json/push/DecryptedPushMessage.kt index 9ff9b3a32c1..6af0aef8f42 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/push/DecryptedPushMessage.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/push/DecryptedPushMessage.kt @@ -30,40 +30,28 @@ import kotlinx.parcelize.Parcelize data class DecryptedPushMessage( @JsonField(name = ["app"]) var app: String?, - @JsonField(name = ["type"]) var type: String?, - @JsonField(name = ["subject"]) var subject: String, - @JsonField(name = ["id"]) var id: String?, - @JsonField(name = ["nid"]) var notificationId: Long?, - @JsonField(name = ["nids"]) var notificationIds: LongArray?, - @JsonField(name = ["delete"]) var delete: Boolean, - @JsonField(name = ["delete-all"]) var deleteAll: Boolean, - @JsonField(name = ["delete-multiple"]) var deleteMultiple: Boolean, - @JsonIgnore var notificationUser: NotificationUser?, - @JsonIgnore var text: String?, - @JsonIgnore var timestamp: Long, - @JsonIgnore var objectId: String? ) : Parcelable { @@ -85,7 +73,9 @@ data class DecryptedPushMessage( if (notificationIds != null) { if (other.notificationIds == null) return false if (!notificationIds.contentEquals(other.notificationIds)) return false - } else if (other.notificationIds != null) return false + } else if (other.notificationIds != null) { + return false + } if (delete != other.delete) return false if (deleteAll != other.deleteAll) return false if (deleteMultiple != other.deleteMultiple) return false diff --git a/app/src/main/java/com/nextcloud/talk/models/json/push/NotificationUser.kt b/app/src/main/java/com/nextcloud/talk/models/json/push/NotificationUser.kt index 8f0634bc512..1efcc9108ee 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/push/NotificationUser.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/push/NotificationUser.kt @@ -31,10 +31,8 @@ import kotlinx.parcelize.Parcelize data class NotificationUser( @JsonField(name = ["type"]) var type: String?, - @JsonField(name = ["id"]) var id: String?, - @JsonField(name = ["name"]) var name: String? ) : Parcelable { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.kt b/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.kt index 9b6e39956f8..151c1e604b1 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/push/PushConfigurationState.kt @@ -31,16 +31,12 @@ import kotlinx.parcelize.Parcelize data class PushConfigurationState( @JsonField(name = ["pushToken"]) var pushToken: String?, - @JsonField(name = ["deviceIdentifier"]) var deviceIdentifier: String?, - @JsonField(name = ["deviceIdentifierSignature"]) var deviceIdentifierSignature: String?, - @JsonField(name = ["userPublicKey"]) var userPublicKey: String?, - @JsonField(name = ["usesRegularPass"]) var usesRegularPass: Boolean? ) : Parcelable { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistration.kt b/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistration.kt index dcfa7c1c733..e61033b4cc8 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistration.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/push/PushRegistration.kt @@ -31,10 +31,8 @@ import kotlinx.parcelize.Parcelize data class PushRegistration( @JsonField(name = ["publicKey"]) var publicKey: String?, - @JsonField(name = ["deviceIdentifier"]) var deviceIdentifier: String?, - @JsonField(name = ["signature"]) var signature: String? ) : Parcelable { diff --git a/app/src/main/java/com/nextcloud/talk/models/json/reactions/ReactionVoter.kt b/app/src/main/java/com/nextcloud/talk/models/json/reactions/ReactionVoter.kt index c5001558740..74eeec9a395 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/reactions/ReactionVoter.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/reactions/ReactionVoter.kt @@ -41,6 +41,8 @@ data class ReactionVoter( constructor() : this(null, null, null, 0) enum class ReactionActorType { - DUMMY, GUESTS, USERS + DUMMY, + GUESTS, + USERS } } diff --git a/app/src/main/java/com/nextcloud/talk/models/json/status/Status.kt b/app/src/main/java/com/nextcloud/talk/models/json/status/Status.kt index 9f43683abac..74c19d28518 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/status/Status.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/status/Status.kt @@ -31,7 +31,7 @@ data class Status( var userId: String?, @JsonField(name = ["message"]) var message: String?, - /* TODO Change to enum */ + // TODO Change to enum @JsonField(name = ["messageId"]) var messageId: String?, @JsonField(name = ["messageIsPredefined"]) @@ -40,7 +40,7 @@ data class Status( var icon: String?, @JsonField(name = ["clearAt"]) var clearAt: Long = 0, - /* TODO Change to enum */ + // TODO Change to enum @JsonField(name = ["status"]) var status: String = "offline", @JsonField(name = ["statusIsUserDefined"]) diff --git a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.kt b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.kt index dc05729d70d..9a94818f545 100644 --- a/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.kt +++ b/app/src/main/java/com/nextcloud/talk/models/json/userprofile/UserProfileData.kt @@ -33,43 +33,30 @@ import kotlinx.parcelize.Parcelize data class UserProfileData( @JsonField(name = ["display-name"]) var displayName: String?, - @JsonField(name = ["displaynameScope"], typeConverter = ScopeConverter::class) var displayNameScope: Scope?, - @JsonField(name = ["displayname"]) var displayNameAlt: String?, - @JsonField(name = ["id"]) var userId: String?, - @JsonField(name = ["phone"]) var phone: String?, - @JsonField(name = ["phoneScope"], typeConverter = ScopeConverter::class) var phoneScope: Scope?, - @JsonField(name = ["email"]) var email: String?, - @JsonField(name = ["emailScope"], typeConverter = ScopeConverter::class) var emailScope: Scope?, - @JsonField(name = ["address"]) var address: String?, - @JsonField(name = ["addressScope"], typeConverter = ScopeConverter::class) var addressScope: Scope?, - @JsonField(name = ["twitter"]) var twitter: String?, - @JsonField(name = ["twitterScope"], typeConverter = ScopeConverter::class) var twitterScope: Scope?, - @JsonField(name = ["website"]) var website: String?, - @JsonField(name = ["websiteScope"], typeConverter = ScopeConverter::class) var websiteScope: Scope? ) : Parcelable { diff --git a/app/src/main/java/com/nextcloud/talk/openconversations/adapters/OpenConversationsAdapter.kt b/app/src/main/java/com/nextcloud/talk/openconversations/adapters/OpenConversationsAdapter.kt index 0c6303058fc..915f8b781b5 100644 --- a/app/src/main/java/com/nextcloud/talk/openconversations/adapters/OpenConversationsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/openconversations/adapters/OpenConversationsAdapter.kt @@ -35,7 +35,7 @@ class OpenConversationsAdapter(val user: User, private val onClick: (OpenConvers ListAdapter(ConversationsCallback) { inner class OpenConversationsViewHolder(val itemBinding: RvItemOpenConversationBinding) : RecyclerView.ViewHolder(itemBinding.root) { - var currentConversation: OpenConversation? = null + var currentConversation: OpenConversation? = null init { itemBinding.root.setOnClickListener { diff --git a/app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepositoryImpl.kt index b92cd8f05b4..79396a52a6a 100644 --- a/app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/openconversations/data/OpenConversationsRepositoryImpl.kt @@ -41,9 +41,7 @@ class OpenConversationsRepositoryImpl(private val ncApi: NcApi, currentUserProvi ).map { mapToOpenConversationsModel(it.ocs?.data!!) } } - private fun mapToOpenConversationsModel( - conversations: List - ): OpenConversationsModel { + private fun mapToOpenConversationsModel(conversations: List): OpenConversationsModel { return OpenConversationsModel( conversations.map { conversation -> OpenConversation( diff --git a/app/src/main/java/com/nextcloud/talk/openconversations/viewmodels/OpenConversationsViewModel.kt b/app/src/main/java/com/nextcloud/talk/openconversations/viewmodels/OpenConversationsViewModel.kt index 747572c760a..eb2f382e176 100644 --- a/app/src/main/java/com/nextcloud/talk/openconversations/viewmodels/OpenConversationsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/openconversations/viewmodels/OpenConversationsViewModel.kt @@ -33,13 +33,18 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class OpenConversationsViewModel @Inject constructor(private val repository: OpenConversationsRepository) : +class OpenConversationsViewModel +@Inject +constructor(private val repository: OpenConversationsRepository) : ViewModel() { sealed interface ViewState object FetchConversationsStartState : ViewState + object FetchConversationsEmptyState : ViewState + object FetchConversationsErrorState : ViewState + open class FetchConversationsSuccessState(val conversations: List) : ViewState private val _viewState: MutableLiveData = MutableLiveData(FetchConversationsStartState) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt index ba7ef21fa1d..a6c1b8a0964 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionViewHolder.kt @@ -60,34 +60,37 @@ class PollCreateOptionViewHolder( textListener = getTextWatcher(pollCreateOptionItem, itemsListener) binding.pollOptionTextEdit.addTextChangedListener(textListener) - binding.pollOptionTextInputLayout.hint = String.format( - binding.pollOptionTextInputLayout.resources.getString(R.string.polls_option_hint), - position + 1 - ) + binding.pollOptionTextInputLayout.hint = + String + .format( + binding.pollOptionTextInputLayout.resources.getString(R.string.polls_option_hint), + position + 1 + ) - binding.pollOptionDelete.contentDescription = String.format( - binding.pollOptionTextInputLayout.resources.getString(R.string.polls_option_delete), - position + 1 - ) + binding.pollOptionDelete.contentDescription = + String + .format( + binding.pollOptionTextInputLayout.resources.getString(R.string.polls_option_delete), + position + 1 + ) } private fun getTextWatcher( pollCreateOptionItem: PollCreateOptionItem, itemsListener: PollCreateOptionsItemListener - ) = - object : TextWatcher { - override fun afterTextChanged(s: Editable) { - // unused atm - } + ) = object : TextWatcher { + override fun afterTextChanged(s: Editable) { + // unused atm + } - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - // unused atm - } + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // unused atm + } - override fun onTextChanged(option: CharSequence, start: Int, before: Int, count: Int) { - pollCreateOptionItem.pollOption = option.toString() + override fun onTextChanged(option: CharSequence, start: Int, before: Int, count: Int) { + pollCreateOptionItem.pollOption = option.toString() - itemsListener.onOptionsItemTextChanged(pollCreateOptionItem) - } + itemsListener.onOptionsItemTextChanged(pollCreateOptionItem) } + } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt index a057592bd78..1f16f487934 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollCreateOptionsItemListener.kt @@ -23,7 +23,6 @@ package com.nextcloud.talk.polls.adapters import android.widget.EditText interface PollCreateOptionsItemListener { - fun onRemoveOptionsItemClick(pollCreateOptionItem: PollCreateOptionItem, position: Int) fun onOptionsItemTextChanged(pollCreateOptionItem: PollCreateOptionItem) diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt index cf752f49368..72837044f9b 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultVotersOverviewViewHolder.kt @@ -45,10 +45,12 @@ class PollResultVotersOverviewViewHolder( binding.root.setOnClickListener { clickListener.onClick() } - val layoutParams = LinearLayout.LayoutParams( - AVATAR_SIZE, - AVATAR_SIZE - ) + val layoutParams = + LinearLayout + .LayoutParams( + AVATAR_SIZE, + AVATAR_SIZE + ) var avatarsToDisplay = MAX_AVATARS if (item.detailsList.size < avatarsToDisplay) { diff --git a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt index e74b29aa44f..e7f903db72e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/adapters/PollResultsAdapter.kt @@ -41,27 +41,35 @@ class PollResultsAdapter( when (viewType) { PollResultHeaderItem.VIEW_TYPE -> { - val itemBinding = PollResultHeaderItemBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) + val itemBinding = + PollResultHeaderItemBinding + .inflate( + LayoutInflater.from(parent.context), + parent, + false + ) viewHolder = PollResultHeaderViewHolder(itemBinding, viewThemeUtils) } + PollResultVoterItem.VIEW_TYPE -> { - val itemBinding = PollResultVoterItemBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) + val itemBinding = + PollResultVoterItemBinding + .inflate( + LayoutInflater.from(parent.context), + parent, + false + ) viewHolder = PollResultVoterViewHolder(user, itemBinding, viewThemeUtils) } + PollResultVotersOverviewItem.VIEW_TYPE -> { - val itemBinding = PollResultVotersOverviewItemBinding.inflate( - LayoutInflater.from(parent.context), - parent, - false - ) + val itemBinding = + PollResultVotersOverviewItemBinding + .inflate( + LayoutInflater.from(parent.context), + parent, + false + ) viewHolder = PollResultVotersOverviewViewHolder(user, itemBinding) } } @@ -74,10 +82,12 @@ class PollResultsAdapter( val pollResultItem = list[position] holder.bind(pollResultItem as PollResultHeaderItem, clickListener) } + PollResultVoterItem.VIEW_TYPE -> { val pollResultItem = list[position] holder.bind(pollResultItem as PollResultVoterItem, clickListener) } + PollResultVotersOverviewItem.VIEW_TYPE -> { val pollResultItem = list[position] holder.bind(pollResultItem as PollResultVotersOverviewItem, clickListener) diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt index c32e9c74d1b..e4ef9bc8a38 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/PollRepositoryImpl.kt @@ -35,7 +35,6 @@ import kotlin.collections.forEach as kForEach class PollRepositoryImpl(private val ncApi: NcApi, private val currentUserProvider: CurrentUserProviderNew) : PollRepository { - val currentUser: User = currentUserProvider.currentUser.blockingGet() val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt index 336a8122671..251e3b7a375 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollDetailsResponse.kt @@ -29,13 +29,10 @@ import kotlinx.parcelize.Parcelize data class PollDetailsResponse( @JsonField(name = ["actorType"]) var actorType: String? = null, - @JsonField(name = ["actorId"]) var actorId: String, - @JsonField(name = ["actorDisplayName"]) var actorDisplayName: String, - @JsonField(name = ["optionId"]) var optionId: Int ) : Parcelable { diff --git a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt index 948d8698c46..11a0528708e 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/repositories/model/PollResponse.kt @@ -29,40 +29,28 @@ import kotlinx.parcelize.Parcelize data class PollResponse( @JsonField(name = ["id"]) var id: String, - @JsonField(name = ["question"]) var question: String? = null, - @JsonField(name = ["options"]) var options: ArrayList? = null, - @JsonField(name = ["votes"]) var votes: Map? = null, - @JsonField(name = ["actorType"]) var actorType: String? = null, - @JsonField(name = ["actorId"]) var actorId: String? = null, - @JsonField(name = ["actorDisplayName"]) var actorDisplayName: String? = null, - @JsonField(name = ["status"]) var status: Int = 0, - @JsonField(name = ["resultMode"]) var resultMode: Int = 0, - @JsonField(name = ["maxVotes"]) var maxVotes: Int = 0, - @JsonField(name = ["votedSelf"]) var votedSelf: ArrayList? = null, - @JsonField(name = ["numVoters"]) var numVoters: Int = 0, - @JsonField(name = ["details"]) var details: ArrayList? = null ) : Parcelable { diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt index 8fac4d169e8..f42f5b0b4b4 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollCreateDialogFragment.kt @@ -72,8 +72,9 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { binding = DialogPollCreateBinding.inflate(LayoutInflater.from(context)) - val dialogBuilder = MaterialAlertDialogBuilder(binding.root.context) - .setView(binding.root) + val dialogBuilder = + MaterialAlertDialogBuilder(binding.root.context) + .setView(binding.root) viewThemeUtils.dialog.colorMaterialAlertDialogBackground(binding.root.context, dialogBuilder) return dialogBuilder.create() @@ -124,21 +125,23 @@ class PollCreateDialogFragment : DialogFragment(), PollCreateOptionsItemListener dismiss() } - binding.pollCreateQuestionTextEdit.addTextChangedListener(object : TextWatcher { - override fun afterTextChanged(s: Editable) { - // unused atm - } + binding.pollCreateQuestionTextEdit.addTextChangedListener( + object : TextWatcher { + override fun afterTextChanged(s: Editable) { + // unused atm + } - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - // unused atm - } + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // unused atm + } - override fun onTextChanged(question: CharSequence, start: Int, before: Int, count: Int) { - if (question.toString() != viewModel.question) { - viewModel.setQuestion(question.toString()) + override fun onTextChanged(question: CharSequence, start: Int, before: Int, count: Int) { + if (question.toString() != viewModel.question) { + viewModel.setQuestion(question.toString()) + } } } - }) + ) binding.pollPrivatePollCheckbox.setOnClickListener { viewModel.setPrivatePoll(binding.pollPrivatePollCheckbox.isChecked) diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollLoadingFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollLoadingFragment.kt index f3a41f5bb99..5aae7fbee27 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollLoadingFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollLoadingFragment.kt @@ -48,11 +48,7 @@ class PollLoadingFragment : Fragment() { fragmentHeight = arguments?.getInt(KEY_FRAGMENT_HEIGHT)!! } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { binding = DialogPollLoadingBinding.inflate(inflater, container, false) binding.root.layoutParams.height = fragmentHeight viewThemeUtils.platform.colorCircularProgressBar(binding.pollLoadingProgressbar, ColorRole.PRIMARY) @@ -64,12 +60,8 @@ class PollLoadingFragment : Fragment() { private const val KEY_FRAGMENT_HEIGHT = "keyFragmentHeight" @JvmStatic - fun newInstance( - fragmentHeight: Int - ): PollLoadingFragment { - val args = bundleOf( - KEY_FRAGMENT_HEIGHT to fragmentHeight - ) + fun newInstance(fragmentHeight: Int): PollLoadingFragment { + val args = bundleOf(KEY_FRAGMENT_HEIGHT to fragmentHeight) val fragment = PollLoadingFragment() fragment.arguments = args diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt index e32986bf5eb..c49e52d8f92 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollMainDialogFragment.kt @@ -103,16 +103,20 @@ class PollMainDialogFragment : DialogFragment() { initVotersAmount(state.showVotersAmount, state.poll.numVoters, false) showVoteScreen() } + is PollMainViewModel.PollResultState -> { initVotersAmount(state.showVotersAmount, state.poll.numVoters, true) showResultsScreen() } + is PollMainViewModel.LoadingState -> { showLoadingScreen() } + is PollMainViewModel.DismissDialogState -> { dismiss() } + else -> {} } } @@ -120,7 +124,7 @@ class PollMainDialogFragment : DialogFragment() { private fun showLoadingScreen() { binding.root.post { - run() { + run { val fragmentHeight = binding.messagePollContentFragment.measuredHeight val contentFragment = PollLoadingFragment.newInstance(fragmentHeight) @@ -151,11 +155,13 @@ class PollMainDialogFragment : DialogFragment() { if (showVotersAmount) { viewThemeUtils.dialog.colorDialogSupportingText(binding.pollVotesAmount) binding.pollVotesAmount.visibility = View.VISIBLE - binding.pollVotesAmount.text = resources.getQuantityString( - R.plurals.polls_amount_voters, - numVoters, - numVoters - ) + binding.pollVotesAmount.text = + resources + .getQuantityString( + R.plurals.polls_amount_voters, + numVoters, + numVoters + ) } else { binding.pollVotesAmount.visibility = View.GONE } @@ -188,13 +194,14 @@ class PollMainDialogFragment : DialogFragment() { pollId: String, name: String ): PollMainDialogFragment { - val args = bundleOf( - KEY_USER_ENTITY to user, - KEY_ROOM_TOKEN to roomTokenParam, - KEY_OWNER_OR_MODERATOR to isOwnerOrModerator, - KEY_POLL_ID to pollId, - KEY_POLL_TITLE to name - ) + val args = + bundleOf( + KEY_USER_ENTITY to user, + KEY_ROOM_TOKEN to roomTokenParam, + KEY_OWNER_OR_MODERATOR to isOwnerOrModerator, + KEY_POLL_ID to pollId, + KEY_POLL_TITLE to name + ) val fragment = PollMainDialogFragment() fragment.arguments = args diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt index 1d1634565de..ac399313aea 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollResultsFragment.kt @@ -64,11 +64,7 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[PollMainViewModel::class.java] } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { binding = DialogPollResultsBinding.inflate(inflater, container, false) return binding.root } @@ -86,11 +82,12 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { } viewModel.items.observe(viewLifecycleOwner) { - val adapter = PollResultsAdapter(parentViewModel.user, this, viewThemeUtils).apply { - if (it != null) { - list = it + val adapter = + PollResultsAdapter(parentViewModel.user, this, viewThemeUtils).apply { + if (it != null) { + list = it + } } - } binding.pollResultsList.adapter = adapter } @@ -123,13 +120,14 @@ class PollResultsFragment : Fragment(), PollResultItemClickListener { if (showEndPollButton) { binding.pollResultsEndPollButton.visibility = View.VISIBLE binding.pollResultsEndPollButton.setOnClickListener { - val dialogBuilder = MaterialAlertDialogBuilder(binding.pollResultsEndPollButton.context) - .setTitle(R.string.polls_end_poll) - .setMessage(R.string.polls_end_poll_confirm) - .setPositiveButton(R.string.polls_end_poll) { _, _ -> - parentViewModel.endPoll() - } - .setNegativeButton(R.string.nc_cancel, null) + val dialogBuilder = + MaterialAlertDialogBuilder(binding.pollResultsEndPollButton.context) + .setTitle(R.string.polls_end_poll) + .setMessage(R.string.polls_end_poll_confirm) + .setPositiveButton(R.string.polls_end_poll) { _, _ -> + parentViewModel.endPoll() + } + .setNegativeButton(R.string.nc_cancel, null) viewThemeUtils.dialog.colorMaterialAlertDialogBackground( binding.pollResultsEndPollButton.context, diff --git a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt index 5fc3a523fa5..160e1f6b79f 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/ui/PollVoteFragment.kt @@ -68,11 +68,7 @@ class PollVoteFragment : Fragment() { parentViewModel = ViewModelProvider(requireParentFragment(), viewModelFactory)[PollMainViewModel::class.java] } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View { binding = DialogPollVoteBinding.inflate(inflater, container, false) return binding.root } @@ -95,10 +91,12 @@ class PollVoteFragment : Fragment() { Log.e(TAG, "Failed to vote on poll.") Snackbar.make(binding.root, R.string.nc_common_error_sorry, Snackbar.LENGTH_LONG).show() } + is PollVoteViewModel.PollVoteHiddenSuccessState -> { Snackbar.make(binding.root, R.string.polls_voted_hidden_success, Snackbar.LENGTH_LONG).show() parentViewModel.dismissDialog() } + is PollVoteViewModel.PollVoteSuccessState -> { parentViewModel.voted() } @@ -157,10 +155,12 @@ class PollVoteFragment : Fragment() { } else { binding.voteOptionsCheckboxesWrapper.removeAllViews() - val layoutParams = LinearLayout.LayoutParams( - ViewGroup.LayoutParams.WRAP_CONTENT, - ViewGroup.LayoutParams.WRAP_CONTENT - ) + val layoutParams = + LinearLayout + .LayoutParams( + ViewGroup.LayoutParams.WRAP_CONTENT, + ViewGroup.LayoutParams.WRAP_CONTENT + ) layoutParams.marginStart = CHECKBOX_MARGIN_LEFT poll.options?.map { option -> @@ -206,13 +206,14 @@ class PollVoteFragment : Fragment() { if (showEndPollButton) { binding.pollVoteEndPollButton.visibility = View.VISIBLE binding.pollVoteEndPollButton.setOnClickListener { - val dialogBuilder = MaterialAlertDialogBuilder(binding.pollVoteEndPollButton.context) - .setTitle(R.string.polls_end_poll) - .setMessage(R.string.polls_end_poll_confirm) - .setPositiveButton(R.string.polls_end_poll) { _, _ -> - parentViewModel.endPoll() - } - .setNegativeButton(R.string.nc_cancel, null) + val dialogBuilder = + MaterialAlertDialogBuilder(binding.pollVoteEndPollButton.context) + .setTitle(R.string.polls_end_poll) + .setMessage(R.string.polls_end_poll_confirm) + .setPositiveButton(R.string.polls_end_poll) { _, _ -> + parentViewModel.endPoll() + } + .setNegativeButton(R.string.nc_cancel, null) viewThemeUtils.dialog.colorMaterialAlertDialogBackground( binding.pollVoteEndPollButton.context, diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt index f6022537b0c..e6ab4e784fe 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollCreateViewModel.kt @@ -33,21 +33,26 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class PollCreateViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { - +class PollCreateViewModel +@Inject +constructor(private val repository: PollRepository) : ViewModel() { private lateinit var roomToken: String sealed interface ViewState + open class PollCreationState(val enableAddOptionButton: Boolean, val enableCreatePollButton: Boolean) : ViewState + object PollCreatedState : ViewState + object PollCreationFailedState : ViewState - private val _viewState: MutableLiveData = MutableLiveData( - PollCreationState( - enableAddOptionButton = true, - enableCreatePollButton = false + private val _viewState: MutableLiveData = + MutableLiveData( + PollCreationState( + enableAddOptionButton = true, + enableCreatePollButton = false + ) ) - ) val viewState: LiveData get() = _viewState @@ -182,7 +187,6 @@ class PollCreateViewModel @Inject constructor(private val repository: PollReposi } inner class PollObserver : Observer { - lateinit var poll: Poll override fun onSubscribe(d: Disposable) = Unit diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt index ba62345dda4..1bc3b40e8b7 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollMainViewModel.kt @@ -34,8 +34,9 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class PollMainViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { - +class PollMainViewModel +@Inject +constructor(private val repository: PollRepository) : ViewModel() { @Inject lateinit var userManager: UserManager @@ -48,8 +49,11 @@ class PollMainViewModel @Inject constructor(private val repository: PollReposito private var editVotes: Boolean = false sealed interface ViewState + object InitialState : ViewState + object DismissDialogState : ViewState + object LoadingState : ViewState open class PollVoteState( diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt index c1de5b8e5fb..8191ab3edc4 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollResultsViewModel.kt @@ -32,16 +32,19 @@ import com.nextcloud.talk.polls.model.Poll import io.reactivex.disposables.Disposable import javax.inject.Inject -class PollResultsViewModel @Inject constructor() : ViewModel() { +class PollResultsViewModel +@Inject +constructor() : ViewModel() { sealed interface ViewState + object InitialState : ViewState private var _poll: Poll? = null val poll: Poll? get() = _poll - private var _itemsOverviewList: ArrayList = ArrayList() - private var _itemsDetailsList: ArrayList = ArrayList() + private var itemsOverviewList: ArrayList = ArrayList() + private var itemsDetailsList: ArrayList = ArrayList() private var _items: MutableLiveData?> = MutableLiveData?>() val items: MutableLiveData?> @@ -71,28 +74,29 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { val votersAmountForThisOption = getVotersAmountForOption(poll, index) val optionsPercent = oneVoteInPercent * votersAmountForThisOption - val pollResultHeaderItem = PollResultHeaderItem( - option, - optionsPercent, - isOptionSelfVoted(poll, index) - ) - _itemsOverviewList.add(pollResultHeaderItem) - _itemsDetailsList.add(pollResultHeaderItem) + val pollResultHeaderItem = + PollResultHeaderItem( + option, + optionsPercent, + isOptionSelfVoted(poll, index) + ) + itemsOverviewList.add(pollResultHeaderItem) + itemsDetailsList.add(pollResultHeaderItem) val voters = poll.details?.filter { it.optionId == index } if (!voters.isNullOrEmpty()) { - _itemsOverviewList.add(PollResultVotersOverviewItem(voters)) + itemsOverviewList.add(PollResultVotersOverviewItem(voters)) } if (!voters.isNullOrEmpty()) { voters.forEach { - _itemsDetailsList.add(PollResultVoterItem(it)) + itemsDetailsList.add(PollResultVoterItem(it)) } } } - _items.value = _itemsOverviewList + _items.value = itemsOverviewList } private fun getVotersAmountForOption(poll: Poll, index: Int): Int { @@ -113,10 +117,10 @@ class PollResultsViewModel @Inject constructor() : ViewModel() { } fun toggleDetails() { - if (_items.value?.containsAll(_itemsDetailsList) == true) { - _items.value = _itemsOverviewList + if (_items.value?.containsAll(itemsDetailsList) == true) { + _items.value = itemsOverviewList } else { - _items.value = _itemsDetailsList + _items.value = itemsDetailsList } } diff --git a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt index 3c5a2d35c99..98f9db4afe8 100644 --- a/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/polls/viewmodels/PollVoteViewModel.kt @@ -34,11 +34,17 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class PollVoteViewModel @Inject constructor(private val repository: PollRepository) : ViewModel() { +class PollVoteViewModel +@Inject +constructor(private val repository: PollRepository) : ViewModel() { sealed interface ViewState + object InitialState : ViewState + open class PollVoteSuccessState : ViewState + open class PollVoteHiddenSuccessState : ViewState + open class PollVoteFailedState : ViewState private val _viewState: MutableLiveData = MutableLiveData(InitialState) @@ -65,11 +71,12 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito } fun selectOption(option: Int, isRadioBox: Boolean) { - _selectedOptions = if (isRadioBox) { - listOf(option) - } else { - _selectedOptions.plus(option) - } + _selectedOptions = + if (isRadioBox) { + listOf(option) + } else { + _selectedOptions.plus(option) + } } fun deSelectOption(option: Int) { @@ -94,16 +101,13 @@ class PollVoteViewModel @Inject constructor(private val repository: PollReposito } fun updateSubmitButton() { - val areSelectedOptionsDifferentToVotedOptions = !( - votedOptions.containsAll(selectedOptions) && - selectedOptions.containsAll(votedOptions) - ) + val areSelectedOptionsDifferentToVotedOptions = + !(votedOptions.containsAll(selectedOptions) && selectedOptions.containsAll(votedOptions)) _submitButtonEnabled.value = areSelectedOptionsDifferentToVotedOptions && selectedOptions.isNotEmpty() } inner class PollObserver : Observer { - lateinit var poll: Poll override fun onSubscribe(d: Disposable) = Unit diff --git a/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt b/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt index 8b7f16478ef..42a27a055db 100644 --- a/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/profile/ProfileActivity.kt @@ -131,55 +131,59 @@ class ProfileActivity : BaseActivity() { ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - DisplayUtils.loadAvatarImage( - currentUser, - binding.avatarImage, - true - ) - } + override fun onNext(genericOverall: GenericOverall) { + DisplayUtils.loadAvatarImage( + currentUser, + binding.avatarImage, + true + ) + } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to delete avatar", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to delete avatar", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } binding.avatarImage.let { ViewCompat.setTransitionName(it, "userAvatar.transitionTag") } ncApi.getUserProfile(credentials, ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl)) .retry(DEFAULT_RETRIES) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(userProfileOverall: UserProfileOverall) { - userInfo = userProfileOverall.ocs!!.data - showUserProfile() - } + override fun onNext(userProfileOverall: UserProfileOverall) { + userInfo = userProfileOverall.ocs!!.data + showUserProfile() + } - override fun onError(e: Throwable) { - setErrorMessageForMultiList( - getString(R.string.userinfo_no_info_headline), - getString(R.string.userinfo_error_text), - R.drawable.ic_list_empty_error - ) - } + override fun onError(e: Throwable) { + setErrorMessageForMultiList( + getString(R.string.userinfo_no_info_headline), + getString(R.string.userinfo_error_text), + R.drawable.ic_list_empty_error + ) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) colorIcons() } @@ -236,25 +240,27 @@ class ProfileActivity : BaseActivity() { ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(userProfileFieldsOverall: UserProfileFieldsOverall) { - editableFields = userProfileFieldsOverall.ocs!!.data!! - adapter!!.notifyDataSetChanged() - } + override fun onNext(userProfileFieldsOverall: UserProfileFieldsOverall) { + editableFields = userProfileFieldsOverall.ocs!!.data!! + adapter!!.notifyDataSetChanged() + } - override fun onError(e: Throwable) { - Log.e(TAG, "Error loading editable user profile from server", e) - edit = false - } + override fun onError(e: Throwable) { + Log.e(TAG, "Error loading editable user profile from server", e) + edit = false + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } else { item.setTitle(R.string.edit) item.icon = ContextCompat.getDrawable(this, R.drawable.ic_edit) @@ -334,26 +340,28 @@ class ProfileActivity : BaseActivity() { ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(userProfileFieldsOverall: UserProfileFieldsOverall) { - editableFields = userProfileFieldsOverall.ocs!!.data!! - invalidateOptionsMenu() - adapter!!.notifyDataSetChanged() - } + override fun onNext(userProfileFieldsOverall: UserProfileFieldsOverall) { + editableFields = userProfileFieldsOverall.ocs!!.data!! + invalidateOptionsMenu() + adapter!!.notifyDataSetChanged() + } - override fun onError(e: Throwable) { - Log.e(TAG, "Error loading editable user profile from server", e) - edit = false - } + override fun onError(e: Throwable) { + Log.e(TAG, "Error loading editable user profile from server", e) + edit = false + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } } @@ -445,37 +453,39 @@ class ProfileActivity : BaseActivity() { .retry(DEFAULT_RETRIES) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(userProfileOverall: GenericOverall) { - Log.d(TAG, "Successfully saved: " + item.text + " as " + item.field) - if (item.field == Field.DISPLAYNAME) { - binding?.userinfoFullName?.text = item.text + override fun onNext(userProfileOverall: GenericOverall) { + Log.d(TAG, "Successfully saved: " + item.text + " as " + item.field) + if (item.field == Field.DISPLAYNAME) { + binding?.userinfoFullName?.text = item.text + } } - } - override fun onError(e: Throwable) { - item.text = userInfo?.getValueByField(item.field) - Snackbar.make( - binding.root, - String.format( - resources!!.getString(R.string.failed_to_save), - item.field - ), - Snackbar.LENGTH_LONG - ).show() - adapter!!.updateFilteredList() - adapter!!.notifyDataSetChanged() - Log.e(TAG, "Failed to saved: " + item.text + " as " + item.field, e) - } + override fun onError(e: Throwable) { + item.text = userInfo?.getValueByField(item.field) + Snackbar.make( + binding.root, + String.format( + resources!!.getString(R.string.failed_to_save), + item.field + ), + Snackbar.LENGTH_LONG + ).show() + adapter!!.updateFilteredList() + adapter!!.notifyDataSetChanged() + Log.e(TAG, "Failed to saved: " + item.text + " as " + item.field, e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } // Scope @@ -509,9 +519,11 @@ class ProfileActivity : BaseActivity() { data ) { uploadAvatar(it.toFile()) } } + ImagePicker.RESULT_ERROR -> { Snackbar.make(binding.root, getError(data), Snackbar.LENGTH_SHORT).show() } + else -> { Log.i(TAG, "Task Cancelled") } @@ -526,11 +538,12 @@ class ProfileActivity : BaseActivity() { file!!.name, file.asRequestBody(IMAGE_PREFIX_GENERIC.toMediaTypeOrNull()) ) - val filePart: MultipartBody.Part = MultipartBody.Part.createFormData( - "files[]", - file.name, - file.asRequestBody(IMAGE_JPG.toMediaTypeOrNull()) - ) + val filePart: MultipartBody.Part = + MultipartBody.Part.createFormData( + "files[]", + file.name, + file.asRequestBody(IMAGE_JPG.toMediaTypeOrNull()) + ) // upload file ncApi.uploadAvatar( @@ -540,29 +553,31 @@ class ProfileActivity : BaseActivity() { ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - DisplayUtils.loadAvatarImage(currentUser, binding?.avatarImage, true) - } + override fun onNext(genericOverall: GenericOverall) { + DisplayUtils.loadAvatarImage(currentUser, binding?.avatarImage, true) + } - override fun onError(e: Throwable) { - Snackbar.make( - binding.root, - context.getString(R.string.default_error_msg), - Snackbar - .LENGTH_LONG - ).show() - Log.e(TAG, "Error uploading avatar", e) - } + override fun onError(e: Throwable) { + Snackbar.make( + binding.root, + context.getString(R.string.default_error_msg), + Snackbar + .LENGTH_LONG + ).show() + Log.e(TAG, "Error uploading avatar", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } fun saveScope(item: UserInfoDetailsItem, userInfo: UserProfileData?) { @@ -576,24 +591,26 @@ class ProfileActivity : BaseActivity() { .retry(DEFAULT_RETRIES) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(userProfileOverall: GenericOverall) { - Log.d(TAG, "Successfully saved: " + item.scope + " as " + item.field) - } + override fun onNext(userProfileOverall: GenericOverall) { + Log.d(TAG, "Successfully saved: " + item.scope + " as " + item.field) + } - override fun onError(e: Throwable) { - item.scope = userInfo?.getScopeByField(item.field) - Log.e(TAG, "Failed to saved: " + item.scope + " as " + item.field, e) - } + override fun onError(e: Throwable) { + item.scope = userInfo?.getScopeByField(item.field) + Log.e(TAG, "Failed to saved: " + item.scope + " as " + item.field, e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } class UserInfoDetailsItem( @@ -643,11 +660,12 @@ class ProfileActivity : BaseActivity() { } override fun onBindViewHolder(holder: ViewHolder, position: Int) { - val item: UserInfoDetailsItem = if (profileActivity.edit) { - displayList!![position] - } else { - filteredDisplayList[position] - } + val item: UserInfoDetailsItem = + if (profileActivity.edit) { + displayList!![position] + } else { + filteredDisplayList[position] + } initScopeElements(item, holder) @@ -688,36 +706,33 @@ class ProfileActivity : BaseActivity() { } } - private fun initUserInfoEditText( - holder: ViewHolder, - item: UserInfoDetailsItem - ) { + private fun initUserInfoEditText(holder: ViewHolder, item: UserInfoDetailsItem) { holder.binding.userInfoEditTextEdit.setText(item.text) holder.binding.userInfoInputLayout.hint = item.hint - holder.binding.userInfoEditTextEdit.addTextChangedListener(object : TextWatcher { - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - // unused atm - } + holder.binding.userInfoEditTextEdit.addTextChangedListener( + object : TextWatcher { + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // unused atm + } - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - if (profileActivity.edit) { - displayList!![holder.adapterPosition].text = holder.binding.userInfoEditTextEdit.text.toString() - } else { - filteredDisplayList[holder.adapterPosition].text = - holder.binding.userInfoEditTextEdit.text.toString() + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + if (profileActivity.edit) { + displayList!![holder.adapterPosition].text = + holder.binding.userInfoEditTextEdit.text.toString() + } else { + filteredDisplayList[holder.adapterPosition].text = + holder.binding.userInfoEditTextEdit.text.toString() + } } - } - override fun afterTextChanged(s: Editable) { - // unused atm + override fun afterTextChanged(s: Editable) { + // unused atm + } } - }) + ) } - private fun initScopeElements( - item: UserInfoDetailsItem, - holder: ViewHolder - ) { + private fun initScopeElements(item: UserInfoDetailsItem, holder: ViewHolder) { if (item.scope == null) { holder.binding.scope.visibility = View.GONE } else { @@ -731,10 +746,11 @@ class ProfileActivity : BaseActivity() { // nothing } } - holder.binding.scope.contentDescription = holder.binding.scope.context.getString( - R.string.scope_toggle_description, - item.hint - ) + holder.binding.scope.contentDescription = + holder.binding.scope.context.getString( + R.string.scope_toggle_description, + item.hint + ) } } diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt index 81f33849a52..4aec93ea631 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepository.kt @@ -23,11 +23,7 @@ package com.nextcloud.talk.raisehand import io.reactivex.Observable interface RequestAssistanceRepository { - fun requestAssistance( - roomToken: String - ): Observable + fun requestAssistance(roomToken: String): Observable - fun withdrawRequestAssistance( - roomToken: String - ): Observable + fun withdrawRequestAssistance(roomToken: String): Observable } diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt index a2ae95de119..561b1a81c99 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/RequestAssistanceRepositoryImpl.kt @@ -56,18 +56,14 @@ class RequestAssistanceRepositoryImpl(private val ncApi: NcApi, currentUserProvi ).map { mapToWithdrawRequestAssistanceModel(it.ocs?.meta!!) } } - private fun mapToRequestAssistanceModel( - response: GenericMeta - ): RequestAssistanceModel { + private fun mapToRequestAssistanceModel(response: GenericMeta): RequestAssistanceModel { val success = response.statusCode == HTTP_OK return RequestAssistanceModel( success ) } - private fun mapToWithdrawRequestAssistanceModel( - response: GenericMeta - ): WithdrawRequestAssistanceModel { + private fun mapToWithdrawRequestAssistanceModel(response: GenericMeta): WithdrawRequestAssistanceModel { val success = response.statusCode == HTTP_OK return WithdrawRequestAssistanceModel( success diff --git a/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt b/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt index f1033a6ed39..6675250cde4 100644 --- a/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/raisehand/viewmodel/RaiseHandViewModel.kt @@ -34,7 +34,9 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class RaiseHandViewModel @Inject constructor(private val repository: RequestAssistanceRepository) : ViewModel() { +class RaiseHandViewModel +@Inject +constructor(private val repository: RequestAssistanceRepository) : ViewModel() { @Inject lateinit var userManager: UserManager @@ -44,7 +46,9 @@ class RaiseHandViewModel @Inject constructor(private val repository: RequestAssi sealed interface ViewState object RaisedHandState : ViewState + object LoweredHandState : ViewState + object ErrorState : ViewState private val _viewState: MutableLiveData = MutableLiveData(LoweredHandState) diff --git a/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt index 73cec07befa..e3e2db7446f 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/DirectReplyReceiver.kt @@ -96,24 +96,26 @@ class DirectReplyReceiver : BroadcastReceiver() { ncApi.sendChatMessage(credentials, url, replyMessage, currentUser.displayName, null, false) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + confirmReplySent() + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to send reply", e) + informReplyFailed() + } + + override fun onComplete() { + // unused atm + } } - - override fun onNext(genericOverall: GenericOverall) { - confirmReplySent() - } - - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to send reply", e) - informReplyFailed() - } - - override fun onComplete() { - // unused atm - } - }) + ) } private fun confirmReplySent() { @@ -147,16 +149,20 @@ class DirectReplyReceiver : BroadcastReceiver() { val previousBuilder = NotificationCompat.Builder(context, previousNotification) // Extract MessagingStyle from the active notification - val previousStyle = NotificationCompat.MessagingStyle - .extractMessagingStyleFromNotification(previousNotification) + val previousStyle = + NotificationCompat + .MessagingStyle + .extractMessagingStyleFromNotification(previousNotification) // Add reply Single.fromCallable { val avatarUrl = ApiUtils.getUrlForAvatar(currentUser.baseUrl, currentUser.userId, false) - val me = Person.Builder() - .setName(currentUser.displayName) - .setIcon(NotificationUtils.loadAvatarSync(avatarUrl, context)) - .build() + val me = + Person + .Builder() + .setName(currentUser.displayName) + .setIcon(NotificationUtils.loadAvatarSync(avatarUrl, context)) + .build() val message = NotificationCompat.MessagingStyle.Message(reply, System.currentTimeMillis(), me) previousStyle?.addMessage(message) diff --git a/app/src/main/java/com/nextcloud/talk/receivers/DismissRecordingAvailableReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/DismissRecordingAvailableReceiver.kt index e1fafbc0f2a..c42417ba1e3 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/DismissRecordingAvailableReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/DismissRecordingAvailableReceiver.kt @@ -78,23 +78,25 @@ class DismissRecordingAvailableReceiver : BroadcastReceiver() { ncApi.sendCommonDeleteRequest(credentials, link) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + cancelNotification(systemNotificationId!!) + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to send dismiss for recording available", e) + } + + override fun onComplete() { + // unused atm + } } - - override fun onNext(genericOverall: GenericOverall) { - cancelNotification(systemNotificationId!!) - } - - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to send dismiss for recording available", e) - } - - override fun onComplete() { - // unused atm - } - }) + ) } private fun cancelNotification(notificationId: Int) { diff --git a/app/src/main/java/com/nextcloud/talk/receivers/MarkAsReadReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/MarkAsReadReceiver.kt index f810ba822b1..565802a5d8b 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/MarkAsReadReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/MarkAsReadReceiver.kt @@ -80,32 +80,36 @@ class MarkAsReadReceiver : BroadcastReceiver() { private fun markAsRead() { val credentials = ApiUtils.getCredentials(currentUser.username, currentUser.token) val apiVersion = ApiUtils.getChatApiVersion(currentUser, intArrayOf(1)) - val url = ApiUtils.getUrlForChatReadMarker( - apiVersion, - currentUser.baseUrl, - roomToken - ) + val url = + ApiUtils + .getUrlForChatReadMarker( + apiVersion, + currentUser.baseUrl, + roomToken + ) ncApi.setChatReadMarker(credentials, url, messageId) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + cancelNotification(systemNotificationId!!) + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to set chat read marker", e) + } + + override fun onComplete() { + // unused atm + } } - - override fun onNext(genericOverall: GenericOverall) { - cancelNotification(systemNotificationId!!) - } - - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to set chat read marker", e) - } - - override fun onComplete() { - // unused atm - } - }) + ) } private fun cancelNotification(notificationId: Int) { diff --git a/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt b/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt index 3a4960a7a42..3e8efb2143c 100644 --- a/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt +++ b/app/src/main/java/com/nextcloud/talk/receivers/ShareRecordingToChatReceiver.kt @@ -78,34 +78,36 @@ class ShareRecordingToChatReceiver : BroadcastReceiver() { ncApi.sendCommonPostRequest(credentials, link) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + cancelNotification(systemNotificationId!!) + + // Here it would make sense to open the chat where the recording was shared to (startActivity...). + // However, as we are in a broadcast receiver, this needs a TaskStackBuilder + // combined with addNextIntentWithParentStack. For further reading, see + // https://developer.android.com/develop/ui/views/notifications/navigation#DirectEntry + + Snackbar.make( + View(context), + context.resources.getString(R.string.nc_all_ok_operation), + Snackbar.LENGTH_LONG + ).show() + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to share recording to chat request", e) + } + + override fun onComplete() { + // unused atm + } } - - override fun onNext(genericOverall: GenericOverall) { - cancelNotification(systemNotificationId!!) - - // Here it would make sense to open the chat where the recording was shared to (startActivity...). - // However, as we are in a broadcast receiver, this needs a TaskStackBuilder - // combined with addNextIntentWithParentStack. For further reading, see - // https://developer.android.com/develop/ui/views/notifications/navigation#DirectEntry - - Snackbar.make( - View(context), - context.resources.getString(R.string.nc_all_ok_operation), - Snackbar.LENGTH_LONG - ).show() - } - - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to share recording to chat request", e) - } - - override fun onComplete() { - // unused atm - } - }) + ) } private fun cancelNotification(notificationId: Int) { diff --git a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt index fcec2209aa1..99b8f12fdde 100644 --- a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/activities/RemoteFileBrowserActivity.kt @@ -74,12 +74,13 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe private var filesSelectionDoneMenuItem: MenuItem? = null - private val onBackPressedCallback = object : OnBackPressedCallback(true) { - override fun handleOnBackPressed() { - setResult(Activity.RESULT_CANCELED) - finish() + private val onBackPressedCallback = + object : OnBackPressedCallback(true) { + override fun handleOnBackPressed() { + setResult(Activity.RESULT_CANCELED) + finish() + } } - } override fun onCreate(savedInstanceState: Bundle?) { super.onCreate(savedInstanceState) @@ -128,12 +129,15 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe is RemoteFileBrowserItemsViewModel.LoadingItemsState, RemoteFileBrowserItemsViewModel.InitialState -> { showLoading() } + is RemoteFileBrowserItemsViewModel.NoRemoteFileItemsState -> { showEmpty() } + is RemoteFileBrowserItemsViewModel.LoadedState -> { loadList(state, mimeTypeSelectionFilter) } + is RemoteFileBrowserItemsViewModel.FinishState -> { finishWithResult(state.selectedPaths) } @@ -159,31 +163,30 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe } } - private fun loadList( - state: RemoteFileBrowserItemsViewModel.LoadedState, - mimeTypeSelectionFilter: String? - ) { + private fun loadList(state: RemoteFileBrowserItemsViewModel.LoadedState, mimeTypeSelectionFilter: String?) { val remoteFileBrowserItems = state.items Log.d(TAG, "Items received: $remoteFileBrowserItems") // TODO make showGrid based on preferences (when available) val showGrid = false - val layoutManager = if (showGrid) { - GridLayoutManager(this, SPAN_COUNT) - } else { - LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) - } + val layoutManager = + if (showGrid) { + GridLayoutManager(this, SPAN_COUNT) + } else { + LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + } // TODO do not needlessly recreate adapter if it can be reused - val adapter = RemoteFileBrowserItemsAdapter( - showGrid = showGrid, - mimeTypeSelectionFilter = mimeTypeSelectionFilter, - user = currentUserProvider.currentUser.blockingGet(), - selectionInterface = this, - viewThemeUtils = viewThemeUtils, - dateUtils = dateUtils, - onItemClicked = viewModel::onItemClicked - ) + val adapter = + RemoteFileBrowserItemsAdapter( + showGrid = showGrid, + mimeTypeSelectionFilter = mimeTypeSelectionFilter, + user = currentUserProvider.currentUser.blockingGet(), + selectionInterface = this, + viewThemeUtils = viewThemeUtils, + dateUtils = dateUtils, + onItemClicked = viewModel::onItemClicked + ) adapter.items = remoteFileBrowserItems binding.recyclerView.adapter = adapter @@ -206,8 +209,9 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe } private fun changeSorting() { - val newFragment: DialogFragment = SortingOrderDialogFragment - .newInstance(FileSortOrder.getFileSortOrder(viewModel.fileSortOrder.value!!.name)) + val newFragment: DialogFragment = + SortingOrderDialogFragment + .newInstance(FileSortOrder.getFileSortOrder(viewModel.fileSortOrder.value!!.name)) newFragment.show( supportFragmentManager, SortingOrderDialogFragment.SORTING_ORDER_FRAGMENT @@ -220,10 +224,12 @@ class RemoteFileBrowserActivity : AppCompatActivity(), SelectionInterface, Swipe onBackPressedDispatcher.onBackPressed() true } + R.id.files_selection_done -> { viewModel.onSelectionDone() true } + else -> { return super.onOptionsItemSelected(item) } diff --git a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/adapters/RemoteFileBrowserItemsListViewHolder.kt b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/adapters/RemoteFileBrowserItemsListViewHolder.kt index 7ceba517a6c..7c7c100ac32 100644 --- a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/adapters/RemoteFileBrowserItemsListViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/adapters/RemoteFileBrowserItemsListViewHolder.kt @@ -92,11 +92,13 @@ class RemoteFileBrowserItemsListViewHolder( val placeholder = viewThemeUtils.talk.getPlaceholderImage(binding.root.context, item.mimeType) if (item.hasPreview) { - val path = ApiUtils.getUrlForFilePreviewWithRemotePath( - currentUser.baseUrl, - item.path, - fileIcon.context.resources.getDimensionPixelSize(R.dimen.small_item_height) - ) + val path = + ApiUtils + .getUrlForFilePreviewWithRemotePath( + currentUser.baseUrl, + item.path, + fileIcon.context.resources.getDimensionPixelSize(R.dimen.small_item_height) + ) if (path.isNotEmpty()) { fileIcon.loadImage(path, currentUser, placeholder) } @@ -105,11 +107,13 @@ class RemoteFileBrowserItemsListViewHolder( } binding.filenameTextView.text = item.displayName - binding.fileModifiedInfo.text = String.format( - binding.fileModifiedInfo.context.getString(R.string.nc_last_modified), - Formatter.formatShortFileSize(binding.fileModifiedInfo.context, item.size), - dateUtils.getLocalDateTimeStringFromTimestamp(item.modifiedTimestamp) - ) + binding.fileModifiedInfo.text = + String + .format( + binding.fileModifiedInfo.context.getString(R.string.nc_last_modified), + Formatter.formatShortFileSize(binding.fileModifiedInfo.context, item.size), + dateUtils.getLocalDateTimeStringFromTimestamp(item.modifiedTimestamp) + ) binding.selectFileCheckbox.isChecked = selectionInterface.isPathSelected(item.path!!) } diff --git a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/model/RemoteFileBrowserItem.kt b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/model/RemoteFileBrowserItem.kt index 3dbfc89bb66..77cb352dfc0 100644 --- a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/model/RemoteFileBrowserItem.kt +++ b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/model/RemoteFileBrowserItem.kt @@ -33,7 +33,6 @@ data class RemoteFileBrowserItem( var modifiedTimestamp: Long = 0, var size: Long = 0, var isFile: Boolean = false, - // Used for remote files var remoteId: String? = null, var hasPreview: Boolean = false, diff --git a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/repositories/RemoteFileBrowserItemsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/repositories/RemoteFileBrowserItemsRepositoryImpl.kt index ad61b745c3e..96cd0b3e7d3 100644 --- a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/repositories/RemoteFileBrowserItemsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/repositories/RemoteFileBrowserItemsRepositoryImpl.kt @@ -28,15 +28,16 @@ import io.reactivex.Observable import okhttp3.OkHttpClient import javax.inject.Inject -class RemoteFileBrowserItemsRepositoryImpl @Inject constructor( +class RemoteFileBrowserItemsRepositoryImpl +@Inject +constructor( private val okHttpClient: OkHttpClient, private val userProvider: CurrentUserProviderNew ) : RemoteFileBrowserItemsRepository { private val user: User get() = userProvider.currentUser.blockingGet() - override fun listFolder(path: String): - Observable> { + override fun listFolder(path: String): Observable> { return Observable.fromCallable { val operation = ReadFolderListingOperation( diff --git a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt index ac402ea143d..6b2463ef6ef 100644 --- a/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/remotefilebrowser/viewmodels/RemoteFileBrowserItemsViewModel.kt @@ -66,10 +66,15 @@ constructor( ) : ViewModel() { sealed interface ViewState + object InitialState : ViewState + object NoRemoteFileItemsState : ViewState + object LoadingItemsState : ViewState + class LoadedState(val items: List) : ViewState + class FinishState(val selectedPaths: Set) : ViewState private val initialSortOrder = FileSortOrder.getFileSortOrder(appPreferences.sorting) @@ -140,6 +145,7 @@ constructor( is LoadedState, LoadingItemsState -> { this@RemoteFileBrowserItemsViewModel._viewState.value = LoadedState(items) } + else -> return } } diff --git a/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepository.kt index 7b8cbe85336..7cbc1503163 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepository.kt @@ -25,11 +25,7 @@ import com.nextcloud.talk.models.domain.StopCallRecordingModel import io.reactivex.Observable interface CallRecordingRepository { - fun startRecording( - roomToken: String - ): Observable + fun startRecording(roomToken: String): Observable - fun stopRecording( - roomToken: String - ): Observable + fun stopRecording(roomToken: String): Observable } diff --git a/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepositoryImpl.kt index fac6baf1a28..f5a27bc17f8 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/callrecording/CallRecordingRepositoryImpl.kt @@ -36,9 +36,7 @@ class CallRecordingRepositoryImpl(private val ncApi: NcApi, currentUserProvider: var apiVersion = 1 - override fun startRecording( - roomToken: String - ): Observable { + override fun startRecording(roomToken: String): Observable { return ncApi.startRecording( credentials, ApiUtils.getUrlForRecording( @@ -50,9 +48,7 @@ class CallRecordingRepositoryImpl(private val ncApi: NcApi, currentUserProvider: ).map { mapToStartCallRecordingModel(it.ocs?.meta!!) } } - override fun stopRecording( - roomToken: String - ): Observable { + override fun stopRecording(roomToken: String): Observable { return ncApi.stopRecording( credentials, ApiUtils.getUrlForRecording( @@ -63,18 +59,14 @@ class CallRecordingRepositoryImpl(private val ncApi: NcApi, currentUserProvider: ).map { mapToStopCallRecordingModel(it.ocs?.meta!!) } } - private fun mapToStartCallRecordingModel( - response: GenericMeta - ): StartCallRecordingModel { + private fun mapToStartCallRecordingModel(response: GenericMeta): StartCallRecordingModel { val success = response.statusCode == HTTP_OK return StartCallRecordingModel( success ) } - private fun mapToStopCallRecordingModel( - response: GenericMeta - ): StopCallRecordingModel { + private fun mapToStopCallRecordingModel(response: GenericMeta): StopCallRecordingModel { val success = response.statusCode == HTTP_OK return StopCallRecordingModel( success diff --git a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt index d1e6e8edc55..773d64ef854 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepository.kt @@ -41,5 +41,6 @@ interface ConversationsRepository { data class ResendInvitationsResult( val successful: Boolean ) + fun resendInvitations(token: String): Observable } diff --git a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt index 73d69f4b104..950dfa8cda3 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/conversations/ConversationsRepositoryImpl.kt @@ -40,58 +40,66 @@ class ConversationsRepositoryImpl(private val api: NcApi, private val userProvid get() = ApiUtils.getCredentials(user.username, user.token) override fun allowGuests(token: String, allow: Boolean): Observable { - val url = ApiUtils.getUrlForRoomPublic( - apiVersion(), - user.baseUrl, - token - ) + val url = + ApiUtils + .getUrlForRoomPublic( + apiVersion(), + user.baseUrl, + token + ) - val apiObservable = if (allow) { - api.makeRoomPublic( - credentials, - url - ) - } else { - api.makeRoomPrivate( - credentials, - url - ) - } + val apiObservable = + if (allow) { + api.makeRoomPublic( + credentials, + url + ) + } else { + api.makeRoomPrivate( + credentials, + url + ) + } return apiObservable.map { AllowGuestsResult(it.ocs!!.meta!!.statusCode == STATUS_CODE_OK && allow) } } override fun password(password: String, token: String): Observable { - val apiObservable = api.setPassword2( - credentials, - ApiUtils.getUrlForRoomPassword( - apiVersion(), - user.baseUrl!!, - token - ), - password - ) + val apiObservable = + api + .setPassword2( + credentials, + ApiUtils.getUrlForRoomPassword( + apiVersion(), + user.baseUrl!!, + token + ), + password + ) return apiObservable.map { - val passwordPolicyMessage = if (it.code() == STATUS_CODE_BAD_REQUEST) { - LoganSquare.parse(it.errorBody()!!.string(), PasswordOverall::class.java).ocs!!.data!! - .message!! - } else { - "" - } + val passwordPolicyMessage = + if (it.code() == STATUS_CODE_BAD_REQUEST) { + LoganSquare.parse(it.errorBody()!!.string(), PasswordOverall::class.java).ocs!!.data!! + .message!! + } else { + "" + } PasswordResult(it.isSuccessful, passwordPolicyMessage.isNotEmpty(), passwordPolicyMessage) } } override fun resendInvitations(token: String): Observable { - val apiObservable = api.resendParticipantInvitations( - credentials, - ApiUtils.getUrlForParticipantsResendInvitations( - apiVersion(), - user.baseUrl!!, - token - ) - ) + val apiObservable = + api + .resendParticipantInvitations( + credentials, + ApiUtils.getUrlForParticipantsResendInvitations( + apiVersion(), + user.baseUrl!!, + token + ) + ) return apiObservable.map { ResendInvitationsResult(true) diff --git a/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepository.kt b/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepository.kt index a1605094d13..63d92f1b2df 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepository.kt @@ -26,15 +26,7 @@ import com.nextcloud.talk.models.json.chat.ChatMessage import io.reactivex.Observable interface ReactionsRepository { - fun addReaction( - roomToken: String, - message: ChatMessage, - emoji: String - ): Observable + fun addReaction(roomToken: String, message: ChatMessage, emoji: String): Observable - fun deleteReaction( - roomToken: String, - message: ChatMessage, - emoji: String - ): Observable + fun deleteReaction(roomToken: String, message: ChatMessage, emoji: String): Observable } diff --git a/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt index 47a6e4ec76f..ca09be79d02 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/reactions/ReactionsRepositoryImpl.kt @@ -35,11 +35,7 @@ class ReactionsRepositoryImpl(private val ncApi: NcApi, currentUserProvider: Cur val currentUser: User = currentUserProvider.currentUser.blockingGet() val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) - override fun addReaction( - roomToken: String, - message: ChatMessage, - emoji: String - ): Observable { + override fun addReaction(roomToken: String, message: ChatMessage, emoji: String): Observable { return ncApi.sendReaction( credentials, ApiUtils.getUrlForMessageReaction( diff --git a/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt index 5a9bb813fa1..fbb39ac7b31 100644 --- a/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/repositories/unifiedsearch/UnifiedSearchRepositoryImpl.kt @@ -43,14 +43,15 @@ class UnifiedSearchRepositoryImpl(private val api: NcApi, private val userProvid cursor: Int, limit: Int ): Observable> { - val apiObservable = api.performUnifiedSearch( - credentials, - ApiUtils.getUrlForUnifiedSearch(user.baseUrl!!, PROVIDER_TALK_MESSAGE), - searchTerm, - null, - limit, - cursor - ) + val apiObservable = + api.performUnifiedSearch( + credentials, + ApiUtils.getUrlForUnifiedSearch(user.baseUrl!!, PROVIDER_TALK_MESSAGE), + searchTerm, + null, + limit, + cursor + ) return apiObservable.map { mapToMessageResults(it.ocs?.data!!, searchTerm, limit) } } @@ -60,14 +61,15 @@ class UnifiedSearchRepositoryImpl(private val api: NcApi, private val userProvid cursor: Int, limit: Int ): Observable> { - val apiObservable = api.performUnifiedSearch( - credentials, - ApiUtils.getUrlForUnifiedSearch(user.baseUrl!!, PROVIDER_TALK_MESSAGE_CURRENT), - searchTerm, - fromUrlForRoom(roomToken), - limit, - cursor - ) + val apiObservable = + api.performUnifiedSearch( + credentials, + ApiUtils.getUrlForUnifiedSearch(user.baseUrl!!, PROVIDER_TALK_MESSAGE_CURRENT), + searchTerm, + fromUrlForRoom(roomToken), + limit, + cursor + ) return apiObservable.map { mapToMessageResults(it.ocs?.data!!, searchTerm, limit) } } @@ -80,8 +82,11 @@ class UnifiedSearchRepositoryImpl(private val api: NcApi, private val userProvid private const val ATTRIBUTE_CONVERSATION = "conversation" private const val ATTRIBUTE_MESSAGE_ID = "messageId" - private fun mapToMessageResults(data: UnifiedSearchResponseData, searchTerm: String, limit: Int): - UnifiedSearchRepository.UnifiedSearchResults { + private fun mapToMessageResults( + data: UnifiedSearchResponseData, + searchTerm: String, + limit: Int + ): UnifiedSearchRepository.UnifiedSearchResults { val entries = data.entries?.map { it -> mapToMessage(it, searchTerm) } val cursor = data.cursor ?: 0 val hasMore = entries?.size == limit diff --git a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt index 2bf2c3d47db..dafbbbf712e 100644 --- a/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/settings/SettingsActivity.kt @@ -157,11 +157,13 @@ class SettingsActivity : BaseActivity() { binding.settingsIncognitoKeyboard.visibility = View.GONE } - binding.settingsScreenLockSummary.text = String.format( - Locale.getDefault(), - resources!!.getString(R.string.nc_settings_screen_lock_desc), - resources!!.getString(R.string.nc_app_product_name) - ) + binding.settingsScreenLockSummary.text = + String + .format( + Locale.getDefault(), + resources!!.getString(R.string.nc_settings_screen_lock_desc), + resources!!.getString(R.string.nc_app_product_name) + ) setupPrivacyUrl() setupSourceCodeUrl() @@ -195,9 +197,10 @@ class SettingsActivity : BaseActivity() { setupScreenLockSetting() if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { - binding.settingsNotificationsTitle.text = resources!!.getString( - R.string.nc_settings_notification_sounds_post_oreo - ) + binding.settingsNotificationsTitle.text = + resources!!.getString( + R.string.nc_settings_notification_sounds_post_oreo + ) } val callRingtoneUri = getCallRingtoneUri(context, (appPreferences)) @@ -405,9 +408,10 @@ class SettingsActivity : BaseActivity() { typingStatusFlow = appPreferences.readBoolean(AppPreferencesImpl.TYPING_STATUS) phoneBookIntegrationFlow = appPreferences.readBoolean(AppPreferencesImpl.PHONE_BOOK_INTEGRATION) - var pos = resources.getStringArray(R.array.screen_lock_timeout_entry_values).indexOf( - appPreferences.screenLockTimeout - ) + var pos = + resources.getStringArray(R.array.screen_lock_timeout_entry_values).indexOf( + appPreferences.screenLockTimeout + ) binding.settingsScreenLockTimeoutLayoutDropdown.setText( resources.getStringArray(R.array.screen_lock_timeout_descriptions)[pos] ) @@ -444,15 +448,16 @@ class SettingsActivity : BaseActivity() { private fun showRemoveAccountWarning() { binding.messageText.context?.let { - val materialAlertDialogBuilder = MaterialAlertDialogBuilder(it) - .setTitle(R.string.nc_settings_remove_account) - .setMessage(R.string.nc_settings_remove_confirmation) - .setPositiveButton(R.string.nc_settings_remove) { _, _ -> - removeCurrentAccount() - } - .setNegativeButton(R.string.nc_cancel) { _, _ -> - // unused atm - } + val materialAlertDialogBuilder = + MaterialAlertDialogBuilder(it) + .setTitle(R.string.nc_settings_remove_account) + .setMessage(R.string.nc_settings_remove_confirmation) + .setPositiveButton(R.string.nc_settings_remove) { _, _ -> + removeCurrentAccount() + } + .setNegativeButton(R.string.nc_cancel) { _, _ -> + // unused atm + } viewThemeUtils.dialog.colorMaterialAlertDialogBackground( it, @@ -479,10 +484,12 @@ class SettingsActivity : BaseActivity() { when (workInfo.state) { WorkInfo.State.SUCCEEDED -> { - val text = String.format( - context.resources.getString(R.string.nc_deleted_user), - currentUser!!.displayName - ) + val text = + String + .format( + context.resources.getString(R.string.nc_deleted_user), + currentUser!!.displayName + ) Toast.makeText( context, text, @@ -490,6 +497,7 @@ class SettingsActivity : BaseActivity() { ).show() restartApp() } + WorkInfo.State.FAILED, WorkInfo.State.CANCELLED -> { Toast.makeText( context, @@ -650,47 +658,50 @@ class SettingsActivity : BaseActivity() { ?.alpha(0.0f) ?.setDuration(DURATION) ?.setStartDelay(START_DELAY) - ?.setListener(object : AnimatorListenerAdapter() { - override fun onAnimationEnd(animation: Animator) { - super.onAnimationEnd(animation) - binding.messageText.visibility = View.GONE + ?.setListener( + object : AnimatorListenerAdapter() { + override fun onAnimationEnd(animation: Animator) { + super.onAnimationEnd(animation) + binding.messageText.visibility = View.GONE + } } - }) + ) } else { binding.messageText.visibility = View.GONE } } private fun setupProfileQueryDisposable() { - profileQueryDisposable = ncApi.getUserProfile( - credentials, - ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl) - ) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { userProfileOverall: UserProfileOverall -> - var displayName: String? = null - if (!TextUtils.isEmpty( - userProfileOverall.ocs!!.data!!.displayName - ) - ) { - displayName = userProfileOverall.ocs!!.data!!.displayName - } else if (!TextUtils.isEmpty( - userProfileOverall.ocs!!.data!!.displayNameAlt - ) - ) { - displayName = userProfileOverall.ocs!!.data!!.displayNameAlt - } - if ((!TextUtils.isEmpty(displayName) && !(displayName == currentUser!!.displayName))) { - currentUser!!.displayName = displayName - userManager.updateOrCreateUser(currentUser!!) - binding.nameText.text = currentUser!!.displayName - } - }, - { dispose(profileQueryDisposable) }, - { dispose(profileQueryDisposable) } + profileQueryDisposable = + ncApi.getUserProfile( + credentials, + ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl) ) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { userProfileOverall: UserProfileOverall -> + var displayName: String? = null + if (!TextUtils.isEmpty( + userProfileOverall.ocs!!.data!!.displayName + ) + ) { + displayName = userProfileOverall.ocs!!.data!!.displayName + } else if (!TextUtils.isEmpty( + userProfileOverall.ocs!!.data!!.displayNameAlt + ) + ) { + displayName = userProfileOverall.ocs!!.data!!.displayNameAlt + } + if ((!TextUtils.isEmpty(displayName) && !(displayName == currentUser!!.displayName))) { + currentUser!!.displayName = displayName + userManager.updateOrCreateUser(currentUser!!) + binding.nameText.text = currentUser!!.displayName + } + }, + { dispose(profileQueryDisposable) }, + { dispose(profileQueryDisposable) } + ) } private fun setupServerAgeWarning() { @@ -1036,27 +1047,29 @@ class SettingsActivity : BaseActivity() { ApiUtils.getUrlForUserProfile(currentUser!!.baseUrl) ).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(userProfileOverall: UserProfileOverall) { - if (userProfileOverall.ocs!!.data!!.phone?.isEmpty() == true) { - askForPhoneNumber() - } else { - Log.d(TAG, "phone number already set") + override fun onNext(userProfileOverall: UserProfileOverall) { + if (userProfileOverall.ocs!!.data!!.phone?.isEmpty() == true) { + askForPhoneNumber() + } else { + Log.d(TAG, "phone number already set") + } } - } - override fun onError(e: Throwable) { - // unused atm - } + override fun onError(e: Throwable) { + // unused atm + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun askForPhoneNumber() { @@ -1070,41 +1083,48 @@ class SettingsActivity : BaseActivity() { ) phoneNumberField.inputType = InputType.TYPE_CLASS_PHONE phoneNumberField.setText("+") - phoneNumberField.addTextChangedListener(object : TextWatcher { - override fun afterTextChanged(s: Editable) { - // unused atm - } + phoneNumberField.addTextChangedListener( + object : TextWatcher { + override fun afterTextChanged(s: Editable) { + // unused atm + } - override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { - // unused atm - } + override fun beforeTextChanged(s: CharSequence, start: Int, count: Int, after: Int) { + // unused atm + } - override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { - phoneNumberInputLayout.helperText = "" + override fun onTextChanged(s: CharSequence, start: Int, before: Int, count: Int) { + phoneNumberInputLayout.helperText = "" + } } - }) + ) phoneNumberInputLayout.addView(phoneNumberField) phoneNumberLayoutWrapper.addView(phoneNumberInputLayout) - val dialogBuilder = MaterialAlertDialogBuilder(phoneNumberInputLayout.context) - .setTitle(R.string.nc_settings_phone_book_integration_phone_number_dialog_title) - .setMessage(R.string.nc_settings_phone_book_integration_phone_number_dialog_description) - .setView(phoneNumberLayoutWrapper) - .setPositiveButton(context.resources.getString(R.string.nc_common_set), null) - .setNegativeButton(context.resources.getString(R.string.nc_common_skip), null) + val dialogBuilder = + MaterialAlertDialogBuilder(phoneNumberInputLayout.context) + .setTitle(R.string.nc_settings_phone_book_integration_phone_number_dialog_title) + .setMessage(R.string.nc_settings_phone_book_integration_phone_number_dialog_description) + .setView(phoneNumberLayoutWrapper) + .setPositiveButton(context.resources.getString(R.string.nc_common_set), null) + .setNegativeButton(context.resources.getString(R.string.nc_common_skip), null) viewThemeUtils.dialog.colorMaterialAlertDialogBackground(phoneNumberInputLayout.context, dialogBuilder) val dialog = dialogBuilder.create() - dialog.setOnShowListener(object : OnShowListener { - override fun onShow(dialogInterface: DialogInterface) { - val button = dialog.getButton(AlertDialog.BUTTON_POSITIVE) - button.setOnClickListener(object : View.OnClickListener { - override fun onClick(view: View) { - setPhoneNumber(phoneNumberInputLayout, dialog) - } - }) + dialog.setOnShowListener( + object : OnShowListener { + override fun onShow(dialogInterface: DialogInterface) { + val button = dialog.getButton(AlertDialog.BUTTON_POSITIVE) + button.setOnClickListener( + object : View.OnClickListener { + override fun onClick(view: View) { + setPhoneNumber(phoneNumberInputLayout, dialog) + } + } + ) + } } - }) + ) dialog.show() @@ -1123,43 +1143,49 @@ class SettingsActivity : BaseActivity() { phoneNumber ).subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - when (val statusCode = genericOverall.ocs?.meta?.statusCode) { - HTTP_CODE_OK -> { - dialog.dismiss() - Snackbar.make( - binding.root, - context.resources.getString( - R.string.nc_settings_phone_book_integration_phone_number_dialog_success - ), - Snackbar.LENGTH_LONG - ).show() + override fun onNext(genericOverall: GenericOverall) { + when (val statusCode = genericOverall.ocs?.meta?.statusCode) { + HTTP_CODE_OK -> { + dialog.dismiss() + Snackbar + .make( + binding.root, + context.resources.getString( + R.string.nc_settings_phone_book_integration_phone_number_dialog_success + ), + Snackbar.LENGTH_LONG + ).show() + } + + else -> { + textInputLayout.helperText = + context.resources.getString( + R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid + ) + Log.d(TAG, "failed to set phoneNumber. statusCode=$statusCode") + } } - else -> { - textInputLayout.helperText = context.resources.getString( + } + + override fun onError(e: Throwable) { + textInputLayout.helperText = + context.resources.getString( R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid ) - Log.d(TAG, "failed to set phoneNumber. statusCode=$statusCode") - } + Log.e(TAG, "setPhoneNumber error", e) } - } - - override fun onError(e: Throwable) { - textInputLayout.helperText = context.resources.getString( - R.string.nc_settings_phone_book_integration_phone_number_dialog_invalid - ) - Log.e(TAG, "setPhoneNumber error", e) - } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun observeReadPrivacy() { @@ -1177,24 +1203,26 @@ class SettingsActivity : BaseActivity() { ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - // unused atm - } - - override fun onError(e: Throwable) { - appPreferences.setReadPrivacy(!newBoolean) - binding.settingsReadPrivacySwitch.isChecked = !newBoolean - } - - override fun onComplete() { - // unused atm + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + // unused atm + } + + override fun onError(e: Throwable) { + appPreferences.setReadPrivacy(!newBoolean) + binding.settingsReadPrivacySwitch.isChecked = !newBoolean + } + + override fun onComplete() { + // unused atm + } } - }) + ) } } } @@ -1215,25 +1243,27 @@ class SettingsActivity : BaseActivity() { ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(genericOverall: GenericOverall) { - loadCapabilitiesAndUpdateSettings() - Log.i(TAG, "onNext called typing status set") + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(genericOverall: GenericOverall) { + loadCapabilitiesAndUpdateSettings() + Log.i(TAG, "onNext called typing status set") + } + + override fun onError(e: Throwable) { + appPreferences.typingStatus = !newBoolean + binding.settingsTypingStatusSwitch.isChecked = !newBoolean + } + + override fun onComplete() { + // unused atm + } } - - override fun onError(e: Throwable) { - appPreferences.typingStatus = !newBoolean - binding.settingsTypingStatusSwitch.isChecked = !newBoolean - } - - override fun onComplete() { - // unused atm - } - }) + ) } } } diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/activities/SharedItemsActivity.kt b/app/src/main/java/com/nextcloud/talk/shareditems/activities/SharedItemsActivity.kt index 3e47243c01a..9598be52e44 100644 --- a/app/src/main/java/com/nextcloud/talk/shareditems/activities/SharedItemsActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/shareditems/activities/SharedItemsActivity.kt @@ -99,14 +99,16 @@ class SharedItemsActivity : AppCompatActivity() { handleModelChange(state, user, roomToken, isUserConversationOwnerOrModerator) } - binding.imageRecycler.addOnScrollListener(object : RecyclerView.OnScrollListener() { - override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { - super.onScrollStateChanged(recyclerView, newState) - if (!recyclerView.canScrollVertically(1) && newState == RecyclerView.SCROLL_STATE_IDLE) { - viewModel.loadNextItems() + binding.imageRecycler.addOnScrollListener( + object : RecyclerView.OnScrollListener() { + override fun onScrollStateChanged(recyclerView: RecyclerView, newState: Int) { + super.onScrollStateChanged(recyclerView, newState) + if (!recyclerView.canScrollVertically(1) && newState == RecyclerView.SCROLL_STATE_IDLE) { + viewModel.loadNextItems() + } } } - }) + ) viewModel.initialize(user, roomToken) } @@ -122,35 +124,41 @@ class SharedItemsActivity : AppCompatActivity() { is SharedItemsViewModel.LoadingItemsState, SharedItemsViewModel.InitialState -> { showLoading() } + is SharedItemsViewModel.NoSharedItemsState -> { showEmpty() } + is SharedItemsViewModel.LoadedState -> { val sharedMediaItems = state.items Log.d(TAG, "Items received: $sharedMediaItems") val showGrid = state.selectedType == SharedItemType.MEDIA - val layoutManager = if (showGrid) { - GridLayoutManager(this, SPAN_COUNT) - } else { - LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) - } - - val adapter = SharedItemsAdapter( - showGrid, - user, - roomToken, - isUserConversationOwnerOrModerator, - viewThemeUtils - ).apply { - items = sharedMediaItems.items - } + val layoutManager = + if (showGrid) { + GridLayoutManager(this, SPAN_COUNT) + } else { + LinearLayoutManager(this, LinearLayoutManager.VERTICAL, false) + } + + val adapter = + SharedItemsAdapter( + showGrid, + user, + roomToken, + isUserConversationOwnerOrModerator, + viewThemeUtils + ).apply { + items = sharedMediaItems.items + } binding.imageRecycler.adapter = adapter binding.imageRecycler.layoutManager = layoutManager } + is SharedItemsViewModel.TypesLoadedState -> { initTabs(state.types) } + else -> {} } @@ -239,15 +247,17 @@ class SharedItemsActivity : AppCompatActivity() { binding.sharedItemsTabs.addTab(tabOther) } - binding.sharedItemsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab) { - viewModel.initialLoadItems(tab.tag as SharedItemType) - } + binding.sharedItemsTabs.addOnTabSelectedListener( + object : TabLayout.OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + viewModel.initialLoadItems(tab.tag as SharedItemType) + } - override fun onTabUnselected(tab: TabLayout.Tab) = Unit + override fun onTabUnselected(tab: TabLayout.Tab) = Unit - override fun onTabReselected(tab: TabLayout.Tab) = Unit - }) + override fun onTabReselected(tab: TabLayout.Tab) = Unit + } + ) } override fun onOptionsItemSelected(item: MenuItem): Boolean { diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsAdapter.kt b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsAdapter.kt index 009e2a5977a..f3d7aa3bce5 100644 --- a/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsAdapter.kt +++ b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsAdapter.kt @@ -87,13 +87,15 @@ class SharedItemsAdapter( } private fun showPoll(item: SharedItem, context: Context) { - val pollVoteDialog = PollMainDialogFragment.newInstance( - user, - roomToken, - isUserConversationOwnerOrModerator, - item.id, - item.name - ) + val pollVoteDialog = + PollMainDialogFragment + .newInstance( + user, + roomToken, + isUserConversationOwnerOrModerator, + item.id, + item.name + ) pollVoteDialog.show( (context as SharedItemsActivity).supportFragmentManager, TAG diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsListViewHolder.kt b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsListViewHolder.kt index f41cbebb3a6..aa7bd2948fd 100644 --- a/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsListViewHolder.kt +++ b/app/src/main/java/com/nextcloud/talk/shareditems/adapters/SharedItemsListViewHolder.kt @@ -57,12 +57,14 @@ class SharedItemsListViewHolder( super.onBind(item) binding.fileName.text = item.name - binding.fileSize.text = item.fileSize.let { - Formatter.formatShortFileSize( - binding.fileSize.context, - it - ) - } + binding.fileSize.text = + item + .fileSize.let { + Formatter.formatShortFileSize( + binding.fileSize.context, + it + ) + } binding.fileDate.text = item.dateTime binding.actor.text = item.actorName } diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt index 174afc7cafe..edf0a180a44 100644 --- a/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepository.kt @@ -27,16 +27,9 @@ import com.nextcloud.talk.shareditems.model.SharedItems import io.reactivex.Observable interface SharedItemsRepository { - fun media( - parameters: Parameters, - type: SharedItemType - ): Observable? + fun media(parameters: Parameters, type: SharedItemType): Observable? - fun media( - parameters: Parameters, - type: SharedItemType, - lastKnownMessageId: Int? - ): Observable? + fun media(parameters: Parameters, type: SharedItemType, lastKnownMessageId: Int?): Observable? fun availableTypes(parameters: Parameters): Observable> diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt index 450a477c97b..e2b3eae6c97 100644 --- a/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/shareditems/repositories/SharedItemsRepositoryImpl.kt @@ -45,12 +45,11 @@ import java.util.HashMap import java.util.Locale import javax.inject.Inject -class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, private val dateUtils: DateUtils) : +class SharedItemsRepositoryImpl +@Inject +constructor(private val ncApi: NcApi, private val dateUtils: DateUtils) : SharedItemsRepository { - override fun media( - parameters: SharedItemsRepository.Parameters, - type: SharedItemType - ): Observable? { + override fun media(parameters: SharedItemsRepository.Parameters, type: SharedItemType): Observable? { return media(parameters, type, null) } @@ -86,9 +85,10 @@ class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, pr if (mediaItems != null) { for (it in mediaItems) { val actorParameters = it.value.messageParameters!!["actor"]!! - val dateTime = dateUtils.getLocalDateTimeStringFromTimestamp( - it.value.timestamp * DateConstants.SECOND_DIVIDER - ) + val dateTime = + dateUtils.getLocalDateTimeStringFromTimestamp( + it.value.timestamp * DateConstants.SECOND_DIVIDER + ) if (it.value.messageParameters?.containsKey("file") == true) { val fileParameters = it.value.messageParameters!!["file"]!! @@ -96,19 +96,20 @@ class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, pr val previewAvailable = "yes".equals(fileParameters["preview-available"]!!, ignoreCase = true) - items[it.value.id] = SharedFileItem( - fileParameters["id"]!!, - fileParameters["name"]!!, - actorParameters["id"]!!, - actorParameters["name"]!!, - dateTime, - fileParameters["size"]!!.toLong(), - fileParameters["path"]!!, - fileParameters["link"]!!, - fileParameters["mimetype"]!!, - previewAvailable, - previewLink(fileParameters["id"], parameters.baseUrl) - ) + items[it.value.id] = + SharedFileItem( + fileParameters["id"]!!, + fileParameters["name"]!!, + actorParameters["id"]!!, + actorParameters["name"]!!, + dateTime, + fileParameters["size"]!!.toLong(), + fileParameters["path"]!!, + fileParameters["link"]!!, + fileParameters["mimetype"]!!, + previewAvailable, + previewLink(fileParameters["id"], parameters.baseUrl) + ) } else if (it.value.messageParameters?.containsKey("object") == true) { val objectParameters = it.value.messageParameters!!["object"]!! items[it.value.id] = itemFromObject(objectParameters, actorParameters, dateTime) @@ -137,45 +138,49 @@ class SharedItemsRepositoryImpl @Inject constructor(private val ncApi: NcApi, pr val returnValue: SharedItem when (objectParameters["type"]) { "talk-poll" -> { - returnValue = SharedPollItem( - objectParameters["id"]!!, - objectParameters["name"]!!, - actorParameters["id"]!!, - actorParameters["name"]!!, - dateTime - ) + returnValue = + SharedPollItem( + objectParameters["id"]!!, + objectParameters["name"]!!, + actorParameters["id"]!!, + actorParameters["name"]!!, + dateTime + ) } "geo-location" -> { - returnValue = SharedLocationItem( - objectParameters["id"]!!, - objectParameters["name"]!!, - actorParameters["id"]!!, - actorParameters["name"]!!, - dateTime, - Uri.parse(objectParameters["id"]!!.replace("geo:", "geo:0,0?z=11&q=")) - ) + returnValue = + SharedLocationItem( + objectParameters["id"]!!, + objectParameters["name"]!!, + actorParameters["id"]!!, + actorParameters["name"]!!, + dateTime, + Uri.parse(objectParameters["id"]!!.replace("geo:", "geo:0,0?z=11&q=")) + ) } "deck-card" -> { - returnValue = SharedDeckCardItem( - objectParameters["id"]!!, - objectParameters["name"]!!, - actorParameters["id"]!!, - actorParameters["name"]!!, - dateTime, - Uri.parse(objectParameters["link"]!!) - ) + returnValue = + SharedDeckCardItem( + objectParameters["id"]!!, + objectParameters["name"]!!, + actorParameters["id"]!!, + actorParameters["name"]!!, + dateTime, + Uri.parse(objectParameters["link"]!!) + ) } else -> { - returnValue = SharedOtherItem( - objectParameters["id"]!!, - objectParameters["name"]!!, - actorParameters["id"]!!, - actorParameters["name"]!!, - dateTime - ) + returnValue = + SharedOtherItem( + objectParameters["id"]!!, + objectParameters["name"]!!, + actorParameters["id"]!!, + actorParameters["name"]!!, + dateTime + ) } } return returnValue diff --git a/app/src/main/java/com/nextcloud/talk/shareditems/viewmodels/SharedItemsViewModel.kt b/app/src/main/java/com/nextcloud/talk/shareditems/viewmodels/SharedItemsViewModel.kt index 6ce35e89e3c..8800b301fd6 100644 --- a/app/src/main/java/com/nextcloud/talk/shareditems/viewmodels/SharedItemsViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/shareditems/viewmodels/SharedItemsViewModel.kt @@ -36,16 +36,22 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class SharedItemsViewModel @Inject constructor( +class SharedItemsViewModel +@Inject +constructor( private val repository: SharedItemsRepository ) : ViewModel() { private lateinit var repositoryParameters: SharedItemsRepository.Parameters sealed interface ViewState + object InitialState : ViewState + object NoSharedItemsState : ViewState + open class TypesLoadedState(val types: Set, val selectedType: SharedItemType) : ViewState + class LoadingItemsState(types: Set, selectedType: SharedItemType) : TypesLoadedState(types, selectedType) @@ -57,50 +63,54 @@ class SharedItemsViewModel @Inject constructor( get() = _viewState fun initialize(user: User, roomToken: String) { - repositoryParameters = SharedItemsRepository.Parameters( - user.userId!!, - user.token!!, - user.baseUrl!!, - roomToken - ) + repositoryParameters = + SharedItemsRepository + .Parameters( + user.userId!!, + user.token!!, + user.baseUrl!!, + roomToken + ) loadAvailableTypes() } private fun loadAvailableTypes() { repository.availableTypes(repositoryParameters).subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer> { + ?.subscribe( + object : Observer> { + var types: Set? = null - var types: Set? = null + override fun onSubscribe(d: Disposable) = Unit - override fun onSubscribe(d: Disposable) = Unit - - override fun onNext(types: Set) { - this.types = types - } + override fun onNext(types: Set) { + this.types = types + } - override fun onError(e: Throwable) { - Log.d(TAG, "An error occurred: $e") - } + override fun onError(e: Throwable) { + Log.d(TAG, "An error occurred: $e") + } - override fun onComplete() { - val newTypes = this.types - if (newTypes.isNullOrEmpty()) { - this@SharedItemsViewModel._viewState.value = NoSharedItemsState - } else { - val selectedType = chooseInitialType(newTypes) - this@SharedItemsViewModel._viewState.value = - TypesLoadedState(newTypes, selectedType) - initialLoadItems(selectedType) + override fun onComplete() { + val newTypes = this.types + if (newTypes.isNullOrEmpty()) { + this@SharedItemsViewModel._viewState.value = NoSharedItemsState + } else { + val selectedType = chooseInitialType(newTypes) + this@SharedItemsViewModel._viewState.value = + TypesLoadedState(newTypes, selectedType) + initialLoadItems(selectedType) + } } } - }) + ) } - private fun chooseInitialType(newTypes: Set): SharedItemType = when { - newTypes.contains(SharedItemType.MEDIA) -> SharedItemType.MEDIA - else -> newTypes.toList().first() - } + private fun chooseInitialType(newTypes: Set): SharedItemType = + when { + newTypes.contains(SharedItemType.MEDIA) -> SharedItemType.MEDIA + else -> newTypes.toList().first() + } fun initialLoadItems(type: SharedItemType) { val state = _viewState.value @@ -123,6 +133,7 @@ class SharedItemsViewModel @Inject constructor( ?.subscribe(SharedMediaItemsObserver()) } } + else -> return } } @@ -161,12 +172,14 @@ class SharedItemsViewModel @Inject constructor( private fun setCurrentState(items: SharedItems) { when (val state = this@SharedItemsViewModel._viewState.value) { is TypesLoadedState -> { - this@SharedItemsViewModel._viewState.value = LoadedState( - state.types, - state.selectedType, - items - ) + this@SharedItemsViewModel._viewState.value = + LoadedState( + state.types, + state.selectedType, + items + ) } + else -> return } } diff --git a/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepository.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepository.kt index e067fc06274..b8f6640a716 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepository.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepository.kt @@ -12,8 +12,5 @@ interface TranslateRepository { fromLanguage: String? ): Observable - fun getLanguages( - authorization: String, - url: String - ): Observable> + fun getLanguages(authorization: String, url: String): Observable> } diff --git a/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepositoryImpl.kt b/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepositoryImpl.kt index 9102b577259..ab28a7c74ab 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepositoryImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/repositories/TranslateRepositoryImpl.kt @@ -5,7 +5,9 @@ import com.nextcloud.talk.translate.repositories.model.Language import io.reactivex.Observable import javax.inject.Inject -class TranslateRepositoryImpl @Inject constructor(private val ncApi: NcApi) : TranslateRepository { +class TranslateRepositoryImpl +@Inject +constructor(private val ncApi: NcApi) : TranslateRepository { override fun translateMessage( authorization: String, url: String, diff --git a/app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt b/app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt index c0bf087d012..98cadf94d00 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/ui/TranslateActivity.kt @@ -116,6 +116,7 @@ class TranslateActivity : BaseActivity() { super.onResume() languages?.let { setItems() } } + override fun onSaveInstanceState(outState: Bundle) { outState.run { putString(BundleKeys.SAVED_TRANSLATED_MESSAGE, binding.translatedMessageTextview.text.toString()) @@ -128,10 +129,12 @@ class TranslateActivity : BaseActivity() { binding.copyTranslatedMessage.setOnClickListener { val clipboardManager = getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager - val clipData = ClipData.newPlainText( - resources?.getString(R.string.nc_app_product_name), - binding.translatedMessageTextview.text?.toString() - ) + val clipData = + ClipData + .newPlainText( + resources?.getString(R.string.nc_app_product_name), + binding.translatedMessageTextview.text?.toString() + ) clipboardManager.setPrimaryClip(clipData) } } @@ -189,18 +192,19 @@ class TranslateActivity : BaseActivity() { } private fun showDialog(titleInt: Int, messageInt: Int) { - val dialogBuilder = MaterialAlertDialogBuilder(this@TranslateActivity) - .setIcon( - viewThemeUtils.dialog.colorMaterialAlertDialogIcon( - context, - R.drawable.ic_warning_white + val dialogBuilder = + MaterialAlertDialogBuilder(this@TranslateActivity) + .setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_warning_white + ) ) - ) - .setTitle(titleInt) - .setMessage(messageInt) - .setPositiveButton(R.string.nc_ok) { dialog, _ -> - dialog.dismiss() - } + .setTitle(titleInt) + .setMessage(messageInt) + .setPositiveButton(R.string.nc_ok) { dialog, _ -> + dialog.dismiss() + } viewThemeUtils.dialog.colorMaterialAlertDialogBackground(context, dialogBuilder) @@ -236,17 +240,21 @@ class TranslateActivity : BaseActivity() { fillSpinners() val text = intent.extras!!.getString(BundleKeys.KEY_TRANSLATE_MESSAGE) - binding.fromLanguage.onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ -> - val fromLabel: String = getISOFromLanguage(parent.getItemAtPosition(position).toString()) - val toLabel: String = getISOFromLanguage(binding.toLanguage.text.toString()) - viewModel.translateMessage(toLabel, fromLabel, text!!) - } + binding.fromLanguage.onItemClickListener = + AdapterView + .OnItemClickListener { parent, _, position, _ -> + val fromLabel: String = getISOFromLanguage(parent.getItemAtPosition(position).toString()) + val toLabel: String = getISOFromLanguage(binding.toLanguage.text.toString()) + viewModel.translateMessage(toLabel, fromLabel, text!!) + } - binding.toLanguage.onItemClickListener = AdapterView.OnItemClickListener { parent, _, position, _ -> - val toLabel: String = getISOFromLanguage(parent.getItemAtPosition(position).toString()) - val fromLabel: String = getISOFromLanguage(binding.fromLanguage.text.toString()) - viewModel.translateMessage(toLabel, fromLabel, text!!) - } + binding.toLanguage.onItemClickListener = + AdapterView + .OnItemClickListener { parent, _, position, _ -> + val toLabel: String = getISOFromLanguage(parent.getItemAtPosition(position).toString()) + val fromLabel: String = getISOFromLanguage(binding.fromLanguage.text.toString()) + viewModel.translateMessage(toLabel, fromLabel, text!!) + } } private fun fillSpinners() { diff --git a/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt b/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt index 72839a3ae26..0eb1721b23c 100644 --- a/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/translate/viewmodels/TranslateViewModel.kt @@ -15,13 +15,16 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class TranslateViewModel @Inject constructor( +class TranslateViewModel +@Inject +constructor( private val repository: TranslateRepository, private val userManager: UserManager ) : ViewModel() { sealed interface ViewState data object StartState : ViewState + class TranslatedState(val msg: String) : ViewState class LanguagesRetrievedState(val list: List) : ViewState @@ -38,7 +41,12 @@ class TranslateViewModel @Inject constructor( val currentUser: User = userManager.currentUser.blockingGet() val authorization: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) val url: String = ApiUtils.getUrlForTranslation(currentUser.baseUrl) - val calculatedFromLanguage = if (fromLanguage == null || fromLanguage == "") { null } else { fromLanguage } + val calculatedFromLanguage = + if (fromLanguage == null || fromLanguage == "") { + null + } else { + fromLanguage + } Log.i(TAG, "translateMessage Called") repository.translateMessage( authorization, @@ -60,25 +68,27 @@ class TranslateViewModel @Inject constructor( repository.getLanguages(authorization, url) .subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer> { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onError(e: Throwable) { - _viewState.value = LanguagesErrorState - Log.e(TAG, "Error while retrieving languages: $e") - } - - override fun onComplete() { - // unused atm - } - - override fun onNext(list: List) { - _viewState.value = LanguagesRetrievedState(list) - Log.d(TAG, "Languages retrieved: $list") + ?.subscribe( + object : Observer> { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onError(e: Throwable) { + _viewState.value = LanguagesErrorState + Log.e(TAG, "Error while retrieving languages: $e") + } + + override fun onComplete() { + // unused atm + } + + override fun onNext(list: List) { + _viewState.value = LanguagesRetrievedState(list) + Log.d(TAG, "Languages retrieved: $list") + } } - }) + ) } inner class TranslateObserver : Observer { @@ -99,6 +109,7 @@ class TranslateViewModel @Inject constructor( // nothing? } } + companion object { private val TAG = TranslateViewModel::class.simpleName } diff --git a/app/src/main/java/com/nextcloud/talk/ui/MicInputCloud.kt b/app/src/main/java/com/nextcloud/talk/ui/MicInputCloud.kt index cc1ce661c30..d7a2eb10d84 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/MicInputCloud.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/MicInputCloud.kt @@ -97,11 +97,13 @@ class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs private var centerX: Float = 0f private var centerY: Float = 0f - private val bottomCirclePaint = Paint(ANTI_ALIAS_FLAG).apply { - color = primaryColor - style = Paint.Style.FILL - alpha = DEFAULT_OPACITY - } + private val bottomCirclePaint = + Paint(ANTI_ALIAS_FLAG) + .apply { + color = primaryColor + style = Paint.Style.FILL + alpha = DEFAULT_OPACITY + } private val topCircleBounds = Rect(0, 0, 0, 0) private val iconBounds = topCircleBounds @@ -117,45 +119,51 @@ class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs } private fun createAnimators() { - ovalOneAnimator = ValueAnimator.ofInt( - o1h, - OVAL_ONE_DEFAULT_HEIGHT + ANIMATION_CAP, - o1h - ).apply { - duration = OVAL_ONE_ANIMATION_LENGTH - interpolator = LinearInterpolator() - repeatCount = ValueAnimator.INFINITE - addUpdateListener { valueAnimator -> - o1h = valueAnimator.animatedValue as Int - } - } + ovalOneAnimator = + ValueAnimator + .ofInt( + o1h, + OVAL_ONE_DEFAULT_HEIGHT + ANIMATION_CAP, + o1h + ).apply { + duration = OVAL_ONE_ANIMATION_LENGTH + interpolator = LinearInterpolator() + repeatCount = ValueAnimator.INFINITE + addUpdateListener { valueAnimator -> + o1h = valueAnimator.animatedValue as Int + } + } - ovalTwoAnimator = ValueAnimator.ofInt( - o2h, - OVAL_TWO_DEFAULT_HEIGHT + ANIMATION_CAP, - o2h - ).apply { - duration = OVAL_TWO_ANIMATION_LENGTH - interpolator = LinearInterpolator() - repeatCount = ValueAnimator.INFINITE - addUpdateListener { valueAnimator -> - o2h = valueAnimator.animatedValue as Int - } - } + ovalTwoAnimator = + ValueAnimator + .ofInt( + o2h, + OVAL_TWO_DEFAULT_HEIGHT + ANIMATION_CAP, + o2h + ).apply { + duration = OVAL_TWO_ANIMATION_LENGTH + interpolator = LinearInterpolator() + repeatCount = ValueAnimator.INFINITE + addUpdateListener { valueAnimator -> + o2h = valueAnimator.animatedValue as Int + } + } - ovalThreeAnimator = ValueAnimator.ofInt( - o3h, - OVAL_THREE_DEFAULT_HEIGHT + ANIMATION_CAP, - o3h - ).apply { - duration = OVAL_THREE_ANIMATION_LENGTH - interpolator = LinearInterpolator() - repeatCount = ValueAnimator.INFINITE - addUpdateListener { valueAnimator -> - o3h = valueAnimator.animatedValue as Int - invalidate() // needed to animate the other listeners as well - } - } + ovalThreeAnimator = + ValueAnimator + .ofInt( + o3h, + OVAL_THREE_DEFAULT_HEIGHT + ANIMATION_CAP, + o3h + ).apply { + duration = OVAL_THREE_ANIMATION_LENGTH + interpolator = LinearInterpolator() + repeatCount = ValueAnimator.INFINITE + addUpdateListener { valueAnimator -> + o3h = valueAnimator.animatedValue as Int + invalidate() // needed to animate the other listeners as well + } + } } private fun destroyAnimators() { @@ -259,33 +267,35 @@ class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs val heightMode = MeasureSpec.getMode(heightMeasureSpec) val heightSize = MeasureSpec.getSize(heightMeasureSpec) - val width: Int = when (widthMode) { - MeasureSpec.EXACTLY -> { - widthSize - } + val width: Int = + when (widthMode) { + MeasureSpec.EXACTLY -> { + widthSize + } - MeasureSpec.AT_MOST -> { - desiredWidth.coerceAtMost(widthSize) - } + MeasureSpec.AT_MOST -> { + desiredWidth.coerceAtMost(widthSize) + } - else -> { - desiredWidth + else -> { + desiredWidth + } } - } - val height: Int = when (heightMode) { - MeasureSpec.EXACTLY -> { - heightSize - } + val height: Int = + when (heightMode) { + MeasureSpec.EXACTLY -> { + heightSize + } - MeasureSpec.AT_MOST -> { - desiredHeight.coerceAtMost(heightSize) - } + MeasureSpec.AT_MOST -> { + desiredHeight.coerceAtMost(heightSize) + } - else -> { - desiredHeight + else -> { + desiredHeight + } } - } centerX = (width / 2).toFloat() centerY = (height / 2).toFloat() @@ -311,17 +321,18 @@ class MicInputCloud(context: Context, attrs: AttributeSet) : View(context, attrs } override fun performClick(): Boolean { - state = if (state == ViewState.PAUSED_STATE) { - ovalOneAnimator?.resume() - ovalTwoAnimator?.resume() - ovalThreeAnimator?.resume() - ViewState.PLAY_STATE - } else { - ovalOneAnimator?.pause() - ovalTwoAnimator?.pause() - ovalThreeAnimator?.pause() - ViewState.PAUSED_STATE - } + state = + if (state == ViewState.PAUSED_STATE) { + ovalOneAnimator?.resume() + ovalTwoAnimator?.resume() + ovalThreeAnimator?.resume() + ViewState.PLAY_STATE + } else { + ovalOneAnimator?.pause() + ovalTwoAnimator?.pause() + ovalThreeAnimator?.pause() + ViewState.PAUSED_STATE + } invalidate() return super.performClick() } diff --git a/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt b/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt index cfc9839e20f..d1febab3dee 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/bottom/sheet/ProfileBottomSheet.kt @@ -57,28 +57,30 @@ class ProfileBottomSheet(val ncApi: NcApi, val userModel: User) { ApiUtils.getCredentials(userModel.username, userModel.token), ApiUtils.getUrlForHoverCard(userModel.baseUrl, user) ).subscribeOn(Schedulers.io()).observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(hoverCardOverall: HoverCardOverall) { + bottomSheet( + hoverCardOverall.ocs!!.data!!.actions!!, + hoverCardOverall.ocs!!.data!!.displayName!!, + user, + context + ) + } + + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to get hover card for user $user", e) + } + + override fun onComplete() { + // unused atm + } } - - override fun onNext(hoverCardOverall: HoverCardOverall) { - bottomSheet( - hoverCardOverall.ocs!!.data!!.actions!!, - hoverCardOverall.ocs!!.data!!.displayName!!, - user, - context - ) - } - - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to get hover card for user $user", e) - } - - override fun onComplete() { - // unused atm - } - }) + ) } @SuppressLint("CheckResult") @@ -104,11 +106,12 @@ class ProfileBottomSheet(val ncApi: NcApi, val userModel: User) { } private fun configureActionListItem(action: HoverCardAction): BasicListItemWithImage { - val drawable = when (AllowedAppIds.createFor(action)) { - PROFILE -> R.drawable.ic_user - EMAIL -> R.drawable.ic_email - SPREED -> R.drawable.ic_talk - } + val drawable = + when (AllowedAppIds.createFor(action)) { + PROFILE -> R.drawable.ic_user + EMAIL -> R.drawable.ic_email + SPREED -> R.drawable.ic_talk + } return BasicListItemWithImage( drawable, @@ -119,14 +122,16 @@ class ProfileBottomSheet(val ncApi: NcApi, val userModel: User) { private fun talkTo(userId: String, context: Context) { val apiVersion = ApiUtils.getConversationApiVersion(userModel, intArrayOf(ApiUtils.APIv4, 1)) - val retrofitBucket = ApiUtils.getRetrofitBucketForCreateRoom( - apiVersion, - userModel.baseUrl, - "1", - null, - userId, - null - ) + val retrofitBucket = + ApiUtils + .getRetrofitBucketForCreateRoom( + apiVersion, + userModel.baseUrl, + "1", + null, + userId, + null + ) val credentials = ApiUtils.getCredentials(userModel.username, userModel.token) ncApi.createRoom( credentials, @@ -135,38 +140,42 @@ class ProfileBottomSheet(val ncApi: NcApi, val userModel: User) { ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } + + override fun onNext(roomOverall: RoomOverall) { + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) + bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId) + + val chatIntent = Intent(context, ChatActivity::class.java) + chatIntent.putExtras(bundle) + chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) + context.startActivity(chatIntent) + } + + override fun onError(e: Throwable) { + Log.e(TAG, e.message, e) + } + + override fun onComplete() { + // unused atm + } } - - override fun onNext(roomOverall: RoomOverall) { - val bundle = Bundle() - bundle.putString(BundleKeys.KEY_ROOM_TOKEN, roomOverall.ocs!!.data!!.token) - bundle.putString(BundleKeys.KEY_ROOM_ID, roomOverall.ocs!!.data!!.roomId) - - val chatIntent = Intent(context, ChatActivity::class.java) - chatIntent.putExtras(bundle) - chatIntent.addFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP) - context.startActivity(chatIntent) - } - - override fun onError(e: Throwable) { - Log.e(TAG, e.message, e) - } - - override fun onComplete() { - // unused atm - } - }) + ) } private fun composeEmail(address: String, context: Context) { val addresses = arrayListOf(address) - val intent = Intent(Intent.ACTION_SENDTO).apply { - data = Uri.parse("mailto:") // only email apps should handle this - putExtra(Intent.EXTRA_EMAIL, addresses) - } + val intent = + Intent(Intent.ACTION_SENDTO) + .apply { + data = Uri.parse("mailto:") // only email apps should handle this + putExtra(Intent.EXTRA_EMAIL, addresses) + } context.startActivity(intent) } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt index c050a9b15bb..43745d14cc6 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/AttachmentDialog.kt @@ -61,12 +61,14 @@ class AttachmentDialog(val activity: Activity, var chatActivity: ChatActivity) : private fun initItemsStrings() { var serverName = CapabilitiesUtilNew.getServerName(chatActivity.conversationUser) - dialogAttachmentBinding.txtAttachFileFromCloud.text = chatActivity.resources?.let { - if (serverName.isNullOrEmpty()) { - serverName = it.getString(R.string.nc_server_product_name) - } - String.format(it.getString(R.string.nc_upload_from_cloud), serverName) - } + dialogAttachmentBinding.txtAttachFileFromCloud.text = + chatActivity + .resources?.let { + if (serverName.isNullOrEmpty()) { + serverName = it.getString(R.string.nc_server_product_name) + } + String.format(it.getString(R.string.nc_upload_from_cloud), serverName) + } } private fun initItemsVisibility() { diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountShareToDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountShareToDialogFragment.kt index 3fe01b13106..f9350e96119 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountShareToDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ChooseAccountShareToDialogFragment.kt @@ -112,11 +112,12 @@ class ChooseAccountShareToDialogFragment : DialogFragment() { userEntity = userItem if (!userEntity.current) { var userId: String? - userId = if (userEntity.userId != null) { - userEntity.userId - } else { - userEntity.username - } + userId = + if (userEntity.userId != null) { + userEntity.userId + } else { + userEntity.username + } participant = Participant() participant.actorType = Participant.ActorType.USERS participant.actorId = userId @@ -155,20 +156,23 @@ class ChooseAccountShareToDialogFragment : DialogFragment() { binding = null } - private val onSwitchItemClickListener = FlexibleAdapter.OnItemClickListener { view, position -> - if (userItems.size > position) { - val user = userItems[position].user - if (userManager!!.setUserAsActive(user).blockingGet()) { - cookieManager!!.cookieStore.removeAll() - activity?.recreate() - dismiss() + private val onSwitchItemClickListener = + FlexibleAdapter + .OnItemClickListener { view, position -> + if (userItems.size > position) { + val user = userItems[position].user + if (userManager!!.setUserAsActive(user).blockingGet()) { + cookieManager!!.cookieStore.removeAll() + activity?.recreate() + dismiss() + } + } + true } - } - true - } companion object { val TAG = ChooseAccountShareToDialogFragment::class.java.simpleName + fun newInstance(): ChooseAccountShareToDialogFragment { return ChooseAccountShareToDialogFragment() } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ConversationsListBottomDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/ConversationsListBottomDialog.kt index 9463b34d488..21b1a265e6c 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ConversationsListBottomDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ConversationsListBottomDialog.kt @@ -107,35 +107,42 @@ class ConversationsListBottomDialog( val hasFavoritesCapability = CapabilitiesUtilNew.hasSpreedFeatureCapability(currentUser, "favorites") val canModerate = conversation.canModerate(currentUser) - binding.conversationRemoveFromFavorites.visibility = setVisibleIf( - hasFavoritesCapability && conversation.favorite - ) - binding.conversationAddToFavorites.visibility = setVisibleIf( - hasFavoritesCapability && !conversation.favorite - ) + binding.conversationRemoveFromFavorites.visibility = + setVisibleIf( + hasFavoritesCapability && conversation.favorite + ) + binding.conversationAddToFavorites.visibility = + setVisibleIf( + hasFavoritesCapability && !conversation.favorite + ) - binding.conversationMarkAsRead.visibility = setVisibleIf( - conversation.unreadMessages > 0 && CapabilitiesUtilNew.canSetChatReadMarker(currentUser) - ) + binding.conversationMarkAsRead.visibility = + setVisibleIf( + conversation.unreadMessages > 0 && CapabilitiesUtilNew.canSetChatReadMarker(currentUser) + ) - binding.conversationMarkAsUnread.visibility = setVisibleIf( - conversation.unreadMessages <= 0 && CapabilitiesUtilNew.canMarkRoomAsUnread(currentUser) - ) + binding.conversationMarkAsUnread.visibility = + setVisibleIf( + conversation.unreadMessages <= 0 && CapabilitiesUtilNew.canMarkRoomAsUnread(currentUser) + ) - binding.conversationOperationRename.visibility = setVisibleIf( - conversation.isNameEditable(currentUser) - ) + binding.conversationOperationRename.visibility = + setVisibleIf( + conversation.isNameEditable(currentUser) + ) - binding.conversationOperationDelete.visibility = setVisibleIf( - canModerate - ) + binding.conversationOperationDelete.visibility = + setVisibleIf( + canModerate + ) - binding.conversationOperationLeave.visibility = setVisibleIf( - conversation.canLeave() && - // leaving is by api not possible for the last user with moderator permissions. - // for now, hide this option for all moderators. - !conversation.canModerate(currentUser) - ) + binding.conversationOperationLeave.visibility = + setVisibleIf( + conversation.canLeave() && + // leaving is by api not possible for the last user with moderator permissions. + // for now, hide this option for all moderators. + !conversation.canModerate(currentUser) + ) } private fun setVisibleIf(boolean: Boolean): Int { @@ -189,31 +196,33 @@ class ConversationsListBottomDialog( .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(1) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - activity.fetchRooms() - activity.showSnackbar( - String.format( - context.resources.getString(R.string.added_to_favorites), - conversation.displayName + override fun onNext(genericOverall: GenericOverall) { + activity.fetchRooms() + activity.showSnackbar( + String.format( + context.resources.getString(R.string.added_to_favorites), + conversation.displayName + ) ) - ) - dismiss() - } + dismiss() + } - override fun onError(e: Throwable) { - activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry)) - dismiss() - } + override fun onError(e: Throwable) { + activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry)) + dismiss() + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun removeConversationFromFavorites() { @@ -229,31 +238,33 @@ class ConversationsListBottomDialog( .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(1) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - activity.fetchRooms() - activity.showSnackbar( - String.format( - context.resources.getString(R.string.removed_from_favorites), - conversation.displayName + override fun onNext(genericOverall: GenericOverall) { + activity.fetchRooms() + activity.showSnackbar( + String.format( + context.resources.getString(R.string.removed_from_favorites), + conversation.displayName + ) ) - ) - dismiss() - } + dismiss() + } - override fun onError(e: Throwable) { - activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry)) - dismiss() - } + override fun onError(e: Throwable) { + activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry)) + dismiss() + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun markConversationAsUnread() { @@ -268,31 +279,33 @@ class ConversationsListBottomDialog( .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(1) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - activity.fetchRooms() - activity.showSnackbar( - String.format( - context.resources.getString(R.string.marked_as_unread), - conversation.displayName + override fun onNext(genericOverall: GenericOverall) { + activity.fetchRooms() + activity.showSnackbar( + String.format( + context.resources.getString(R.string.marked_as_unread), + conversation.displayName + ) ) - ) - dismiss() - } + dismiss() + } - override fun onError(e: Throwable) { - activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry)) - dismiss() - } + override fun onError(e: Throwable) { + activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry)) + dismiss() + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun markConversationAsRead() { @@ -308,46 +321,51 @@ class ConversationsListBottomDialog( .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) .retry(1) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - activity.fetchRooms() - activity.showSnackbar( - String.format( - context.resources.getString(R.string.marked_as_read), - conversation.displayName + override fun onNext(genericOverall: GenericOverall) { + activity.fetchRooms() + activity.showSnackbar( + String.format( + context.resources.getString(R.string.marked_as_read), + conversation.displayName + ) ) - ) - dismiss() - } + dismiss() + } - override fun onError(e: Throwable) { - activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry)) - dismiss() - } + override fun onError(e: Throwable) { + activity.showSnackbar(context.resources.getString(R.string.nc_common_error_sorry)) + dismiss() + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun renameConversation() { if (!TextUtils.isEmpty(conversation.token)) { dismiss() - val conversationDialog = RenameConversationDialogFragment.newInstance( - conversation.token!!, - conversation.displayName!! - ) + val conversationDialog = + RenameConversationDialogFragment + .newInstance( + conversation.token!!, + conversation.displayName!! + ) conversationDialog.show( activity.supportFragmentManager, TAG ) } } + private fun leaveConversation() { val dataBuilder = Data.Builder() dataBuilder.putString(KEY_ROOM_TOKEN, conversation.token) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/DateTimePickerFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/DateTimePickerFragment.kt index 886ef57f99f..9c9832ecbbe 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/DateTimePickerFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/DateTimePickerFragment.kt @@ -107,13 +107,13 @@ class DateTimePickerFragment( binding.dateTimePickerLaterTodayTextview.text = getTimeFromTimeStamp(laterTodayTimeStamp) if (Calendar.getInstance().get(Calendar.DAY_OF_WEEK) == Calendar.SATURDAY) { - tomorrowTimeStamp = getTimeFromCalendar( - hour = HOUR_EIGHT_AM, - minute = 0, - daysToAdd = 1, - weekInYear = - currentWeekInYear + 1 - ) + tomorrowTimeStamp = + getTimeFromCalendar( + hour = HOUR_EIGHT_AM, + minute = 0, + daysToAdd = 1, + weekInYear = currentWeekInYear + 1 + ) binding.dateTimePickerWeekend.visibility = View.GONE // because today is the weekend } else { @@ -123,13 +123,13 @@ class DateTimePickerFragment( binding.dateTimePickerTomorrowTextview.text = getTimeFromTimeStamp(tomorrowTimeStamp) binding.dateTimePickerWeekendTextview.text = getTimeFromTimeStamp(weekendTimeStamp) - nextWeekTimeStamp = getTimeFromCalendar( - hour = HOUR_EIGHT_AM, - day = Calendar.MONDAY, - minute = 0, - weekInYear = - currentWeekInYear + 1 - ) // this should only pick mondays from next week only + nextWeekTimeStamp = + getTimeFromCalendar( + hour = HOUR_EIGHT_AM, + day = Calendar.MONDAY, + minute = 0, + weekInYear = currentWeekInYear + 1 + ) // this should only pick mondays from next week only binding.dateTimePickerNextWeekTextview.text = getTimeFromTimeStamp(nextWeekTimeStamp) // This is to hide the later today option, if it's past 6pm @@ -194,14 +194,19 @@ class DateTimePickerFragment( setTimeStamp(getTimeFromTimeStamp(nextWeekTimeStamp)) } binding.dateTimePickerCustom.setOnClickListener { - val constraintsBuilder = CalendarConstraints.Builder() - .setValidator(DateValidatorPointForward.now()) - .build() + val constraintsBuilder = + CalendarConstraints + .Builder() + .setValidator(DateValidatorPointForward.now()) + .build() val time = System.currentTimeMillis() - val datePicker = MaterialDatePicker.Builder.datePicker() - .setTitleText(R.string.nc_remind) - .setSelection(time + TimeZone.getDefault().getOffset(time)) - .setCalendarConstraints(constraintsBuilder).build() + val datePicker = + MaterialDatePicker + .Builder + .datePicker() + .setTitleText(R.string.nc_remind) + .setSelection(time + TimeZone.getDefault().getOffset(time)) + .setCalendarConstraints(constraintsBuilder).build() datePicker.addOnPositiveButtonClickListener { selection -> val localTimeInMillis = selection - TimeZone.getDefault().getOffset(selection) @@ -232,20 +237,23 @@ class DateTimePickerFragment( private fun setUpTimePicker(year: Int, month: Int, day: Int, weekInYear: Int) { val locale = if (DateFormat.is24HourFormat(requireContext())) TimeFormat.CLOCK_24H else TimeFormat.CLOCK_12H - val timePicker = MaterialTimePicker.Builder() - .setTitleText(R.string.nc_remind) - .setTimeFormat(locale) - .build() + val timePicker = + MaterialTimePicker + .Builder() + .setTitleText(R.string.nc_remind) + .setTimeFormat(locale) + .build() timePicker.addOnPositiveButtonClickListener { - val timestamp = getTimeFromCalendar( - year, - month, - day, - timePicker.hour, - timePicker.minute, - weekInYear = weekInYear - ) + val timestamp = + getTimeFromCalendar( + year, + month, + day, + timePicker.hour, + timePicker.minute, + weekInYear = weekInYear + ) setTimeStamp(getTimeFromTimeStamp(timestamp)) currentTimeStamp = timestamp / ONE_SEC } @@ -263,16 +271,19 @@ class DateTimePickerFragment( daysToAdd: Int = 0, weekInYear: Int = Calendar.getInstance().get(Calendar.WEEK_OF_YEAR) ): Long { - val calendar: Calendar = Calendar.getInstance().apply { - set(Calendar.YEAR, year) - set(Calendar.MONTH, month) - set(Calendar.DAY_OF_WEEK, day) - add(Calendar.DAY_OF_WEEK, daysToAdd) - set(Calendar.WEEK_OF_YEAR, weekInYear) - set(Calendar.HOUR_OF_DAY, hour) - set(Calendar.MINUTE, minute) - set(Calendar.SECOND, 0) - } + val calendar: Calendar = + Calendar + .getInstance() + .apply { + set(Calendar.YEAR, year) + set(Calendar.MONTH, month) + set(Calendar.DAY_OF_WEEK, day) + add(Calendar.DAY_OF_WEEK, daysToAdd) + set(Calendar.WEEK_OF_YEAR, weekInYear) + set(Calendar.HOUR_OF_DAY, hour) + set(Calendar.MINUTE, minute) + set(Calendar.SECOND, 0) + } return calendar.timeInMillis } @@ -281,15 +292,18 @@ class DateTimePickerFragment( } private fun getTimeFromTimeStamp(time: Long): String { - return DateUtils.formatDateTime( - requireContext(), - time, - DateUtils.FORMAT_SHOW_DATE - ) + ", " + DateUtils.formatDateTime( - requireContext(), - time, - DateUtils.FORMAT_SHOW_TIME - ) + return DateUtils + .formatDateTime( + requireContext(), + time, + DateUtils.FORMAT_SHOW_DATE + ) + + ", " + + DateUtils.formatDateTime( + requireContext(), + time, + DateUtils.FORMAT_SHOW_TIME + ) } companion object { @@ -299,14 +313,11 @@ class DateTimePickerFragment( private const val HOUR_SIX_PM = 18 @JvmStatic - fun newInstance( - token: String, - id: String, - chatViewModel: ChatViewModel - ) = DateTimePickerFragment( - token, - id, - chatViewModel - ) + fun newInstance(token: String, id: String, chatViewModel: ChatViewModel) = + DateTimePickerFragment( + token, + id, + chatViewModel + ) } } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/FileAttachmentPreviewFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/FileAttachmentPreviewFragment.kt index e4f004b95b6..b96854ec3b6 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/FileAttachmentPreviewFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/FileAttachmentPreviewFragment.kt @@ -51,6 +51,7 @@ class FileAttachmentPreviewFragment( @Inject lateinit var viewThemeUtils: ViewThemeUtils + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { binding = DialogFileAttachmentPreviewBinding.inflate(LayoutInflater.from(context)) return MaterialAlertDialogBuilder(requireContext()).setView(binding.root).create() @@ -93,8 +94,8 @@ class FileAttachmentPreviewFragment( filenames: String, filesToUpload: MutableList, functionToCall: (files: MutableList, caption: String) -> Unit - ) = - FileAttachmentPreviewFragment(filenames, filesToUpload, functionToCall) + ) = FileAttachmentPreviewFragment(filenames, filesToUpload, functionToCall) + val TAG: String = FilterConversationFragment::class.java.simpleName } } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/FilterConversationFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/FilterConversationFragment.kt index aa8061e42c1..20d0382cc5e 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/FilterConversationFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/FilterConversationFragment.kt @@ -55,6 +55,7 @@ class FilterConversationFragment( @Inject lateinit var arbitraryStorageManager: ArbitraryStorageManager + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { binding = DialogFilterConversationBinding.inflate(LayoutInflater.from(context)) dialogView = binding.root @@ -62,11 +63,7 @@ class FilterConversationFragment( return MaterialAlertDialogBuilder(requireContext()).setView(dialogView).create() } - override fun onCreateView( - inflater: LayoutInflater, - container: ViewGroup?, - savedInstanceState: Bundle? - ): View? { + override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? { NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) setUpColors() setUpListeners() @@ -133,6 +130,7 @@ class FilterConversationFragment( savedFilterState: MutableMap, conversationsListActivity: ConversationsListActivity ) = FilterConversationFragment(savedFilterState, conversationsListActivity) + val TAG: String = FilterConversationFragment::class.java.simpleName const val MENTION: String = "mention" const val UNREAD: String = "unread" diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt index 884eacd1a1e..36e943f452f 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MessageActionsDialog.kt @@ -143,26 +143,28 @@ class MessageActionsDialog( true } - popup = EmojiPopup( - rootView = dialogMessageActionsBinding.root, - editText = dialogMessageActionsBinding.emojiMore, - onEmojiPopupShownListener = { - dialogMessageActionsBinding.emojiMore.clearFocus() - dialogMessageActionsBinding.messageActions.visibility = View.GONE - }, - onEmojiClickListener = { - popup.dismiss() - clickOnEmoji(message, it.unicode) - }, - onEmojiPopupDismissListener = { - dialogMessageActionsBinding.emojiMore.clearFocus() - dialogMessageActionsBinding.messageActions.visibility = View.VISIBLE - - val imm: InputMethodManager = context.getSystemService(Context.INPUT_METHOD_SERVICE) as - InputMethodManager - imm.hideSoftInputFromWindow(dialogMessageActionsBinding.emojiMore.windowToken, 0) - } - ) + popup = + EmojiPopup( + rootView = dialogMessageActionsBinding.root, + editText = dialogMessageActionsBinding.emojiMore, + onEmojiPopupShownListener = { + dialogMessageActionsBinding.emojiMore.clearFocus() + dialogMessageActionsBinding.messageActions.visibility = View.GONE + }, + onEmojiClickListener = { + popup.dismiss() + clickOnEmoji(message, it.unicode) + }, + onEmojiPopupDismissListener = { + dialogMessageActionsBinding.emojiMore.clearFocus() + dialogMessageActionsBinding.messageActions.visibility = View.VISIBLE + + val imm: InputMethodManager = + context + .getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(dialogMessageActionsBinding.emojiMore.windowToken, 0) + } + ) dialogMessageActionsBinding.emojiMore.installDisableKeyboardInput(popup) dialogMessageActionsBinding.emojiMore.installForceSingleEmoji() } @@ -329,6 +331,7 @@ class MessageActionsDialog( dialogMessageActionsBinding.menuTranslateMessage.visibility = getVisibility(visible) } + private fun initMenuShare(visible: Boolean) { if (visible) { dialogMessageActionsBinding.menuShare.setOnClickListener { diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt index 30769c72312..9ec4b799fee 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/MoreCallActionsDialog.kt @@ -108,11 +108,13 @@ class MoreCallActionsDialog(private val callActivity: CallActivity) : BottomShee val availableReactions: ArrayList<*> = capabilities?.spreedCapability?.config!!["call"]!!["supported-reactions"] as ArrayList<*> - val param = LinearLayout.LayoutParams( - LinearLayout.LayoutParams.MATCH_PARENT, - LinearLayout.LayoutParams.MATCH_PARENT, - 1.0f - ) + val param = + LinearLayout + .LayoutParams( + LinearLayout.LayoutParams.MATCH_PARENT, + LinearLayout.LayoutParams.MATCH_PARENT, + 1.0f + ) availableReactions.forEach { val emojiView = EmojiTextView(context) diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SaveToStorageDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SaveToStorageDialogFragment.kt index 89f7afc0f96..88afab5e4d3 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SaveToStorageDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SaveToStorageDialogFragment.kt @@ -53,6 +53,7 @@ class SaveToStorageDialogFragment : DialogFragment() { sharedApplication!!.componentApplication.inject(this) fileName = arguments?.getString(KEY_FILE_NAME)!! } + override fun onCreateDialog(savedInstanceState: Bundle?): Dialog { val dialogText = StringBuilder() dialogText.append(resources.getString(R.string.nc_dialog_save_to_storage_content)) @@ -60,14 +61,15 @@ class SaveToStorageDialogFragment : DialogFragment() { dialogText.append("\n") dialogText.append(resources.getString(R.string.nc_dialog_save_to_storage_continue)) - val dialogBuilder = MaterialAlertDialogBuilder(requireContext()) - .setTitle(R.string.nc_dialog_save_to_storage_title) - .setMessage(dialogText) - .setPositiveButton(R.string.nc_dialog_save_to_storage_yes) { _: DialogInterface?, _: Int -> - saveImageToStorage(fileName) - } - .setNegativeButton(R.string.nc_dialog_save_to_storage_no) { _: DialogInterface?, _: Int -> - } + val dialogBuilder = + MaterialAlertDialogBuilder(requireContext()) + .setTitle(R.string.nc_dialog_save_to_storage_title) + .setMessage(dialogText) + .setPositiveButton(R.string.nc_dialog_save_to_storage_yes) { _: DialogInterface?, _: Int -> + saveImageToStorage(fileName) + } + .setNegativeButton(R.string.nc_dialog_save_to_storage_no) { _: DialogInterface?, _: Int -> + } viewThemeUtils.dialog.colorMaterialAlertDialogBackground( requireContext(), dialogBuilder @@ -82,9 +84,7 @@ class SaveToStorageDialogFragment : DialogFragment() { } @SuppressLint("LongLogTag") - private fun saveImageToStorage( - fileName: String - ) { + private fun saveImageToStorage(fileName: String) { val sourceFilePath = requireContext().cacheDir.path val workerTag = SAVE_TO_STORAGE_WORKER_PREFIX + fileName @@ -101,15 +101,19 @@ class SaveToStorageDialogFragment : DialogFragment() { Log.e(TAG, "Error when checking if worker already exists", e) } - val data: Data = Data.Builder() - .putString(SaveFileToStorageWorker.KEY_FILE_NAME, fileName) - .putString(SaveFileToStorageWorker.KEY_SOURCE_FILE_PATH, "$sourceFilePath/$fileName") - .build() + val data: Data = + Data + .Builder() + .putString(SaveFileToStorageWorker.KEY_FILE_NAME, fileName) + .putString(SaveFileToStorageWorker.KEY_SOURCE_FILE_PATH, "$sourceFilePath/$fileName") + .build() - val saveWorker: OneTimeWorkRequest = OneTimeWorkRequest.Builder(SaveFileToStorageWorker::class.java) - .setInputData(data) - .addTag(workerTag) - .build() + val saveWorker: OneTimeWorkRequest = + OneTimeWorkRequest + .Builder(SaveFileToStorageWorker::class.java) + .setInputData(data) + .addTag(workerTag) + .build() WorkManager.getInstance().enqueue(saveWorker) } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt index a52b6856947..40e33340f35 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/SetStatusDialogFragment.kt @@ -131,37 +131,42 @@ class SetStatusDialogFragment : ncApi.getPredefinedStatuses(credentials, ApiUtils.getUrlForPredefinedStatuses(currentUser?.baseUrl)) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } - - override fun onNext(responseBody: ResponseBody) { - val predefinedStatusOverall: PredefinedStatusOverall = LoganSquare.parse( - responseBody - .string(), - PredefinedStatusOverall::class.java - ) - predefinedStatusOverall.ocs?.data?.let { it1 -> predefinedStatusesList.addAll(it1) } - - if (currentStatus?.messageIsPredefined == true && - currentStatus?.messageId?.isNotEmpty() == true - ) { - val messageId = currentStatus!!.messageId - selectedPredefinedStatus = predefinedStatusesList.firstOrNull { ps -> messageId == ps.id } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm } - adapter.notifyDataSetChanged() - } + override fun onNext(responseBody: ResponseBody) { + val predefinedStatusOverall: PredefinedStatusOverall = + LoganSquare + .parse( + responseBody + .string(), + PredefinedStatusOverall::class.java + ) + predefinedStatusOverall.ocs?.data?.let { it1 -> predefinedStatusesList.addAll(it1) } + + if (currentStatus?.messageIsPredefined == true && + currentStatus?.messageId?.isNotEmpty() == true + ) { + val messageId = currentStatus!!.messageId + selectedPredefinedStatus = + predefinedStatusesList.firstOrNull { ps -> messageId == ps.id } + } + + adapter.notifyDataSetChanged() + } - override fun onError(e: Throwable) { - Log.e(TAG, "Error while fetching predefined statuses", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "Error while fetching predefined statuses", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } } @@ -193,31 +198,33 @@ class SetStatusDialogFragment : binding.setStatus.setOnClickListener { setStatusMessage() } binding.emoji.setOnClickListener { openEmojiPopup() } - popup = EmojiPopup( - rootView = view, - editText = binding.emoji, - onEmojiClickListener = { - popup.dismiss() - binding.emoji.clearFocus() - val imm: InputMethodManager = context?.getSystemService(Context.INPUT_METHOD_SERVICE) as - InputMethodManager - imm.hideSoftInputFromWindow(binding.emoji.windowToken, 0) - } - ) + popup = + EmojiPopup( + rootView = view, + editText = binding.emoji, + onEmojiClickListener = { + popup.dismiss() + binding.emoji.clearFocus() + val imm: InputMethodManager = + context?.getSystemService(Context.INPUT_METHOD_SERVICE) as InputMethodManager + imm.hideSoftInputFromWindow(binding.emoji.windowToken, 0) + } + ) binding.emoji.installDisableKeyboardInput(popup) binding.emoji.installForceSingleEmoji() binding.clearStatusAfterSpinner.apply { this.adapter = createClearTimesArrayAdapter() - onItemSelectedListener = object : OnItemSelectedListener { - override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { - setClearStatusAfterValue(position) - } + onItemSelectedListener = + object : OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + setClearStatusAfterValue(position) + } - override fun onNothingSelected(parent: AdapterView<*>?) { - // nothing to do + override fun onNothingSelected(parent: AdapterView<*>?) { + // nothing to do + } } - } } viewThemeUtils.platform.themeDialog(binding.root) @@ -245,9 +252,11 @@ class SetStatusDialogFragment : binding.remainingClearTime.apply { binding.clearStatusMessageTextView.text = getString(R.string.clear_status_message) visibility = View.VISIBLE - text = DisplayUtils.getRelativeTimestamp(context, it.clearAt * ONE_SECOND_IN_MILLIS, true) - .toString() - .decapitalize(Locale.getDefault()) + text = + DisplayUtils + .getRelativeTimestamp(context, it.clearAt * ONE_SECOND_IN_MILLIS, true) + .toString() + .decapitalize(Locale.getDefault()) setOnClickListener { visibility = View.GONE binding.clearStatusAfterSpinner.visibility = View.VISIBLE @@ -309,21 +318,27 @@ class SetStatusDialogFragment : POS_TODAY -> { // today - val date = Calendar.getInstance().apply { - set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) - set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) - set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) - } + val date = + Calendar + .getInstance() + .apply { + set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) + set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) + set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) + } clearAt = date.timeInMillis / ONE_SECOND_IN_MILLIS } POS_END_OF_WEEK -> { // end of week - val date = Calendar.getInstance().apply { - set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) - set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) - set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) - } + val date = + Calendar + .getInstance() + .apply { + set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) + set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) + set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) + } while (date.get(Calendar.DAY_OF_WEEK) != Calendar.SUNDAY) { date.add(Calendar.DAY_OF_YEAR, 1) @@ -348,16 +363,17 @@ class SetStatusDialogFragment : return returnValue } - private fun clearAtToUnixTimeTypeEndOf( - clearAt: ClearAt - ): Long { + private fun clearAtToUnixTimeTypeEndOf(clearAt: ClearAt): Long { var returnValue = -1L if (clearAt.time == "day") { - val date = Calendar.getInstance().apply { - set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) - set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) - set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) - } + val date = + Calendar + .getInstance() + .apply { + set(Calendar.HOUR_OF_DAY, LAST_HOUR_OF_DAY) + set(Calendar.MINUTE, LAST_MINUTE_OF_HOUR) + set(Calendar.SECOND, LAST_SECOND_OF_MINUTE) + } returnValue = date.timeInMillis / ONE_SECOND_IN_MILLIS } return returnValue @@ -371,23 +387,25 @@ class SetStatusDialogFragment : val credentials = ApiUtils.getCredentials(currentUser?.username, currentUser?.token) ncApi.statusDeleteMessage(credentials, ApiUtils.getUrlForStatusMessage(currentUser?.baseUrl)) .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .observeOn(AndroidSchedulers.mainThread()).subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(statusOverall: GenericOverall) { - // unused atm - } + override fun onNext(statusOverall: GenericOverall) { + // unused atm + } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to clear status", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to clear status", e) + } - override fun onComplete() { - dismiss() + override fun onComplete() { + dismiss() + } } - }) + ) } private fun setStatus(statusType: StatusType) { @@ -398,24 +416,26 @@ class SetStatusDialogFragment : Schedulers .io() ) - .observeOn(AndroidSchedulers.mainThread()).subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .observeOn(AndroidSchedulers.mainThread()).subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(statusOverall: GenericOverall) { - Log.d(TAG, "statusType successfully set") - } + override fun onNext(statusOverall: GenericOverall) { + Log.d(TAG, "statusType successfully set") + } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to set statusType", e) - clearTopStatus() - } + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to set statusType", e) + clearTopStatus() + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun visualizeStatus(statusType: String) { @@ -424,16 +444,23 @@ class SetStatusDialogFragment : private fun visualizeStatus(statusType: StatusType) { clearTopStatus() - val views: Triple = when (statusType) { - StatusType.ONLINE -> Triple(binding.onlineStatus, binding.onlineHeadline, binding.onlineIcon) - StatusType.AWAY -> Triple(binding.awayStatus, binding.awayHeadline, binding.awayIcon) - StatusType.DND -> Triple(binding.dndStatus, binding.dndHeadline, binding.dndIcon) - StatusType.INVISIBLE -> Triple(binding.invisibleStatus, binding.invisibleHeadline, binding.invisibleIcon) - else -> { - Log.d(TAG, "unknown status") - return + val views: Triple = + when (statusType) { + StatusType.ONLINE -> Triple(binding.onlineStatus, binding.onlineHeadline, binding.onlineIcon) + StatusType.AWAY -> Triple(binding.awayStatus, binding.awayHeadline, binding.awayIcon) + StatusType.DND -> Triple(binding.dndStatus, binding.dndHeadline, binding.dndIcon) + StatusType.INVISIBLE -> + Triple( + binding.invisibleStatus, + binding.invisibleHeadline, + binding.invisibleIcon + ) + + else -> { + Log.d(TAG, "unknown status") + return + } } - } views.first.isChecked = true viewThemeUtils.platform.colorTextView(views.second, ColorRole.ON_SECONDARY_CONTAINER) } @@ -475,24 +502,26 @@ class SetStatusDialogFragment : ) .subscribeOn(Schedulers.io()) .observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - - override fun onSubscribe(d: Disposable) { // unused atm - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(t: GenericOverall) { - Log.d(TAG, "CustomStatusMessage successfully set") - dismiss() - } + override fun onNext(t: GenericOverall) { + Log.d(TAG, "CustomStatusMessage successfully set") + dismiss() + } - override fun onError(e: Throwable) { - Log.e(TAG, "failed to set CustomStatusMessage", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "failed to set CustomStatusMessage", e) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } else { val clearAt = clearAtToUnixTime(selectedPredefinedStatus!!.clearAt) @@ -503,20 +532,22 @@ class SetStatusDialogFragment : if (clearAt == -1L) null else clearAt ) .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread())?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) = Unit + .observeOn(AndroidSchedulers.mainThread())?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) = Unit - override fun onNext(t: GenericOverall) { - Log.d(TAG, "PredefinedStatusMessage successfully set") - dismiss() - } + override fun onNext(t: GenericOverall) { + Log.d(TAG, "PredefinedStatusMessage successfully set") + dismiss() + } - override fun onError(e: Throwable) { - Log.e(TAG, "failed to set PredefinedStatusMessage", e) - } + override fun onError(e: Throwable) { + Log.e(TAG, "failed to set PredefinedStatusMessage", e) + } - override fun onComplete() = Unit - }) + override fun onComplete() = Unit + } + ) } } diff --git a/app/src/main/java/com/nextcloud/talk/ui/dialog/ShowReactionsDialog.kt b/app/src/main/java/com/nextcloud/talk/ui/dialog/ShowReactionsDialog.kt index 467f952130d..787533f8c8b 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/dialog/ShowReactionsDialog.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/dialog/ShowReactionsDialog.kt @@ -123,20 +123,22 @@ class ShowReactionsDialog( binding.emojiReactionsTabs.getTabAt(0)?.select() - binding.emojiReactionsTabs.addOnTabSelectedListener(object : OnTabSelectedListener { - override fun onTabSelected(tab: TabLayout.Tab) { - // called when a tab is reselected - updateParticipantsForEmoji(chatMessage, tab.customView?.tag as String?) - } + binding.emojiReactionsTabs.addOnTabSelectedListener( + object : OnTabSelectedListener { + override fun onTabSelected(tab: TabLayout.Tab) { + // called when a tab is reselected + updateParticipantsForEmoji(chatMessage, tab.customView?.tag as String?) + } - override fun onTabUnselected(tab: TabLayout.Tab) { - // called when a tab is reselected - } + override fun onTabUnselected(tab: TabLayout.Tab) { + // called when a tab is reselected + } - override fun onTabReselected(tab: TabLayout.Tab) { - // called when a tab is reselected + override fun onTabReselected(tab: TabLayout.Tab) { + // called when a tab is reselected + } } - }) + ) viewThemeUtils.material.themeTabLayoutOnSurface(binding.emojiReactionsTabs) @@ -161,38 +163,40 @@ class ShowReactionsDialog( ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(reactionsOverall: ReactionsOverall) { - val reactionVoters: ArrayList = ArrayList() - if (reactionsOverall.ocs?.data != null) { - val map = reactionsOverall.ocs?.data - for (key in map!!.keys) { - for (reactionVoter in reactionsOverall.ocs?.data!![key]!!) { - reactionVoters.add(ReactionItem(reactionVoter, key)) + override fun onNext(reactionsOverall: ReactionsOverall) { + val reactionVoters: ArrayList = ArrayList() + if (reactionsOverall.ocs?.data != null) { + val map = reactionsOverall.ocs?.data + for (key in map!!.keys) { + for (reactionVoter in reactionsOverall.ocs?.data!![key]!!) { + reactionVoters.add(ReactionItem(reactionVoter, key)) + } } - } - Collections.sort(reactionVoters, ReactionComparator(user?.userId)) + Collections.sort(reactionVoters, ReactionComparator(user?.userId)) - adapter?.list?.addAll(reactionVoters) - adapter?.notifyDataSetChanged() - } else { - Log.e(TAG, "no voters for this reaction") + adapter?.list?.addAll(reactionVoters) + adapter?.notifyDataSetChanged() + } else { + Log.e(TAG, "no voters for this reaction") + } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "failed to retrieve list of reaction voters") - } + override fun onError(e: Throwable) { + Log.e(TAG, "failed to retrieve list of reaction voters") + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } override fun onClick(reactionItem: ReactionItem) { @@ -216,23 +220,25 @@ class ShowReactionsDialog( ) ?.subscribeOn(Schedulers.io()) ?.observeOn(AndroidSchedulers.mainThread()) - ?.subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + ?.subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(genericOverall: GenericOverall) { - Log.d(TAG, "deleted reaction: $emoji") - } + override fun onNext(genericOverall: GenericOverall) { + Log.d(TAG, "deleted reaction: $emoji") + } - override fun onError(e: Throwable) { - Log.e(TAG, "error while deleting reaction: $emoji") - } + override fun onError(e: Throwable) { + Log.e(TAG, "error while deleting reaction: $emoji") + } - override fun onComplete() { - dismiss() + override fun onComplete() { + dismiss() + } } - }) + ) } companion object { @@ -261,44 +267,48 @@ class ShowReactionsDialog( } // own account - val ownAccount = compareOwnAccount( - activeUser, - reactionItem1.reactionVoter.actorId, - reactionItem2.reactionVoter.actorId - ) + val ownAccount = + compareOwnAccount( + activeUser, + reactionItem1.reactionVoter.actorId, + reactionItem2.reactionVoter.actorId + ) if (ownAccount != 0) { return ownAccount } // display-name - val displayName = StringComparator() - .compare( - reactionItem1.reactionVoter.actorDisplayName, - reactionItem2.reactionVoter.actorDisplayName - ) + val displayName = + StringComparator() + .compare( + reactionItem1.reactionVoter.actorDisplayName, + reactionItem2.reactionVoter.actorDisplayName + ) if (displayName != 0) { return displayName } // timestamp - val timestamp = LongComparator() - .compare( - reactionItem1.reactionVoter.timestamp, - reactionItem2.reactionVoter.timestamp - ) + val timestamp = + LongComparator() + .compare( + reactionItem1.reactionVoter.timestamp, + reactionItem2.reactionVoter.timestamp + ) if (timestamp != 0) { return timestamp } // actor-id - val actorId = StringComparator() - .compare( - reactionItem1.reactionVoter.actorId, - reactionItem2.reactionVoter.actorId - ) + val actorId = + StringComparator() + .compare( + reactionItem1.reactionVoter.actorId, + reactionItem2.reactionVoter.actorId + ) if (actorId != 0) { return actorId diff --git a/app/src/main/java/com/nextcloud/talk/ui/recyclerview/MessageSwipeCallback.kt b/app/src/main/java/com/nextcloud/talk/ui/recyclerview/MessageSwipeCallback.kt index 62ee05c665c..811553998f6 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/recyclerview/MessageSwipeCallback.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/recyclerview/MessageSwipeCallback.kt @@ -176,13 +176,14 @@ class MessageSwipeCallback(private val context: Context, private val messageSwip val alpha: Int val scale: Float if (showing) { - scale = if (replyButtonProgress <= SCALE_PROGRESS_TOP_THRESHOLD) { - SCALE_PROGRESS_MULTIPLIER * (replyButtonProgress / SCALE_PROGRESS_TOP_THRESHOLD) - } else { - SCALE_PROGRESS_MULTIPLIER - - SCALE_PROGRESS_BOTTOM_THRESHOLD * - ((replyButtonProgress - SCALE_PROGRESS_TOP_THRESHOLD) / SCALE_PROGRESS_BOTTOM_THRESHOLD) - } + scale = + if (replyButtonProgress <= SCALE_PROGRESS_TOP_THRESHOLD) { + SCALE_PROGRESS_MULTIPLIER * (replyButtonProgress / SCALE_PROGRESS_TOP_THRESHOLD) + } else { + SCALE_PROGRESS_MULTIPLIER - + SCALE_PROGRESS_BOTTOM_THRESHOLD * + ((replyButtonProgress - SCALE_PROGRESS_TOP_THRESHOLD) / SCALE_PROGRESS_BOTTOM_THRESHOLD) + } alpha = min(FULLY_OPAQUE, FULLY_OPAQUE * (replyButtonProgress / SCALE_PROGRESS_TOP_THRESHOLD)).toInt() } else { scale = replyButtonProgress @@ -201,25 +202,28 @@ class MessageSwipeCallback(private val context: Context, private val messageSwip } private fun drawReplyIcon(alpha: Int, scale: Float, canvas: Canvas) { - val x: Int = if (view.translationX > convertToDp(SWIPE_LIMIT)) { - convertToDp(SWIPE_LIMIT) / AXIS_BASE - } else { - (view.translationX / AXIS_BASE).toInt() - } + val x: Int = + if (view.translationX > convertToDp(SWIPE_LIMIT)) { + convertToDp(SWIPE_LIMIT) / AXIS_BASE + } else { + (view.translationX / AXIS_BASE).toInt() + } val y = (view.top + view.measuredHeight / AXIS_BASE).toFloat() shareRound.alpha = alpha imageDrawable.alpha = alpha - shareRound.colorFilter = PorterDuffColorFilter( - ContextCompat.getColor(context, R.color.bg_message_list_incoming_bubble), - PorterDuff.Mode.SRC_IN - ) - imageDrawable.colorFilter = PorterDuffColorFilter( - ContextCompat.getColor(context, R.color.high_emphasis_text), - PorterDuff.Mode.SRC_IN - ) + shareRound.colorFilter = + PorterDuffColorFilter( + ContextCompat.getColor(context, R.color.bg_message_list_incoming_bubble), + PorterDuff.Mode.SRC_IN + ) + imageDrawable.colorFilter = + PorterDuffColorFilter( + ContextCompat.getColor(context, R.color.high_emphasis_text), + PorterDuff.Mode.SRC_IN + ) shareRound.setBounds( (x - convertToDp(BACKGROUND_BOUNDS_PIXEL) * scale).toInt(), diff --git a/app/src/main/java/com/nextcloud/talk/ui/theme/MaterialSchemesProvider.kt b/app/src/main/java/com/nextcloud/talk/ui/theme/MaterialSchemesProvider.kt index fc6b8f2e7da..40050debaf0 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/theme/MaterialSchemesProvider.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/theme/MaterialSchemesProvider.kt @@ -27,6 +27,8 @@ import com.nextcloud.talk.models.json.capabilities.Capabilities interface MaterialSchemesProvider { fun getMaterialSchemesForUser(user: User?): MaterialSchemes + fun getMaterialSchemesForCapabilities(capabilities: Capabilities?): MaterialSchemes + fun getMaterialSchemesForCurrentUser(): MaterialSchemes } diff --git a/app/src/main/java/com/nextcloud/talk/ui/theme/MaterialSchemesProviderImpl.kt b/app/src/main/java/com/nextcloud/talk/ui/theme/MaterialSchemesProviderImpl.kt index ffccd9f9488..8f388ef0f65 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/theme/MaterialSchemesProviderImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/theme/MaterialSchemesProviderImpl.kt @@ -31,19 +31,21 @@ import com.nextcloud.talk.utils.database.user.CurrentUserProviderNew import java.util.concurrent.ConcurrentHashMap import javax.inject.Inject -internal class MaterialSchemesProviderImpl @Inject constructor( +internal class MaterialSchemesProviderImpl +@Inject +constructor( private val userProvider: CurrentUserProviderNew, private val colorUtil: ColorUtil ) : MaterialSchemesProvider { - private val themeCache: ConcurrentHashMap = ConcurrentHashMap() override fun getMaterialSchemesForUser(user: User?): MaterialSchemes { - val url: String = if (user?.baseUrl != null) { - user.baseUrl!! - } else { - FALLBACK_URL - } + val url: String = + if (user?.baseUrl != null) { + user.baseUrl!! + } else { + FALLBACK_URL + } if (!themeCache.containsKey(url)) { themeCache[url] = getMaterialSchemesForCapabilities(user?.capabilities) diff --git a/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt b/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt index 5a06acef8cf..74e4c0f3646 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/theme/TalkSpecificViewThemeUtils.kt @@ -73,7 +73,9 @@ import kotlin.math.roundToInt * */ @Suppress("TooManyFunctions") -class TalkSpecificViewThemeUtils @Inject constructor( +class TalkSpecificViewThemeUtils +@Inject +constructor( schemes: MaterialSchemes, private val appcompat: AndroidXViewThemeUtils ) : @@ -87,39 +89,46 @@ class TalkSpecificViewThemeUtils @Inject constructor( bubbleResource = R.drawable.shape_grouped_incoming_message } - val bgBubbleColor = if (deleted) { - resources.getColor(R.color.bg_message_list_incoming_bubble_deleted, null) - } else { - resources.getColor(R.color.bg_message_list_incoming_bubble, null) - } - val bubbleDrawable = DisplayUtils.getMessageSelector( - bgBubbleColor, - resources.getColor(R.color.transparent, null), - bgBubbleColor, - bubbleResource - ) + val bgBubbleColor = + if (deleted) { + resources.getColor(R.color.bg_message_list_incoming_bubble_deleted, null) + } else { + resources.getColor(R.color.bg_message_list_incoming_bubble, null) + } + val bubbleDrawable = + DisplayUtils + .getMessageSelector( + bgBubbleColor, + resources.getColor(R.color.transparent, null), + bgBubbleColor, + bubbleResource + ) ViewCompat.setBackground(bubble, bubbleDrawable) } fun themeOutgoingMessageBubble(bubble: View, grouped: Boolean, deleted: Boolean) { withScheme(bubble) { scheme -> - val bgBubbleColor = if (deleted) { - ColorUtils.setAlphaComponent(scheme.surfaceVariant, HALF_ALPHA_INT) - } else { - scheme.surfaceVariant - } - - val layout = if (grouped) { - R.drawable.shape_grouped_outcoming_message - } else { - R.drawable.shape_outcoming_message - } - val bubbleDrawable = DisplayUtils.getMessageSelector( - bgBubbleColor, - ResourcesCompat.getColor(bubble.resources, R.color.transparent, null), - bgBubbleColor, - layout - ) + val bgBubbleColor = + if (deleted) { + ColorUtils.setAlphaComponent(scheme.surfaceVariant, HALF_ALPHA_INT) + } else { + scheme.surfaceVariant + } + + val layout = + if (grouped) { + R.drawable.shape_grouped_outcoming_message + } else { + R.drawable.shape_outcoming_message + } + val bubbleDrawable = + DisplayUtils + .getMessageSelector( + bgBubbleColor, + ResourcesCompat.getColor(bubble.resources, R.color.transparent, null), + bgBubbleColor, + layout + ) ViewCompat.setBackground(bubble, bubbleDrawable) } } @@ -161,9 +170,10 @@ class TalkSpecificViewThemeUtils @Inject constructor( fun setCheckedBackground(emoji: EmojiTextView) { withScheme(emoji) { scheme -> - val drawable = AppCompatResources - .getDrawable(emoji.context, R.drawable.reaction_self_bottom_sheet_background)!! - .mutate() + val drawable = + AppCompatResources + .getDrawable(emoji.context, R.drawable.reaction_self_bottom_sheet_background)!! + .mutate() DrawableCompat.setTintList( drawable, ColorStateList.valueOf(scheme.primary) @@ -174,17 +184,19 @@ class TalkSpecificViewThemeUtils @Inject constructor( fun setCheckedBackground(linearLayout: LinearLayout, incoming: Boolean) { withScheme(linearLayout) { scheme -> - val drawable = AppCompatResources - .getDrawable(linearLayout.context, R.drawable.reaction_self_background)!! - .mutate() - val backgroundColor = if (incoming) { - scheme.primaryContainer - } else { - ContextCompat.getColor( - linearLayout.context, - R.color.bg_message_list_incoming_bubble - ) - } + val drawable = + AppCompatResources + .getDrawable(linearLayout.context, R.drawable.reaction_self_background)!! + .mutate() + val backgroundColor = + if (incoming) { + scheme.primaryContainer + } else { + ContextCompat.getColor( + linearLayout.context, + R.color.bg_message_list_incoming_bubble + ) + } DrawableCompat.setTintList( drawable, ColorStateList.valueOf(backgroundColor) @@ -195,10 +207,12 @@ class TalkSpecificViewThemeUtils @Inject constructor( fun getPlaceholderImage(context: Context, mimetype: String?): Drawable? { val drawableResourceId = DrawableUtils.getDrawableResourceIdForMimeType(mimetype) - val drawable = AppCompatResources.getDrawable( - context, - drawableResourceId - ) + val drawable = + AppCompatResources + .getDrawable( + context, + drawableResourceId + ) if (drawable != null && THEMEABLE_PLACEHOLDER_IDS.contains(drawableResourceId)) { colorDrawable(context, drawable) } @@ -295,11 +309,7 @@ class TalkSpecificViewThemeUtils @Inject constructor( } } - fun themeAndHighlightText( - textView: TextView, - originalText: String?, - c: String? - ) { + fun themeAndHighlightText(textView: TextView, originalText: String?, c: String?) { withScheme(textView) { scheme -> var constraint = c constraint = FlexibleUtils.toLowerCase(constraint) @@ -315,8 +325,10 @@ class TalkSpecificViewThemeUtils @Inject constructor( Spannable.SPAN_EXCLUSIVE_EXCLUSIVE ) spanText.setSpan(StyleSpan(Typeface.BOLD), start, end, Spannable.SPAN_EXCLUSIVE_EXCLUSIVE) - start = FlexibleUtils.toLowerCase(originalText) - .indexOf(constraint, end + 1) // +1 skips the consecutive span + start = + FlexibleUtils + .toLowerCase(originalText) + .indexOf(constraint, end + 1) // +1 skips the consecutive span } while (start != -1) textView.setText(spanText, TextView.BufferType.SPANNABLE) } else { @@ -374,11 +386,7 @@ class TalkSpecificViewThemeUtils @Inject constructor( } } - fun getTextColor( - isOutgoingMessage: Boolean, - isSelfReaction: Boolean, - binding: ReactionsInsideMessageBinding - ): Int { + fun getTextColor(isOutgoingMessage: Boolean, isSelfReaction: Boolean, binding: ReactionsInsideMessageBinding): Int { return withScheme(binding.root) { scheme -> return@withScheme if (!isOutgoingMessage || isSelfReaction) { ContextCompat.getColor(binding.root.context, R.color.high_emphasis_text) @@ -389,10 +397,11 @@ class TalkSpecificViewThemeUtils @Inject constructor( } companion object { - private val THEMEABLE_PLACEHOLDER_IDS = listOf( - R.drawable.ic_mimetype_package_x_generic, - R.drawable.ic_mimetype_folder - ) + private val THEMEABLE_PLACEHOLDER_IDS = + listOf( + R.drawable.ic_mimetype_package_x_generic, + R.drawable.ic_mimetype_folder + ) private val ALPHA_80_INT: Int = (255 * 0.8).roundToInt() diff --git a/app/src/main/java/com/nextcloud/talk/ui/theme/ViewThemeUtils.kt b/app/src/main/java/com/nextcloud/talk/ui/theme/ViewThemeUtils.kt index 5c058d16bc5..4475f68664f 100644 --- a/app/src/main/java/com/nextcloud/talk/ui/theme/ViewThemeUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/ui/theme/ViewThemeUtils.kt @@ -30,7 +30,9 @@ import com.nextcloud.android.common.ui.theme.utils.MaterialViewThemeUtils import javax.inject.Inject @Suppress("TooManyFunctions") -class ViewThemeUtils @Inject constructor( +class ViewThemeUtils +@Inject +constructor( schemes: MaterialSchemes, @JvmField val platform: AndroidViewThemeUtils, diff --git a/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkFromFileRequestBody.kt b/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkFromFileRequestBody.kt index e3451af0639..4cc9e55edf1 100644 --- a/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkFromFileRequestBody.kt +++ b/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkFromFileRequestBody.kt @@ -55,6 +55,7 @@ class ChunkFromFileRequestBody( private var mTransferred: Long private var mDataTransferListener: OnDataTransferProgressListener private val mBuffer = ByteBuffer.allocate(BUFFER_CAPACITY) + override fun contentLength(): Long { return try { mChunkSize.coerceAtMost(mChannel.size() - mOffset) diff --git a/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt b/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt index 5c0fcbc6682..5b94375da17 100644 --- a/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt +++ b/app/src/main/java/com/nextcloud/talk/upload/chunked/ChunkedFileUploader.kt @@ -82,17 +82,14 @@ class ChunkedFileUploader( } @Suppress("Detekt.TooGenericExceptionCaught") - fun upload( - localFile: File, - mimeType: MediaType?, - targetPath: String - ): Boolean { + fun upload(localFile: File, mimeType: MediaType?, targetPath: String): Boolean { try { val uploadFolderUri: String = remoteChunkUrl + "/" + FileUtils.md5Sum(localFile) - val davResource = DavResource( - okHttpClientNoRedirects!!, - uploadFolderUri.toHttpUrlOrNull()!! - ) + val davResource = + DavResource( + okHttpClientNoRedirects!!, + uploadFolderUri.toHttpUrlOrNull()!! + ) createFolder(davResource) @@ -136,10 +133,7 @@ class ChunkedFileUploader( } @Suppress("Detekt.ComplexMethod") - private fun getUploadedChunks( - davResource: DavResource, - uploadFolderUri: String - ): MutableList { + private fun getUploadedChunks(davResource: DavResource, uploadFolderUri: String): MutableList { val davResponse = DavResponse() val memberElements: MutableList = ArrayList() val rootElement = arrayOfNulls(1) @@ -197,11 +191,12 @@ class ChunkedFileUploader( val nextChunk: Chunk? = findNextFittingChunk(chunks, start) if (nextChunk == null) { // create new chunk - val end: Long = if (start + CHUNK_SIZE <= length) { - start + CHUNK_SIZE - 1 - } else { - length - } + val end: Long = + if (start + CHUNK_SIZE <= length) { + start + CHUNK_SIZE - 1 + } else { + length + } missingChunks.add(Chunk(start, end)) start = end + 1 } else if (nextChunk.start == start) { @@ -246,21 +241,23 @@ class ChunkedFileUploader( // Log.d(TAG, "chunk.start:${chunk.start}") // Log.d(TAG, "chunk.end:${chunk.end}") - val chunkFromFileRequestBody = ChunkFromFileRequestBody( - localFile, - mimeType, - channel, - chunkSize, - chunk.start, - listener - ) + val chunkFromFileRequestBody = + ChunkFromFileRequestBody( + localFile, + mimeType, + channel, + chunkSize, + chunk.start, + listener + ) val chunkUri = "$uploadFolderUri/$startString-$endString" - val davResource = DavResource( - okHttpClientNoRedirects!!, - chunkUri.toHttpUrlOrNull()!! - ) + val davResource = + DavResource( + okHttpClientNoRedirects!!, + chunkUri.toHttpUrlOrNull()!! + ) davResource.put( chunkFromFileRequestBody ) { response: Response -> @@ -306,11 +303,13 @@ class ChunkedFileUploader( } private fun assembleChunks(uploadFolderUri: String, targetPath: String) { - val destinationUri: String = ApiUtils.getUrlForFileUpload( - currentUser.baseUrl, - currentUser.userId, - targetPath - ) + val destinationUri: String = + ApiUtils + .getUrlForFileUpload( + currentUser.baseUrl, + currentUser.userId, + targetPath + ) val originUri = "$uploadFolderUri/.file" DavResource( @@ -359,30 +358,39 @@ class ChunkedFileUploader( is OCId -> { remoteFileBrowserItem.remoteId = property.ocId } + is ResourceType -> { remoteFileBrowserItem.isFile = !property.types.contains(ResourceType.COLLECTION) } + is GetLastModified -> { remoteFileBrowserItem.modifiedTimestamp = property.lastModified } + is GetContentType -> { remoteFileBrowserItem.mimeType = property.type } + is OCSize -> { remoteFileBrowserItem.size = property.ocSize } + is NCPreview -> { remoteFileBrowserItem.hasPreview = property.isNcPreview } + is OCFavorite -> { remoteFileBrowserItem.isFavorite = property.isOcFavorite } + is DisplayName -> { remoteFileBrowserItem.displayName = property.displayName } + is NCEncrypted -> { remoteFileBrowserItem.isEncrypted = property.isNcEncrypted } + is NCPermission -> { remoteFileBrowserItem.permissions = property.ncPermission } diff --git a/app/src/main/java/com/nextcloud/talk/upload/chunked/OnDataTransferProgressListener.kt b/app/src/main/java/com/nextcloud/talk/upload/chunked/OnDataTransferProgressListener.kt index 049fee4fb16..6ba46334271 100644 --- a/app/src/main/java/com/nextcloud/talk/upload/chunked/OnDataTransferProgressListener.kt +++ b/app/src/main/java/com/nextcloud/talk/upload/chunked/OnDataTransferProgressListener.kt @@ -25,7 +25,5 @@ package com.nextcloud.talk.upload.chunked interface OnDataTransferProgressListener { - fun onTransferProgress( - percentage: Int - ) + fun onTransferProgress(percentage: Int) } diff --git a/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt b/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt index 514aae0da9b..d21f9060589 100644 --- a/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt +++ b/app/src/main/java/com/nextcloud/talk/upload/normal/FileUploader.kt @@ -21,12 +21,7 @@ class FileUploader( val roomToken: String, val ncApi: NcApi ) { - fun upload( - sourceFileUri: Uri, - fileName: String, - remotePath: String, - metaData: String? - ): Observable { + fun upload(sourceFileUri: Uri, fileName: String, remotePath: String, metaData: String?): Observable { return ncApi.uploadFile( ApiUtils.getCredentials(currentUser.username, currentUser.token), ApiUtils.getUrlForFileUpload(currentUser.baseUrl, currentUser.userId, remotePath), diff --git a/app/src/main/java/com/nextcloud/talk/users/UserManager.kt b/app/src/main/java/com/nextcloud/talk/users/UserManager.kt index 378cb811295..2967958f231 100644 --- a/app/src/main/java/com/nextcloud/talk/users/UserManager.kt +++ b/app/src/main/java/com/nextcloud/talk/users/UserManager.kt @@ -143,10 +143,12 @@ class UserManager internal constructor(private val userRepository: UsersReposito return findUser(userAttributes) .map { user: User? -> when (user) { - null -> createUser( - username, - userAttributes - ) + null -> + createUser( + username, + userAttributes + ) + else -> { user.token = userAttributes.token user.baseUrl = userAttributes.serverUrl @@ -187,17 +189,20 @@ class UserManager internal constructor(private val userRepository: UsersReposito user.token = userAttributes.token user.displayName = userAttributes.displayName if (userAttributes.pushConfigurationState != null) { - user.pushConfigurationState = LoganSquare - .parse(userAttributes.pushConfigurationState, PushConfigurationState::class.java) + user.pushConfigurationState = + LoganSquare + .parse(userAttributes.pushConfigurationState, PushConfigurationState::class.java) } if (userAttributes.capabilities != null) { - user.capabilities = LoganSquare - .parse(userAttributes.capabilities, Capabilities::class.java) + user.capabilities = + LoganSquare + .parse(userAttributes.capabilities, Capabilities::class.java) } user.clientCertificate = userAttributes.certificateAlias if (userAttributes.externalSignalingServer != null) { - user.externalSignalingServer = LoganSquare - .parse(userAttributes.externalSignalingServer, ExternalSignalingServer::class.java) + user.externalSignalingServer = + LoganSquare + .parse(userAttributes.externalSignalingServer, ExternalSignalingServer::class.java) } user.current = userAttributes.currentUser == true } @@ -211,8 +216,9 @@ class UserManager internal constructor(private val userRepository: UsersReposito user.displayName = userAttributes.displayName } if (userAttributes.pushConfigurationState != null) { - user.pushConfigurationState = LoganSquare - .parse(userAttributes.pushConfigurationState, PushConfigurationState::class.java) + user.pushConfigurationState = + LoganSquare + .parse(userAttributes.pushConfigurationState, PushConfigurationState::class.java) } if (!TextUtils.isEmpty(userAttributes.userId)) { user.userId = userAttributes.userId @@ -224,8 +230,9 @@ class UserManager internal constructor(private val userRepository: UsersReposito user.clientCertificate = userAttributes.certificateAlias } if (!TextUtils.isEmpty(userAttributes.externalSignalingServer)) { - user.externalSignalingServer = LoganSquare - .parse(userAttributes.externalSignalingServer, ExternalSignalingServer::class.java) + user.externalSignalingServer = + LoganSquare + .parse(userAttributes.externalSignalingServer, ExternalSignalingServer::class.java) } user.current = userAttributes.currentUser == true return user diff --git a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt index 26232c2a6ad..566e93db410 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/AccountUtils.kt @@ -97,12 +97,15 @@ object AccountUtils { val packageManager = context.packageManager var appName = "" try { - appName = packageManager.getApplicationLabel( - packageManager.getApplicationInfo( - packageName, - PackageManager.GET_META_DATA - ) - ) as String + appName = + packageManager + .getApplicationLabel( + packageManager + .getApplicationInfo( + packageName, + PackageManager.GET_META_DATA + ) + ) as String } catch (e: PackageManager.NameNotFoundException) { Log.e(TAG, "Failed to get app name based on package") } @@ -116,10 +119,12 @@ object AccountUtils { val packageInfo = pm.getPackageInfo(context.getString(R.string.nc_import_accounts_from), 0) if (packageInfo.versionCode >= MIN_SUPPORTED_FILES_APP_VERSION) { val ownSignatures = pm.getPackageInfo(context.packageName, PackageManager.GET_SIGNATURES).signatures - val filesAppSignatures = pm.getPackageInfo( - context.getString(R.string.nc_import_accounts_from), - PackageManager.GET_SIGNATURES - ).signatures + val filesAppSignatures = + pm + .getPackageInfo( + context.getString(R.string.nc_import_accounts_from), + PackageManager.GET_SIGNATURES + ).signatures return if (Arrays.equals(ownSignatures, filesAppSignatures)) { val accMgr = AccountManager.get(context) diff --git a/app/src/main/java/com/nextcloud/talk/utils/AudioUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/AudioUtils.kt index 8151ad39e7e..cfe26d14dde 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/AudioUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/AudioUtils.kt @@ -45,10 +45,12 @@ object AudioUtils : DefaultLifecycleObserver { private const val VALUE_10 = 10 private const val TIME_LIMIT = 5000 private const val DEFAULT_SIZE = 500 + private enum class LifeCycleFlag { PAUSED, RESUMED } + private lateinit var currentLifeCycleFlag: LifeCycleFlag override fun onResume(owner: LifecycleOwner) { @@ -112,96 +114,103 @@ object AudioUtils : DefaultLifecycleObserver { * ******************************************************************************************** */ - mediaCodec.setCallback(object : MediaCodec.Callback() { - private var extractor: MediaExtractor? = null - val tempList = mutableListOf() - override fun onInputBufferAvailable(codec: MediaCodec, index: Int) { - // Setting up the extractor if not already done - if (extractor == null) { - extractor = MediaExtractor() - try { - extractor!!.setDataSource(path) - extractor!!.selectTrack(0) - } catch (e: IOException) { - e.printStackTrace() + mediaCodec.setCallback( + object : MediaCodec.Callback() { + private var extractor: MediaExtractor? = null + val tempList = mutableListOf() + + override fun onInputBufferAvailable(codec: MediaCodec, index: Int) { + // Setting up the extractor if not already done + if (extractor == null) { + extractor = MediaExtractor() + try { + extractor!!.setDataSource(path) + extractor!!.selectTrack(0) + } catch (e: IOException) { + e.printStackTrace() + } } - } - // Boiler plate, Extracts a buffer of encoded audio data to be sent to the codec for processing - val byteBuffer = codec.getInputBuffer(index) - if (byteBuffer != null) { - val sampleSize = extractor!!.readSampleData(byteBuffer, 0) - if (sampleSize > 0) { - val isOver = !extractor!!.advance() - codec.queueInputBuffer( - index, - 0, - sampleSize, - extractor!!.sampleTime, - if (isOver) MediaCodec.BUFFER_FLAG_END_OF_STREAM else 0 - ) + // Boiler plate, Extracts a buffer of encoded audio data to be sent to the codec for processing + val byteBuffer = codec.getInputBuffer(index) + if (byteBuffer != null) { + val sampleSize = extractor!!.readSampleData(byteBuffer, 0) + if (sampleSize > 0) { + val isOver = !extractor!!.advance() + codec.queueInputBuffer( + index, + 0, + sampleSize, + extractor!!.sampleTime, + if (isOver) MediaCodec.BUFFER_FLAG_END_OF_STREAM else 0 + ) + } } } - } - override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo) { - // Boiler plate to get the audio data in a usable form - val outputBuffer = codec.getOutputBuffer(index) - val bufferFormat = codec.getOutputFormat(index) - val samples = outputBuffer!!.order(ByteOrder.nativeOrder()).asShortBuffer() - val numChannels = bufferFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) - if (index < 0 || index >= numChannels) { - return - } - val sampleLength = (samples.remaining() / numChannels) + override fun onOutputBufferAvailable(codec: MediaCodec, index: Int, info: MediaCodec.BufferInfo) { + // Boiler plate to get the audio data in a usable form + val outputBuffer = codec.getOutputBuffer(index) + val bufferFormat = codec.getOutputFormat(index) + val samples = outputBuffer!!.order(ByteOrder.nativeOrder()).asShortBuffer() + val numChannels = bufferFormat.getInteger(MediaFormat.KEY_CHANNEL_COUNT) + if (index < 0 || index >= numChannels) { + return + } + val sampleLength = (samples.remaining() / numChannels) + + // Squeezes the value of each sample between [0,1) using y = (x-1)/x + for (i in 0 until sampleLength) { + val x = abs(samples[i * numChannels + index].toInt()) / VALUE_10 + val y = (if (x > 0) ((x - 1) / x.toFloat()) else x.toFloat()) + tempList.add(y) + } - // Squeezes the value of each sample between [0,1) using y = (x-1)/x - for (i in 0 until sampleLength) { - val x = abs(samples[i * numChannels + index].toInt()) / VALUE_10 - val y = (if (x > 0) ((x - 1) / x.toFloat()) else x.toFloat()) - tempList.add(y) + codec.releaseOutputBuffer(index, false) + + // Cancels the process if it ends, exceeds the time limit, or the activity falls out of view + val currTime = SystemClock.elapsedRealtime() - startTime + if (info.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM > 0 || + currTime > TIME_LIMIT || + currentLifeCycleFlag == LifeCycleFlag.PAUSED + ) { + Log.d( + TAG, + "Processing ended with time: $currTime \n" + + "Is finished: ${info.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM > 0} \n" + + "Lifecycle state: $currentLifeCycleFlag" + ) + codec.stop() + codec.release() + extractor!!.release() + extractor = null + result = + if (currTime < TIME_LIMIT) { + tempList + } else { + Log.e( + TAG, + "Error in MediaCodec Callback:\n\tonOutputBufferAvailable: Time limit exceeded" + ) + null + } + } } - codec.releaseOutputBuffer(index, false) - - // Cancels the process if it ends, exceeds the time limit, or the activity falls out of view - val currTime = SystemClock.elapsedRealtime() - startTime - if (info.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM > 0 || - currTime > TIME_LIMIT || - currentLifeCycleFlag == LifeCycleFlag.PAUSED - ) { - Log.d( - TAG, - "Processing ended with time: $currTime \n" + - "Is finished: ${info.flags and MediaCodec.BUFFER_FLAG_END_OF_STREAM > 0} \n" + - "Lifecycle state: $currentLifeCycleFlag" - ) + override fun onError(codec: MediaCodec, e: CodecException) { + Log.e(TAG, "Error in MediaCodec Callback: \n$e") codec.stop() codec.release() extractor!!.release() extractor = null - result = if (currTime < TIME_LIMIT) { - tempList - } else { - Log.e(TAG, "Error in MediaCodec Callback:\n\tonOutputBufferAvailable: Time limit exceeded") - null - } + result = null } - } - - override fun onError(codec: MediaCodec, e: CodecException) { - Log.e(TAG, "Error in MediaCodec Callback: \n$e") - codec.stop() - codec.release() - extractor!!.release() - extractor = null - result = null - } - override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) { - // unused atm + override fun onOutputFormatChanged(codec: MediaCodec, format: MediaFormat) { + // unused atm + } } - }) + ) // More Boiler plate to start the codec mediaFormat.setInteger(MediaFormat.KEY_PCM_ENCODING, AudioFormat.ENCODING_PCM_16BIT) diff --git a/app/src/main/java/com/nextcloud/talk/utils/BitmapShrinker.kt b/app/src/main/java/com/nextcloud/talk/utils/BitmapShrinker.kt index 7b457677211..8cb9cd475be 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/BitmapShrinker.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/BitmapShrinker.kt @@ -34,21 +34,13 @@ object BitmapShrinker { private const val DEGREES_270 = 270f @JvmStatic - fun shrinkBitmap( - path: String, - reqWidth: Int, - reqHeight: Int - ): Bitmap { + fun shrinkBitmap(path: String, reqWidth: Int, reqHeight: Int): Bitmap { val bitmap = decodeBitmap(path, reqWidth, reqHeight) return rotateBitmap(path, bitmap) } // solution inspired by https://developer.android.com/topic/performance/graphics/load-bitmap - private fun decodeBitmap( - path: String, - requestedWidth: Int, - requestedHeight: Int - ): Bitmap { + private fun decodeBitmap(path: String, requestedWidth: Int, requestedHeight: Int): Bitmap { return BitmapFactory.Options().run { inJustDecodeBounds = true BitmapFactory.decodeFile(path, this) @@ -59,11 +51,7 @@ object BitmapShrinker { } // solution inspired by https://developer.android.com/topic/performance/graphics/load-bitmap - private fun getInSampleSize( - options: BitmapFactory.Options, - requestedWidth: Int, - requestedHeight: Int - ): Int { + private fun getInSampleSize(options: BitmapFactory.Options, requestedWidth: Int, requestedHeight: Int): Int { val (height: Int, width: Int) = options.run { outHeight to outWidth } var inSampleSize = 1 if (height > requestedHeight || width > requestedWidth) { @@ -87,22 +75,26 @@ object BitmapShrinker { ExifInterface.ORIENTATION_ROTATE_90 -> { matrix.postRotate(DEGREES_90) } + ExifInterface.ORIENTATION_ROTATE_180 -> { matrix.postRotate(DEGREES_180) } + ExifInterface.ORIENTATION_ROTATE_270 -> { matrix.postRotate(DEGREES_270) } } - val rotatedBitmap = Bitmap.createBitmap( - bitmap, - 0, - 0, - bitmap.getWidth(), - bitmap.getHeight(), - matrix, - true - ) + val rotatedBitmap = + Bitmap + .createBitmap( + bitmap, + 0, + 0, + bitmap.getWidth(), + bitmap.getHeight(), + matrix, + true + ) return rotatedBitmap } catch (e: IOException) { Log.e(TAG, "error while rotating image", e) diff --git a/app/src/main/java/com/nextcloud/talk/utils/ContactUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/ContactUtils.kt index c7da1abd242..02bf48d6a0c 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ContactUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ContactUtils.kt @@ -12,13 +12,16 @@ object ContactUtils { ContactsContract.CommonDataKinds.StructuredName.CONTACT_ID + " = ?" val whereNameParams = arrayOf(ContactsContract.CommonDataKinds.StructuredName.CONTENT_ITEM_TYPE, id) - val nameCursor = context.contentResolver.query( - ContactsContract.Data.CONTENT_URI, - null, - whereName, - whereNameParams, - ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME - ) + val nameCursor = + context + .contentResolver + .query( + ContactsContract.Data.CONTENT_URI, + null, + whereName, + whereNameParams, + ContactsContract.CommonDataKinds.StructuredName.GIVEN_NAME + ) if (nameCursor != null) { while (nameCursor.moveToNext()) { displayName = diff --git a/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt index 6f94209e2e7..db62160ffbf 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/DateUtils.kt @@ -35,18 +35,25 @@ class DateUtils(val context: Context) { private val cal = Calendar.getInstance() private val tz = cal.timeZone - /* date formatter in local timezone and locale */ - private var format: DateFormat = DateFormat.getDateTimeInstance( - DateFormat.DEFAULT, // dateStyle - DateFormat.SHORT, // timeStyle - context.resources.configuration.locales[0] - ) + // date formatter in local timezone and locale + private var format: DateFormat = + DateFormat + .getDateTimeInstance( + // dateStyle + DateFormat.DEFAULT, + // timeStyle + DateFormat.SHORT, + context.resources.configuration.locales[0] + ) - /* date formatter in local timezone and locale */ - private var formatTime: DateFormat = DateFormat.getTimeInstance( - DateFormat.SHORT, // timeStyle - context.resources.configuration.locales[0] - ) + // date formatter in local timezone and locale + private var formatTime: DateFormat = + DateFormat + .getTimeInstance( + // timeStyle + DateFormat.SHORT, + context.resources.configuration.locales[0] + ) init { format.timeZone = tz diff --git a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java index 4682f50fcfc..c964645e826 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/DisplayUtils.java @@ -89,12 +89,12 @@ import coil.transform.CircleCropTransformation; import third.parties.fresco.BetterImageSpan; -import static com.nextcloud.talk.utils.FileSortOrder.sort_a_to_z_id; -import static com.nextcloud.talk.utils.FileSortOrder.sort_big_to_small_id; -import static com.nextcloud.talk.utils.FileSortOrder.sort_new_to_old_id; -import static com.nextcloud.talk.utils.FileSortOrder.sort_old_to_new_id; -import static com.nextcloud.talk.utils.FileSortOrder.sort_small_to_big_id; -import static com.nextcloud.talk.utils.FileSortOrder.sort_z_to_a_id; +import static com.nextcloud.talk.utils.FileSortOrder.SORT_A_TO_Z_ID; +import static com.nextcloud.talk.utils.FileSortOrder.SORT_BIG_TO_SMALL_ID; +import static com.nextcloud.talk.utils.FileSortOrder.SORT_NEW_TO_OLD_ID; +import static com.nextcloud.talk.utils.FileSortOrder.SORT_OLD_TO_NEW_ID; +import static com.nextcloud.talk.utils.FileSortOrder.SORT_SMALL_TO_BIG_ID; +import static com.nextcloud.talk.utils.FileSortOrder.SORT_Z_TO_A_ID; public class DisplayUtils { private static final String TAG = DisplayUtils.class.getSimpleName(); @@ -476,17 +476,17 @@ public static void loadAvatarImage(User user, ImageView avatarImageView, boolean public static @StringRes int getSortOrderStringId(FileSortOrder sortOrder) { switch (sortOrder.getName()) { - case sort_z_to_a_id: + case SORT_Z_TO_A_ID: return R.string.menu_item_sort_by_name_z_a; - case sort_new_to_old_id: + case SORT_NEW_TO_OLD_ID: return R.string.menu_item_sort_by_date_newest_first; - case sort_old_to_new_id: + case SORT_OLD_TO_NEW_ID: return R.string.menu_item_sort_by_date_oldest_first; - case sort_big_to_small_id: + case SORT_BIG_TO_SMALL_ID: return R.string.menu_item_sort_by_size_biggest_first; - case sort_small_to_big_id: + case SORT_SMALL_TO_BIG_ID: return R.string.menu_item_sort_by_size_smallest_first; - case sort_a_to_z_id: + case SORT_A_TO_Z_ID: default: return R.string.menu_item_sort_by_name_a_z; } diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileSortOrder.kt b/app/src/main/java/com/nextcloud/talk/utils/FileSortOrder.kt index f3bfffb1509..cd82d906c7f 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileSortOrder.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileSortOrder.kt @@ -27,28 +27,29 @@ import java.util.Collections open class FileSortOrder(var name: String, var isAscending: Boolean) { companion object { - const val sort_a_to_z_id = "sort_a_to_z" - const val sort_z_to_a_id = "sort_z_to_a" - const val sort_old_to_new_id = "sort_old_to_new" - const val sort_new_to_old_id = "sort_new_to_old" - const val sort_small_to_big_id = "sort_small_to_big" - const val sort_big_to_small_id = "sort_big_to_small" + const val SORT_A_TO_Z_ID = "sort_a_to_z" + const val SORT_Z_TO_A_ID = "sort_z_to_a" + const val SORT_OLD_TO_NEW_ID = "sort_old_to_new" + const val SORT_NEW_TO_OLD_ID = "sort_new_to_old" + const val SORT_SMALL_TO_BIG_ID = "sort_small_to_big" + const val SORT_BIG_TO_SMALL_ID = "sort_big_to_small" - val sort_a_to_z: FileSortOrder = FileSortOrderByName(sort_a_to_z_id, true) - val sort_z_to_a: FileSortOrder = FileSortOrderByName(sort_z_to_a_id, false) - val sort_old_to_new: FileSortOrder = FileSortOrderByDate(sort_old_to_new_id, true) - val sort_new_to_old: FileSortOrder = FileSortOrderByDate(sort_new_to_old_id, false) - val sort_small_to_big: FileSortOrder = FileSortOrderBySize(sort_small_to_big_id, true) - val sort_big_to_small: FileSortOrder = FileSortOrderBySize(sort_big_to_small_id, false) + val sort_a_to_z: FileSortOrder = FileSortOrderByName(SORT_A_TO_Z_ID, true) + val sort_z_to_a: FileSortOrder = FileSortOrderByName(SORT_Z_TO_A_ID, false) + val sort_old_to_new: FileSortOrder = FileSortOrderByDate(SORT_OLD_TO_NEW_ID, true) + val sort_new_to_old: FileSortOrder = FileSortOrderByDate(SORT_NEW_TO_OLD_ID, false) + val sort_small_to_big: FileSortOrder = FileSortOrderBySize(SORT_SMALL_TO_BIG_ID, true) + val sort_big_to_small: FileSortOrder = FileSortOrderBySize(SORT_BIG_TO_SMALL_ID, false) - val sortOrders: Map = mapOf( - sort_a_to_z.name to sort_a_to_z, - sort_z_to_a.name to sort_z_to_a, - sort_old_to_new.name to sort_old_to_new, - sort_new_to_old.name to sort_new_to_old, - sort_small_to_big.name to sort_small_to_big, - sort_big_to_small.name to sort_big_to_small - ) + val sortOrders: Map = + mapOf( + sort_a_to_z.name to sort_a_to_z, + sort_z_to_a.name to sort_z_to_a, + sort_old_to_new.name to sort_old_to_new, + sort_new_to_old.name to sort_new_to_old, + sort_small_to_big.name to sort_small_to_big, + sort_big_to_small.name to sort_big_to_small + ) fun getFileSortOrder(key: String?): FileSortOrder { return if (TextUtils.isEmpty(key) || !sortOrders.containsKey(key)) { diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileUtils.kt index aeff10873fb..55c60322e54 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileUtils.kt @@ -94,20 +94,21 @@ object FileUtils { val fileName = getFileName(sourceFileUri, context) val scheme = sourceFileUri.scheme - val file = if (scheme == null) { - Log.d(TAG, "relative uri: " + sourceFileUri.path) - throw IllegalArgumentException("relative paths are not supported") - } else if (ContentResolver.SCHEME_CONTENT == scheme) { - copyFileToCache(context, sourceFileUri, fileName) - } else if (ContentResolver.SCHEME_FILE == scheme) { - if (sourceFileUri.path != null) { - sourceFileUri.path?.let { File(it) } + val file = + if (scheme == null) { + Log.d(TAG, "relative uri: " + sourceFileUri.path) + throw IllegalArgumentException("relative paths are not supported") + } else if (ContentResolver.SCHEME_CONTENT == scheme) { + copyFileToCache(context, sourceFileUri, fileName) + } else if (ContentResolver.SCHEME_FILE == scheme) { + if (sourceFileUri.path != null) { + sourceFileUri.path?.let { File(it) } + } else { + throw IllegalArgumentException("uri does not contain path") + } } else { - throw IllegalArgumentException("uri does not contain path") + throw IllegalArgumentException("unsupported scheme: " + sourceFileUri.path) } - } else { - throw IllegalArgumentException("unsupported scheme: " + sourceFileUri.path) - } return file } diff --git a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt index 99c8511b605..5a27e0bc35e 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/FileViewerUtils.kt @@ -72,10 +72,7 @@ import java.util.concurrent.ExecutionException * - SharedItemsViewHolder */ class FileViewerUtils(private val context: Context, private val user: User) { - fun openFile( - message: ChatMessage, - progressUi: ProgressUi - ) { + fun openFile(message: ChatMessage, progressUi: ProgressUi) { val fileName = message.selectedIndividualHashMap!![PreviewMessageViewHolder.KEY_NAME]!! val mimetype = message.selectedIndividualHashMap!![PreviewMessageViewHolder.KEY_MIMETYPE]!! val link = message.selectedIndividualHashMap!!["link"]!! @@ -167,13 +164,16 @@ class FileViewerUtils(private val context: Context, private val user: User) { VIDEO_QUICKTIME, VIDEO_OGG -> openMediaView(filename, mimetype) + IMAGE_PNG, IMAGE_JPEG, IMAGE_GIF -> openImageView(filename, mimetype) + TEXT_MARKDOWN, TEXT_PLAIN -> openTextView(filename, mimetype) + else -> openFileByExternalApp(filename, mimetype) } @@ -207,17 +207,19 @@ class FileViewerUtils(private val context: Context, private val user: User) { } fun openFileInFilesApp(link: String, keyID: String) { - val accountString = user.username + "@" + - user.baseUrl - ?.replace("https://", "") - ?.replace("http://", "") + val accountString = + user.username + "@" + + user.baseUrl + ?.replace("https://", "") + ?.replace("http://", "") if (canWeOpenFilesApp(context, accountString)) { val filesAppIntent = Intent(Intent.ACTION_VIEW, null) - val componentName = ComponentName( - context.getString(R.string.nc_import_accounts_from), - "com.owncloud.android.ui.activity.FileDisplayActivity" - ) + val componentName = + ComponentName( + context.getString(R.string.nc_import_accounts_from), + "com.owncloud.android.ui.activity.FileDisplayActivity" + ) filesAppIntent.component = componentName filesAppIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) filesAppIntent.setPackage(context.getString(R.string.nc_import_accounts_from)) @@ -225,10 +227,11 @@ class FileViewerUtils(private val context: Context, private val user: User) { filesAppIntent.putExtra(KEY_FILE_ID, keyID) context.startActivity(filesAppIntent) } else { - val browserIntent = Intent( - Intent.ACTION_VIEW, - Uri.parse(link) - ) + val browserIntent = + Intent( + Intent.ACTION_VIEW, + Uri.parse(link) + ) browserIntent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK) context.startActivity(browserIntent) } @@ -271,6 +274,7 @@ class FileViewerUtils(private val context: Context, private val user: User) { VIDEO_OGG, TEXT_MARKDOWN, TEXT_PLAIN -> true + else -> false } } @@ -299,28 +303,33 @@ class FileViewerUtils(private val context: Context, private val user: User) { } val downloadWorker: OneTimeWorkRequest - val size: Long = if (fileInfo.fileSize == null) { - -1 - } else { - fileInfo.fileSize!! - } + val size: Long = + if (fileInfo.fileSize == null) { + -1 + } else { + fileInfo.fileSize!! + } - val data: Data = Data.Builder() - .putString(DownloadFileToCacheWorker.KEY_BASE_URL, user.baseUrl) - .putString(DownloadFileToCacheWorker.KEY_USER_ID, user.userId) - .putString( - DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, - CapabilitiesUtilNew.getAttachmentFolder(user) - ) - .putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileInfo.fileName) - .putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path) - .putLong(DownloadFileToCacheWorker.KEY_FILE_SIZE, size) - .build() - - downloadWorker = OneTimeWorkRequest.Builder(DownloadFileToCacheWorker::class.java) - .setInputData(data) - .addTag(fileInfo.fileId) - .build() + val data: Data = + Data + .Builder() + .putString(DownloadFileToCacheWorker.KEY_BASE_URL, user.baseUrl) + .putString(DownloadFileToCacheWorker.KEY_USER_ID, user.userId) + .putString( + DownloadFileToCacheWorker.KEY_ATTACHMENT_FOLDER, + CapabilitiesUtilNew.getAttachmentFolder(user) + ) + .putString(DownloadFileToCacheWorker.KEY_FILE_NAME, fileInfo.fileName) + .putString(DownloadFileToCacheWorker.KEY_FILE_PATH, path) + .putLong(DownloadFileToCacheWorker.KEY_FILE_SIZE, size) + .build() + + downloadWorker = + OneTimeWorkRequest + .Builder(DownloadFileToCacheWorker::class.java) + .setInputData(data) + .addTag(fileInfo.fileId) + .build() WorkManager.getInstance().enqueue(downloadWorker) progressUi.progressBar?.visibility = View.VISIBLE WorkManager.getInstance(context).getWorkInfoByIdLiveData(downloadWorker.id) @@ -346,13 +355,16 @@ class FileViewerUtils(private val context: Context, private val user: User) { WorkInfo.State.RUNNING -> { val progress = workInfo.progress.getInt(DownloadFileToCacheWorker.PROGRESS, -1) if (progress > -1) { - progressUi.messageText?.text = String.format( - context.resources.getString(R.string.filename_progress), - fileName, - progress - ) + progressUi.messageText?.text = + String + .format( + context.resources.getString(R.string.filename_progress), + fileName, + progress + ) } } + WorkInfo.State.SUCCEEDED -> { if (progressUi.previewImage.isShown && openWhenDownloaded) { openFileByMimetype(fileName, mimetype) @@ -367,10 +379,12 @@ class FileViewerUtils(private val context: Context, private val user: User) { progressUi.messageText?.text = fileName progressUi.progressBar?.visibility = View.GONE } + WorkInfo.State.FAILED -> { progressUi.messageText?.text = fileName progressUi.progressBar?.visibility = View.GONE } + else -> { } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/ImageEmojiEditText.kt b/app/src/main/java/com/nextcloud/talk/utils/ImageEmojiEditText.kt index 2fa12d87b14..165c94fa1f4 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ImageEmojiEditText.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ImageEmojiEditText.kt @@ -39,7 +39,6 @@ Implementation based on this example: https://developer.android.com/guide/topics/text/image-keyboard */ class ImageEmojiEditText : EmojiEditText { - // Callback function to be called when the user selects an image, pass image Uri lateinit var onCommitContentListener: ((Uri) -> Unit) diff --git a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt index a5682d9597f..f634f1560b8 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/NotificationUtils.kt @@ -50,7 +50,6 @@ import java.io.IOException @Suppress("TooManyFunctions") object NotificationUtils { - const val TAG = "NotificationUtils" enum class NotificationChannels { @@ -80,17 +79,19 @@ object NotificationUtils { Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && notificationManager.getNotificationChannel(notificationChannel.id) == null ) { - val importance = if (notificationChannel.isImportant) { - NotificationManager.IMPORTANCE_HIGH - } else { - NotificationManager.IMPORTANCE_LOW - } + val importance = + if (notificationChannel.isImportant) { + NotificationManager.IMPORTANCE_HIGH + } else { + NotificationManager.IMPORTANCE_LOW + } - val channel = NotificationChannel( - notificationChannel.id, - notificationChannel.name, - importance - ) + val channel = + NotificationChannel( + notificationChannel.id, + notificationChannel.name, + importance + ) channel.description = notificationChannel.description channel.enableLights(true) @@ -102,10 +103,7 @@ object NotificationUtils { } } - private fun createCallsNotificationChannel( - context: Context, - appPreferences: AppPreferences - ) { + private fun createCallsNotificationChannel(context: Context, appPreferences: AppPreferences) { val audioAttributes = AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) @@ -126,10 +124,7 @@ object NotificationUtils { ) } - private fun createMessagesNotificationChannel( - context: Context, - appPreferences: AppPreferences - ) { + private fun createMessagesNotificationChannel(context: Context, appPreferences: AppPreferences) { val audioAttributes = AudioAttributes.Builder() .setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION) @@ -150,9 +145,7 @@ object NotificationUtils { ) } - private fun createUploadsNotificationChannel( - context: Context - ) { + private fun createUploadsNotificationChannel(context: Context) { createNotificationChannel( context, Channel( @@ -166,10 +159,7 @@ object NotificationUtils { ) } - fun registerNotificationChannels( - context: Context, - appPreferences: AppPreferences - ) { + fun registerNotificationChannels(context: Context, appPreferences: AppPreferences) { createCallsNotificationChannel(context, appPreferences) createMessagesNotificationChannel(context, appPreferences) createUploadsNotificationChannel(context) @@ -197,10 +187,7 @@ object NotificationUtils { } @TargetApi(Build.VERSION_CODES.O) - private fun getNotificationChannel( - context: Context, - channelId: String - ): NotificationChannel? { + private fun getNotificationChannel(context: Context, channelId: String): NotificationChannel? { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) { val notificationManager = context.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager return notificationManager.getNotificationChannel(channelId) @@ -275,10 +262,7 @@ object NotificationUtils { } } - fun isNotificationVisible( - context: Context?, - notificationId: Int - ): Boolean { + fun isNotificationVisible(context: Context?, notificationId: Int): Boolean { var isVisible = false val notificationManager = context!!.getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager @@ -319,10 +303,7 @@ object NotificationUtils { } } - fun getCallRingtoneUri( - context: Context, - appPreferences: AppPreferences - ): Uri? { + fun getCallRingtoneUri(context: Context, appPreferences: AppPreferences): Uri? { return getRingtoneUri( context, appPreferences.callRingtoneUri, @@ -331,10 +312,7 @@ object NotificationUtils { ) } - fun getMessageRingtoneUri( - context: Context, - appPreferences: AppPreferences - ): Uri? { + fun getMessageRingtoneUri(context: Context, appPreferences: AppPreferences): Uri? { return getRingtoneUri( context, appPreferences.messageRingtoneUri, @@ -346,24 +324,26 @@ object NotificationUtils { fun loadAvatarSync(avatarUrl: String, context: Context): IconCompat? { var avatarIcon: IconCompat? = null - val request = ImageRequest.Builder(context) - .data(avatarUrl) - .transformations(CircleCropTransformation()) - .placeholder(R.drawable.account_circle_96dp) - .target( - onSuccess = { result -> - val bitmap = (result as BitmapDrawable).bitmap - avatarIcon = IconCompat.createWithBitmap(bitmap) - }, - onError = { error -> - error?.let { - val bitmap = (error as BitmapDrawable).bitmap + val request = + ImageRequest + .Builder(context) + .data(avatarUrl) + .transformations(CircleCropTransformation()) + .placeholder(R.drawable.account_circle_96dp) + .target( + onSuccess = { result -> + val bitmap = (result as BitmapDrawable).bitmap avatarIcon = IconCompat.createWithBitmap(bitmap) + }, + onError = { error -> + error?.let { + val bitmap = (error as BitmapDrawable).bitmap + avatarIcon = IconCompat.createWithBitmap(bitmap) + } + Log.w(TAG, "Can't load avatar for URL: $avatarUrl") } - Log.w(TAG, "Can't load avatar for URL: $avatarUrl") - } - ) - .build() + ) + .build() context.imageLoader.executeBlocking(request) diff --git a/app/src/main/java/com/nextcloud/talk/utils/ParticipantPermissions.kt b/app/src/main/java/com/nextcloud/talk/utils/ParticipantPermissions.kt index dddb8753e5a..cfb066454f9 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ParticipantPermissions.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ParticipantPermissions.kt @@ -34,7 +34,6 @@ class ParticipantPermissions( private val user: User, private val conversation: ConversationModel ) { - @Deprecated("Use ChatRepository.ConversationModel") constructor(user: User, conversation: Conversation) : this( user, @@ -99,7 +98,6 @@ class ParticipantPermissions( } companion object { - val TAG = ParticipantPermissions::class.simpleName const val DEFAULT = 0 const val CUSTOM = 1 diff --git a/app/src/main/java/com/nextcloud/talk/utils/PickImage.kt b/app/src/main/java/com/nextcloud/talk/utils/PickImage.kt index 1900791f0f4..c71bfd85497 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/PickImage.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/PickImage.kt @@ -106,21 +106,26 @@ class PickImage( } private fun handleAvatar(remotePath: String?) { - val uri = currentUser!!.baseUrl + "/index.php/apps/files/api/v1/thumbnail/512/512/" + - Uri.encode(remotePath, "/") - val downloadCall = ncApi.downloadResizedImage( - ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), - uri - ) - downloadCall.enqueue(object : Callback { - override fun onResponse(call: Call, response: Response) { - saveBitmapAndPassToImagePicker(BitmapFactory.decodeStream(response.body()!!.byteStream())) - } + val uri = + currentUser!!.baseUrl + "/index.php/apps/files/api/v1/thumbnail/512/512/" + + Uri.encode(remotePath, "/") + val downloadCall = + ncApi + .downloadResizedImage( + ApiUtils.getCredentials(currentUser!!.username, currentUser!!.token), + uri + ) + downloadCall.enqueue( + object : Callback { + override fun onResponse(call: Call, response: Response) { + saveBitmapAndPassToImagePicker(BitmapFactory.decodeStream(response.body()!!.byteStream())) + } - override fun onFailure(call: Call, t: Throwable) { - // unused atm + override fun onFailure(call: Call, t: Throwable) { + // unused atm + } } - }) + ) } // only possible with API26 @@ -172,17 +177,20 @@ class PickImage( val uri: Uri = data?.data!! handleImage(uri) } + REQUEST_CODE_SELECT_REMOTE_FILES -> { val pathList = data?.getStringArrayListExtra(RemoteFileBrowserActivity.EXTRA_SELECTED_PATHS) if (pathList?.size!! >= 1) { handleAvatar(pathList[0]) } } + REQUEST_CODE_TAKE_PICTURE -> { data?.data?.path?.let { selectLocal(File(it)) } } + else -> { Log.w(TAG, "Unknown intent request code") } diff --git a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt index 5e7dbe4e1a7..e2464a9006a 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/PushUtils.kt @@ -82,13 +82,16 @@ class PushUtils { init { sharedApplication!!.componentApplication.inject(this) - val keyPath = sharedApplication!! - .getDir("PushKeystore", Context.MODE_PRIVATE) - .absolutePath + val keyPath = + sharedApplication!! + .getDir("PushKeystore", Context.MODE_PRIVATE) + .absolutePath publicKeyFile = File(keyPath, "push_key.pub") privateKeyFile = File(keyPath, "push_key.priv") - proxyServer = sharedApplication!! - .resources.getString(R.string.nc_push_server_url) + proxyServer = + sharedApplication!! + .resources + .getString(R.string.nc_push_server_url) } fun verifySignature(signatureBytes: ByteArray?, subjectBytes: ByteArray?): SignatureVerification { @@ -101,10 +104,11 @@ class PushUtils { var publicKey: PublicKey? for (user in users) { if (user.pushConfigurationState != null) { - publicKey = readKeyFromString( - true, - user.pushConfigurationState!!.userPublicKey - ) as PublicKey? + publicKey = + readKeyFromString( + true, + user.pushConfigurationState!!.userPublicKey + ) as PublicKey? signature.initVerify(publicKey) signature.update(subjectBytes) if (signature.verify(signatureBytes)) { @@ -237,58 +241,62 @@ class PushUtils { ApiUtils.getUrlNextcloudPush(user.baseUrl), nextcloudRegisterPushMap ) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(pushRegistrationOverall: PushRegistrationOverall) { - Log.d(TAG, "pushTokenHash successfully registered at nextcloud server.") - val proxyMap: MutableMap = HashMap() - proxyMap["pushToken"] = token - proxyMap["deviceIdentifier"] = pushRegistrationOverall.ocs!!.data!!.deviceIdentifier - proxyMap["deviceIdentifierSignature"] = pushRegistrationOverall.ocs!!.data!!.signature - proxyMap["userPublicKey"] = pushRegistrationOverall.ocs!!.data!!.publicKey - registerDeviceWithPushProxy(ncApi, proxyMap, user) - } + override fun onNext(pushRegistrationOverall: PushRegistrationOverall) { + Log.d(TAG, "pushTokenHash successfully registered at nextcloud server.") + val proxyMap: MutableMap = HashMap() + proxyMap["pushToken"] = token + proxyMap["deviceIdentifier"] = pushRegistrationOverall.ocs!!.data!!.deviceIdentifier + proxyMap["deviceIdentifierSignature"] = pushRegistrationOverall.ocs!!.data!!.signature + proxyMap["userPublicKey"] = pushRegistrationOverall.ocs!!.data!!.publicKey + registerDeviceWithPushProxy(ncApi, proxyMap, user) + } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to register device with nextcloud", e) - eventBus!!.post(EventStatus(user.id!!, EventStatus.EventType.PUSH_REGISTRATION, false)) - } + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to register device with nextcloud", e) + eventBus!!.post(EventStatus(user.id!!, EventStatus.EventType.PUSH_REGISTRATION, false)) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } private fun registerDeviceWithPushProxy(ncApi: NcApi, proxyMap: Map, user: User) { ncApi.registerDeviceForNotificationsWithPushProxy(ApiUtils.getUrlPushProxy(), proxyMap) .subscribeOn(Schedulers.io()) - .subscribe(object : Observer { - override fun onSubscribe(d: Disposable) { - // unused atm - } + .subscribe( + object : Observer { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onNext(t: Unit) { - try { - Log.d(TAG, "pushToken successfully registered at pushproxy.") - updatePushStateForUser(proxyMap, user) - } catch (e: IOException) { - Log.e(TAG, "IOException while updating user", e) + override fun onNext(t: Unit) { + try { + Log.d(TAG, "pushToken successfully registered at pushproxy.") + updatePushStateForUser(proxyMap, user) + } catch (e: IOException) { + Log.e(TAG, "IOException while updating user", e) + } } - } - override fun onError(e: Throwable) { - Log.e(TAG, "Failed to register device with pushproxy", e) - eventBus!!.post(EventStatus(user.id!!, EventStatus.EventType.PUSH_REGISTRATION, false)) - } + override fun onError(e: Throwable) { + Log.e(TAG, "Failed to register device with pushproxy", e) + eventBus!!.post(EventStatus(user.id!!, EventStatus.EventType.PUSH_REGISTRATION, false)) + } - override fun onComplete() { - // unused atm + override fun onComplete() { + // unused atm + } } - }) + ) } @Throws(IOException::class) @@ -300,32 +308,34 @@ class PushUtils { pushConfigurationState.userPublicKey = proxyMap["userPublicKey"] pushConfigurationState.usesRegularPass = java.lang.Boolean.FALSE if (user.id != null) { - userManager!!.updatePushState(user.id!!, pushConfigurationState).subscribe(object : SingleObserver { - override fun onSubscribe(d: Disposable) { - // unused atm - } + userManager!!.updatePushState(user.id!!, pushConfigurationState).subscribe( + object : SingleObserver { + override fun onSubscribe(d: Disposable) { + // unused atm + } - override fun onSuccess(integer: Int) { - eventBus!!.post( - EventStatus( - getIdForUser(user), - EventStatus.EventType.PUSH_REGISTRATION, - true + override fun onSuccess(integer: Int) { + eventBus!!.post( + EventStatus( + getIdForUser(user), + EventStatus.EventType.PUSH_REGISTRATION, + true + ) ) - ) - } + } - override fun onError(e: Throwable) { - Log.e(TAG, "update push state for user failed", e) - eventBus!!.post( - EventStatus( - getIdForUser(user), - EventStatus.EventType.PUSH_REGISTRATION, - false + override fun onError(e: Throwable) { + Log.e(TAG, "update push state for user failed", e) + eventBus!!.post( + EventStatus( + getIdForUser(user), + EventStatus.EventType.PUSH_REGISTRATION, + false + ) ) - ) + } } - }) + ) } else { Log.e(TAG, "failed to update updatePushStateForUser. user.getId() was null") } @@ -333,17 +343,18 @@ class PushUtils { private fun readKeyFromString(readPublicKey: Boolean, keyString: String?): Key? { var keyString = keyString - keyString = if (readPublicKey) { - keyString!!.replace("\\n".toRegex(), "").replace( - "-----BEGIN PUBLIC KEY-----", - "" - ).replace("-----END PUBLIC KEY-----", "") - } else { - keyString!!.replace("\\n".toRegex(), "").replace( - "-----BEGIN PRIVATE KEY-----", - "" - ).replace("-----END PRIVATE KEY-----", "") - } + keyString = + if (readPublicKey) { + keyString!!.replace("\\n".toRegex(), "").replace( + "-----BEGIN PUBLIC KEY-----", + "" + ).replace("-----END PUBLIC KEY-----", "") + } else { + keyString!!.replace("\\n".toRegex(), "").replace( + "-----BEGIN PRIVATE KEY-----", + "" + ).replace("-----END PRIVATE KEY-----", "") + } var keyFactory: KeyFactory? = null try { keyFactory = KeyFactory.getInstance("RSA") @@ -364,11 +375,12 @@ class PushUtils { fun readKeyFromFile(readPublicKey: Boolean): Key? { val path: String - path = if (readPublicKey) { - publicKeyFile.absolutePath - } else { - privateKeyFile.absolutePath - } + path = + if (readPublicKey) { + publicKeyFile.absolutePath + } else { + privateKeyFile.absolutePath + } try { FileInputStream(path).use { fileInputStream -> val bytes = ByteArray(fileInputStream.available()) diff --git a/app/src/main/java/com/nextcloud/talk/utils/RemoteFileUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/RemoteFileUtils.kt index 4786997e445..f66062fac10 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/RemoteFileUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/RemoteFileUtils.kt @@ -31,33 +31,27 @@ import io.reactivex.schedulers.Schedulers object RemoteFileUtils { private val TAG = RemoteFileUtils::class.java.simpleName - fun getNewPathIfFileExists( - ncApi: NcApi, - currentUser: User, - remotePath: String - ): String { + fun getNewPathIfFileExists(ncApi: NcApi, currentUser: User, remotePath: String): String { var finalPath = remotePath - val fileExists = doesFileExist( - ncApi, - currentUser, - remotePath - ).blockingFirst() - - if (fileExists) { - finalPath = getFileNameWithoutCollision( + val fileExists = + doesFileExist( ncApi, currentUser, remotePath - ) + ).blockingFirst() + + if (fileExists) { + finalPath = + getFileNameWithoutCollision( + ncApi, + currentUser, + remotePath + ) } return finalPath } - private fun doesFileExist( - ncApi: NcApi, - currentUser: User, - remotePath: String - ): Observable { + private fun doesFileExist(ncApi: NcApi, currentUser: User, remotePath: String): Observable { return ncApi.checkIfFileExists( ApiUtils.getCredentials(currentUser.username, currentUser.token), ApiUtils.getUrlForFileUpload( @@ -72,11 +66,7 @@ object RemoteFileUtils { } } - private fun getFileNameWithoutCollision( - ncApi: NcApi, - currentUser: User, - remotePath: String - ): String { + private fun getFileNameWithoutCollision(ncApi: NcApi, currentUser: User, remotePath: String): String { val extPos = remotePath.lastIndexOf('.') var suffix: String var extension = "" diff --git a/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.kt index 3aea9377225..41c64487835 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ShareUtils.kt @@ -25,11 +25,7 @@ import com.nextcloud.talk.data.user.model.User import com.nextcloud.talk.models.json.conversations.Conversation object ShareUtils { - fun getStringForIntent( - context: Context, - user: User, - conversation: Conversation? - ): String { + fun getStringForIntent(context: Context, user: User, conversation: Conversation?): String { return String.format( context.resources.getString(R.string.nc_share_text), user.baseUrl, diff --git a/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt index e4d7cfaaf3b..039ba4b8206 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/UriUtils.kt @@ -49,8 +49,7 @@ class UriUtils { fun isInstanceInternalFileUrl(baseUrl: String, url: String): Boolean { // https://cloud.nextcloud.com/apps/files/?dir=/Engineering&fileid=41 return ( - url.startsWith("$baseUrl/apps/files/") || - url.startsWith("$baseUrl/index.php/apps/files/") + url.startsWith("$baseUrl/apps/files/") || url.startsWith("$baseUrl/index.php/apps/files/") ) && Uri.parse(url).queryParameterNames.contains("fileid") && Regex(""".*fileid=\d*""").matches(url) diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/user/CurrentUserProviderImpl.kt b/app/src/main/java/com/nextcloud/talk/utils/database/user/CurrentUserProviderImpl.kt index 21aaa887502..75a425cacb0 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/database/user/CurrentUserProviderImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/database/user/CurrentUserProviderImpl.kt @@ -30,8 +30,9 @@ import javax.inject.Inject /** * Listens to changes in the database and provides the current user without needing to query the database everytime. */ -class CurrentUserProviderImpl @Inject constructor(private val userManager: UserManager) : CurrentUserProviderNew { - +class CurrentUserProviderImpl +@Inject +constructor(private val userManager: UserManager) : CurrentUserProviderNew { private var _currentUser: User? = null // synchronized to avoid multiple observers initialized from different threads @@ -45,10 +46,12 @@ class CurrentUserProviderImpl @Inject constructor(private val userManager: UserM // immediately get a result synchronously _currentUser = userManager.currentUser.blockingGet() if (currentUserObserver == null) { - currentUserObserver = userManager.currentUserObservable - .subscribe { - _currentUser = it - } + currentUserObserver = + userManager + .currentUserObservable + .subscribe { + _currentUser = it + } } } return _currentUser?.let { Maybe.just(it) } ?: Maybe.empty() diff --git a/app/src/main/java/com/nextcloud/talk/utils/message/MessageUtils.kt b/app/src/main/java/com/nextcloud/talk/utils/message/MessageUtils.kt index 786d9f7f4a8..c32b3a3ff2d 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/message/MessageUtils.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/message/MessageUtils.kt @@ -95,14 +95,15 @@ class MessageUtils(val context: Context) { var processedMessageText = spannedText val messageParameters = message.messageParameters if (messageParameters != null && messageParameters.size > 0) { - processedMessageText = processMessageParameters( - themingContext, - viewThemeUtils, - messageParameters, - message, - processedMessageText, - itemView - ) + processedMessageText = + processMessageParameters( + themingContext, + viewThemeUtils, + messageParameters, + message, + processedMessageText, + itemView + ) } return processedMessageText } @@ -122,22 +123,25 @@ class MessageUtils(val context: Context) { if (individualHashMap != null) { when (individualHashMap["type"]) { "user", "guest", "call", "user-group" -> { - val chip = if (individualHashMap["id"] == message.activeUser!!.userId) { - R.xml.chip_you - } else { - R.xml.chip_others - } - messageStringInternal = DisplayUtils.searchAndReplaceWithMentionSpan( - key, - themingContext, - messageStringInternal, - individualHashMap["id"]!!, - individualHashMap["name"]!!, - individualHashMap["type"]!!, - message.activeUser!!, - chip, - viewThemeUtils - ) + val chip = + if (individualHashMap["id"] == message.activeUser!!.userId) { + R.xml.chip_you + } else { + R.xml.chip_others + } + messageStringInternal = + DisplayUtils + .searchAndReplaceWithMentionSpan( + key, + themingContext, + messageStringInternal, + individualHashMap["id"]!!, + individualHashMap["name"]!!, + individualHashMap["type"]!!, + message.activeUser!!, + chip, + viewThemeUtils + ) } "file" -> { @@ -155,19 +159,24 @@ class MessageUtils(val context: Context) { fun getRenderedMarkdownText(context: Context, markdown: String, textColor: Int): Spanned { val drawable = TaskListDrawable(textColor, textColor, context.getColor(R.color.bg_default)) - val markwon = Markwon.builder(context).usePlugin(object : AbstractMarkwonPlugin() { - override fun configureTheme(builder: MarkwonTheme.Builder) { - builder.isLinkUnderlined(true).headingBreakHeight(0) - } + val markwon = + Markwon + .builder(context) + .usePlugin( + object : AbstractMarkwonPlugin() { + override fun configureTheme(builder: MarkwonTheme.Builder) { + builder.isLinkUnderlined(true).headingBreakHeight(0) + } - override fun configureConfiguration(builder: MarkwonConfiguration.Builder) { - builder.linkResolver { view: View?, link: String? -> - Log.i(TAG, "Link action not implemented $view / $link") - } - } - }) - .usePlugin(TaskListPlugin.create(drawable)) - .usePlugin(StrikethroughPlugin.create()).build() + override fun configureConfiguration(builder: MarkwonConfiguration.Builder) { + builder.linkResolver { view: View?, link: String? -> + Log.i(TAG, "Link action not implemented $view / $link") + } + } + } + ) + .usePlugin(TaskListPlugin.create(drawable)) + .usePlugin(StrikethroughPlugin.create()).build() return markwon.toMarkdown(markdown) } diff --git a/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtil.kt b/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtil.kt index 51085108aff..963ba087b72 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtil.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/permissions/PlatformPermissionUtil.kt @@ -23,9 +23,14 @@ package com.nextcloud.talk.utils.permissions interface PlatformPermissionUtil { val privateBroadcastPermission: String + fun isCameraPermissionGranted(): Boolean + fun isMicrophonePermissionGranted(): Boolean + fun isBluetoothPermissionGranted(): Boolean + fun isFilesPermissionGranted(): Boolean + fun isPostNotificationsPermissionGranted(): Boolean } diff --git a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt index a1d893c27e7..25776efaf7d 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/preferences/AppPreferencesImpl.kt @@ -45,13 +45,14 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { }.getCompleted() } - override fun setProxyType(proxyType: String?) = runBlocking { - async { - if (proxyType != null) { - writeString(PROXY_TYPE, proxyType) + override fun setProxyType(proxyType: String?) = + runBlocking { + async { + if (proxyType != null) { + writeString(PROXY_TYPE, proxyType) + } } } - } override fun removeProxyType() { proxyType = "" @@ -61,13 +62,14 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readString(PROXY_HOST).first() } }.getCompleted() } - override fun setProxyHost(proxyHost: String?) = runBlocking { - async { - if (proxyHost != null) { - writeString(PROXY_HOST, proxyHost) + override fun setProxyHost(proxyHost: String?) = + runBlocking { + async { + if (proxyHost != null) { + writeString(PROXY_HOST, proxyHost) + } } } - } override fun removeProxyHost() { proxyHost = "" @@ -77,13 +79,14 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readString(PROXY_PORT).first() } }.getCompleted() } - override fun setProxyPort(proxyPort: String?) = runBlocking { - async { - if (proxyPort != null) { - writeString(PROXY_PORT, proxyPort) + override fun setProxyPort(proxyPort: String?) = + runBlocking { + async { + if (proxyPort != null) { + writeString(PROXY_PORT, proxyPort) + } } } - } override fun removeProxyPort() { proxyPort = "" @@ -93,11 +96,12 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readBoolean(PROXY_CRED).first() } }.getCompleted() } - override fun setProxyNeedsCredentials(proxyNeedsCredentials: Boolean) = runBlocking { - async { - writeBoolean(PROXY_CRED, proxyNeedsCredentials) + override fun setProxyNeedsCredentials(proxyNeedsCredentials: Boolean) = + runBlocking { + async { + writeBoolean(PROXY_CRED, proxyNeedsCredentials) + } } - } override fun removeProxyCredentials() { setProxyNeedsCredentials(false) @@ -107,13 +111,14 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readString(PROXY_USERNAME).first() } }.getCompleted() } - override fun setProxyUsername(proxyUsername: String?) = runBlocking { - async { - if (proxyUsername != null) { - writeString(PROXY_USERNAME, proxyUsername) + override fun setProxyUsername(proxyUsername: String?) = + runBlocking { + async { + if (proxyUsername != null) { + writeString(PROXY_USERNAME, proxyUsername) + } } } - } override fun removeProxyUsername() { proxyUsername = "" @@ -123,13 +128,14 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readString(PROXY_PASSWORD).first() } }.getCompleted() } - override fun setProxyPassword(proxyPassword: String?) = runBlocking { - async { - if (proxyPassword != null) { - writeString(PROXY_PASSWORD, proxyPassword) + override fun setProxyPassword(proxyPassword: String?) = + runBlocking { + async { + if (proxyPassword != null) { + writeString(PROXY_PASSWORD, proxyPassword) + } } } - } override fun removeProxyPassword() { proxyPassword = "" @@ -139,13 +145,14 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readString(PUSH_TOKEN).first() } }.getCompleted() } - override fun setPushToken(pushToken: String?) = runBlocking { - async { - if (pushToken != null) { - writeString(PUSH_TOKEN, pushToken) + override fun setPushToken(pushToken: String?) = + runBlocking { + async { + if (pushToken != null) { + writeString(PUSH_TOKEN, pushToken) + } } } - } override fun removePushToken() { pushToken = "" @@ -155,13 +162,14 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readString(TEMP_CLIENT_CERT_ALIAS).first() } }.getCompleted() } - override fun setTemporaryClientCertAlias(alias: String?) = runBlocking { - async { - if (alias != null) { - writeString(TEMP_CLIENT_CERT_ALIAS, alias) + override fun setTemporaryClientCertAlias(alias: String?) = + runBlocking { + async { + if (alias != null) { + writeString(TEMP_CLIENT_CERT_ALIAS, alias) + } } } - } override fun removeTemporaryClientCertAlias() { temporaryClientCertAlias = "" @@ -171,11 +179,12 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readBoolean(PUSH_TO_TALK_INTRO_SHOWN).first() } }.getCompleted() } - override fun setPushToTalkIntroShown(shown: Boolean) = runBlocking { - async { - writeBoolean(PUSH_TO_TALK_INTRO_SHOWN, shown) + override fun setPushToTalkIntroShown(shown: Boolean) = + runBlocking { + async { + writeBoolean(PUSH_TO_TALK_INTRO_SHOWN, shown) + } } - } override fun removePushToTalkIntroShown() { pushToTalkIntroShown = false @@ -185,13 +194,14 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readString(CALL_RINGTONE).first() } }.getCompleted() } - override fun setCallRingtoneUri(value: String?) = runBlocking { - async { - if (value != null) { - writeString(CALL_RINGTONE, value) + override fun setCallRingtoneUri(value: String?) = + runBlocking { + async { + if (value != null) { + writeString(CALL_RINGTONE, value) + } } } - } override fun removeCallRingtoneUri() { callRingtoneUri = "" @@ -201,13 +211,14 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readString(MESSAGE_RINGTONE).first() } }.getCompleted() } - override fun setMessageRingtoneUri(value: String?) = runBlocking { - async { - if (value != null) { - writeString(MESSAGE_RINGTONE, value) + override fun setMessageRingtoneUri(value: String?) = + runBlocking { + async { + if (value != null) { + writeString(MESSAGE_RINGTONE, value) + } } } - } override fun removeMessageRingtoneUri() { messageRingtoneUri = "" @@ -217,11 +228,12 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readBoolean(NOTIFY_UPGRADE_V2).first() } }.getCompleted() } - override fun setNotificationChannelIsUpgradedToV2(value: Boolean) = runBlocking { - async { - writeBoolean(NOTIFY_UPGRADE_V2, value) + override fun setNotificationChannelIsUpgradedToV2(value: Boolean) = + runBlocking { + async { + writeBoolean(NOTIFY_UPGRADE_V2, value) + } } - } override fun removeNotificationChannelUpgradeToV2() { setNotificationChannelIsUpgradedToV2(false) @@ -231,11 +243,12 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readBoolean(NOTIFY_UPGRADE_V3).first() } }.getCompleted() } - override fun setNotificationChannelIsUpgradedToV3(value: Boolean) = runBlocking { - async { - writeBoolean(NOTIFY_UPGRADE_V3, value) + override fun setNotificationChannelIsUpgradedToV3(value: Boolean) = + runBlocking { + async { + writeBoolean(NOTIFY_UPGRADE_V3, value) + } } - } override fun removeNotificationChannelUpgradeToV3() { setNotificationChannelIsUpgradedToV3(false) @@ -245,11 +258,12 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readBoolean(SCREEN_SECURITY).first() } }.getCompleted() } - override fun setScreenSecurity(value: Boolean) = runBlocking { - async { - writeBoolean(SCREEN_SECURITY, value) + override fun setScreenSecurity(value: Boolean) = + runBlocking { + async { + writeBoolean(SCREEN_SECURITY, value) + } } - } override fun removeScreenSecurity() { setScreenSecurity(false) @@ -259,11 +273,12 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readBoolean(SCREEN_LOCK).first() } }.getCompleted() } - override fun setScreenLock(value: Boolean) = runBlocking { - async { - writeBoolean(SCREEN_LOCK, value) + override fun setScreenLock(value: Boolean) = + runBlocking { + async { + writeBoolean(SCREEN_LOCK, value) + } } - } override fun removeScreenLock() { setScreenLock(false) @@ -274,11 +289,12 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return read } - override fun setIncognitoKeyboard(value: Boolean) = runBlocking { - async { - writeBoolean(INCOGNITO_KEYBOARD, value) + override fun setIncognitoKeyboard(value: Boolean) = + runBlocking { + async { + writeBoolean(INCOGNITO_KEYBOARD, value) + } } - } override fun removeIncognitoKeyboard() { setIncognitoKeyboard(false) @@ -288,17 +304,19 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return runBlocking { async { readBoolean(PHONE_BOOK_INTEGRATION).first() } }.getCompleted() } - override fun setPhoneBookIntegration(value: Boolean) = runBlocking { - async { - writeBoolean(PHONE_BOOK_INTEGRATION, value) + override fun setPhoneBookIntegration(value: Boolean) = + runBlocking { + async { + writeBoolean(PHONE_BOOK_INTEGRATION, value) + } } - } - override fun removeLinkPreviews() = runBlocking { - async { - writeBoolean(LINK_PREVIEWS, false) + override fun removeLinkPreviews() = + runBlocking { + async { + writeBoolean(LINK_PREVIEWS, false) + } } - } override fun getScreenLockTimeout(): String { val default = context.resources.getString(R.string.nc_screen_lock_timeout_sixty) @@ -306,13 +324,14 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return read.ifEmpty { default } } - override fun setScreenLockTimeout(value: String?) = runBlocking { - async { - if (value != null) { - writeString(SCREEN_LOCK_TIMEOUT, value) + override fun setScreenLockTimeout(value: String?) = + runBlocking { + async { + if (value != null) { + writeString(SCREEN_LOCK_TIMEOUT, value) + } } } - } override fun removeScreenLockTimeout() { screenLockTimeout = "" @@ -325,14 +344,15 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return read.ifEmpty { default } } - override fun setTheme(value: String?) = runBlocking { - async { - if (value != null) { - val key = context.resources.getString(R.string.nc_settings_theme_key) - writeString(key, value) + override fun setTheme(value: String?) = + runBlocking { + async { + if (value != null) { + val key = context.resources.getString(R.string.nc_settings_theme_key) + writeString(key, value) + } } } - } override fun removeTheme() { theme = "" @@ -343,68 +363,75 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return read } - override fun setDbCypherToUpgrade(value: Boolean) = runBlocking { - async { - writeBoolean(DB_CYPHER_V4_UPGRADE, value) + override fun setDbCypherToUpgrade(value: Boolean) = + runBlocking { + async { + writeBoolean(DB_CYPHER_V4_UPGRADE, value) + } } - } override fun getIsDbRoomMigrated(): Boolean { return runBlocking { async { readBoolean(DB_ROOM_MIGRATED).first() } }.getCompleted() } - override fun setIsDbRoomMigrated(value: Boolean) = runBlocking { - async { - writeBoolean(DB_ROOM_MIGRATED, value) + override fun setIsDbRoomMigrated(value: Boolean) = + runBlocking { + async { + writeBoolean(DB_ROOM_MIGRATED, value) + } } - } - override fun setPhoneBookIntegrationLastRun(currentTimeMillis: Long) = runBlocking { - async { - writeLong(PHONE_BOOK_INTEGRATION_LAST_RUN, currentTimeMillis) + override fun setPhoneBookIntegrationLastRun(currentTimeMillis: Long) = + runBlocking { + async { + writeLong(PHONE_BOOK_INTEGRATION_LAST_RUN, currentTimeMillis) + } } - } override fun getPhoneBookIntegrationLastRun(defaultValue: Long?): Long { - val result = if (defaultValue != null) { - runBlocking { async { readLong(PHONE_BOOK_INTEGRATION_LAST_RUN, defaultValue = defaultValue).first() } } - .getCompleted() - } else { - runBlocking { async { readLong(PHONE_BOOK_INTEGRATION_LAST_RUN).first() } }.getCompleted() - } + val result = + if (defaultValue != null) { + runBlocking { async { readLong(PHONE_BOOK_INTEGRATION_LAST_RUN, defaultValue = defaultValue).first() } } + .getCompleted() + } else { + runBlocking { async { readLong(PHONE_BOOK_INTEGRATION_LAST_RUN).first() } }.getCompleted() + } return result } - override fun setReadPrivacy(value: Boolean) = runBlocking { - val key = context.resources.getString(R.string.nc_settings_read_privacy_key) - async { - writeBoolean(key, value) + override fun setReadPrivacy(value: Boolean) = + runBlocking { + val key = context.resources.getString(R.string.nc_settings_read_privacy_key) + async { + writeBoolean(key, value) + } } - } override fun getReadPrivacy(): Boolean { val key = context.resources.getString(R.string.nc_settings_read_privacy_key) return runBlocking { async { readBoolean(key).first() } }.getCompleted() } - override fun setTypingStatus(value: Boolean) = runBlocking { - async { - writeBoolean(TYPING_STATUS, value) + override fun setTypingStatus(value: Boolean) = + runBlocking { + async { + writeBoolean(TYPING_STATUS, value) + } } - } override fun getTypingStatus(): Boolean { return runBlocking { async { readBoolean(TYPING_STATUS).first() } }.getCompleted() } - override fun setSorting(value: String?) = runBlocking { - val key = context.resources.getString(R.string.nc_file_browser_sort_by_key) - async { - if (value != null) { - writeString(key, value) + override fun setSorting(value: String?) = + runBlocking { + val key = context.resources.getString(R.string.nc_file_browser_sort_by_key) + async { + if (value != null) { + writeString(key, value) + } } } - } override fun getSorting(): String { val key = context.resources.getString(R.string.nc_file_browser_sort_by_key) @@ -413,11 +440,12 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { return read.ifEmpty { default } } - override fun saveWaveFormForFile(filename: String, array: Array) = runBlocking { - async { - writeString(filename, array.contentToString()) + override fun saveWaveFormForFile(filename: String, array: Array) = + runBlocking { + async { + writeString(filename, array.contentToString()) + } } - } override fun getWaveFormFromFile(filename: String): Array { val string = runBlocking { async { readString(filename).first() } }.getCompleted() @@ -426,29 +454,32 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { override fun clear() {} - private suspend fun writeString(key: String, value: String) = context.dataStore.edit { settings -> - settings[ - stringPreferencesKey( - key - ) - ] = value - } + private suspend fun writeString(key: String, value: String) = + context.dataStore.edit { settings -> + settings[ + stringPreferencesKey( + key + ) + ] = value + } /** * Returns a Flow of type String * @param key the key of the persisted data to be observed */ - fun readString(key: String, defaultValue: String = ""): Flow = context.dataStore.data.map { preferences -> - preferences[stringPreferencesKey(key)] ?: defaultValue - } + fun readString(key: String, defaultValue: String = ""): Flow = + context.dataStore.data.map { preferences -> + preferences[stringPreferencesKey(key)] ?: defaultValue + } - private suspend fun writeBoolean(key: String, value: Boolean) = context.dataStore.edit { settings -> - settings[ - booleanPreferencesKey( - key - ) - ] = value - } + private suspend fun writeBoolean(key: String, value: Boolean) = + context.dataStore.edit { settings -> + settings[ + booleanPreferencesKey( + key + ) + ] = value + } /** * Returns a Flow of type Boolean @@ -459,17 +490,19 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { preferences[booleanPreferencesKey(key)] ?: defaultValue } - private suspend fun writeLong(key: String, value: Long) = context.dataStore.edit { settings -> - settings[ - longPreferencesKey( - key - ) - ] = value - } + private suspend fun writeLong(key: String, value: Long) = + context.dataStore.edit { settings -> + settings[ + longPreferencesKey( + key + ) + ] = value + } - private fun readLong(key: String, defaultValue: Long = 0): Flow = context.dataStore.data.map { preferences -> - preferences[longPreferencesKey(key)] ?: defaultValue - } + private fun readLong(key: String, defaultValue: Long = 0): Flow = + context.dataStore.data.map { preferences -> + preferences[longPreferencesKey(key)] ?: defaultValue + } companion object { @Suppress("UnusedPrivateProperty") @@ -499,6 +532,7 @@ class AppPreferencesImpl(val context: Context) : AppPreferences { const val DB_ROOM_MIGRATED = "db_room_migrated" const val PHONE_BOOK_INTEGRATION_LAST_RUN = "phone_book_integration_last_run" const val TYPING_STATUS = "typing_status" + private fun String.convertStringToArray(): Array { var varString = this val floatList = mutableListOf() diff --git a/app/src/main/java/com/nextcloud/talk/utils/rx/SearchViewObservable.kt b/app/src/main/java/com/nextcloud/talk/utils/rx/SearchViewObservable.kt index 2d84fbffa80..f068de6fc6c 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/rx/SearchViewObservable.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/rx/SearchViewObservable.kt @@ -30,17 +30,19 @@ class SearchViewObservable { @JvmStatic fun observeSearchView(searchView: SearchView): Observable { val subject: PublishSubject = PublishSubject.create() - searchView.setOnQueryTextListener(object : SearchView.OnQueryTextListener { - override fun onQueryTextSubmit(query: String): Boolean { - subject.onComplete() - return true - } + searchView.setOnQueryTextListener( + object : SearchView.OnQueryTextListener { + override fun onQueryTextSubmit(query: String): Boolean { + subject.onComplete() + return true + } - override fun onQueryTextChange(newText: String): Boolean { - subject.onNext(newText) - return true + override fun onQueryTextChange(newText: String): Boolean { + subject.onNext(newText) + return true + } } - }) + ) return subject } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt b/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt index e4a2a845cee..4f8a5be431b 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/ssl/SSLSocketFactoryCompat.kt @@ -54,11 +54,9 @@ class SSLSocketFactoryCompat( } } - override fun getDefaultCipherSuites(): Array? = cipherSuites - ?: delegate.defaultCipherSuites + override fun getDefaultCipherSuites(): Array? = cipherSuites ?: delegate.defaultCipherSuites - override fun getSupportedCipherSuites(): Array? = cipherSuites - ?: delegate.supportedCipherSuites + override fun getSupportedCipherSuites(): Array? = cipherSuites ?: delegate.supportedCipherSuites override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket { val ssl = delegate.createSocket(s, host, port, autoClose) diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/CallRecordingViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/CallRecordingViewModel.kt index 3cff3d02b55..7d81aa4e79f 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/CallRecordingViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/CallRecordingViewModel.kt @@ -34,20 +34,26 @@ import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject -class CallRecordingViewModel @Inject constructor(private val repository: CallRecordingRepository) : ViewModel() { - +class CallRecordingViewModel +@Inject +constructor(private val repository: CallRecordingRepository) : ViewModel() { @Inject lateinit var userManager: UserManager lateinit var roomToken: String sealed interface ViewState + open class RecordingStartedState(val hasVideo: Boolean, val showStartedInfo: Boolean) : ViewState object RecordingStoppedState : ViewState + open class RecordingStartingState(val hasVideo: Boolean) : ViewState + object RecordingStoppingState : ViewState + object RecordingConfirmStopState : ViewState + object RecordingErrorState : ViewState private val _viewState: MutableLiveData = MutableLiveData(RecordingStoppedState) @@ -61,20 +67,25 @@ class CallRecordingViewModel @Inject constructor(private val repository: CallRec is RecordingStartedState -> { _viewState.value = RecordingConfirmStopState } + RecordingStoppedState -> { startRecording() } + RecordingConfirmStopState -> { // confirm dialog to stop recording might have been dismissed without to click an action. // just show it again. _viewState.value = RecordingConfirmStopState } + is RecordingStartingState -> { stopRecording() } + RecordingErrorState -> { stopRecording() } + else -> {} } } diff --git a/app/src/main/java/com/nextcloud/talk/viewmodels/GeoCodingViewModel.kt b/app/src/main/java/com/nextcloud/talk/viewmodels/GeoCodingViewModel.kt index d66d9059c8d..d1ac09f9f1e 100644 --- a/app/src/main/java/com/nextcloud/talk/viewmodels/GeoCodingViewModel.kt +++ b/app/src/main/java/com/nextcloud/talk/viewmodels/GeoCodingViewModel.kt @@ -40,6 +40,7 @@ class GeoCodingViewModel : ViewModel() { private val okHttpClient: OkHttpClient = OkHttpClient.Builder().build() private var geocodingResults: List
= ArrayList() private var query: String = "" + fun getGeocodingResultsLiveData(): LiveData> { return geocodingResultsLiveData } @@ -57,11 +58,12 @@ class GeoCodingViewModel : ViewModel() { } init { - nominatimClient = TalkJsonNominatimClient( - "https://nominatim.openstreetmap.org/", - okHttpClient, - " android@nextcloud.com" - ) + nominatimClient = + TalkJsonNominatimClient( + "https://nominatim.openstreetmap.org/", + okHttpClient, + " android@nextcloud.com" + ) } fun searchLocation() { diff --git a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt index 7e4c5b4ac81..21d872a6f87 100644 --- a/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt +++ b/app/src/main/java/com/nextcloud/talk/webrtc/WebSocketInstance.kt @@ -323,10 +323,12 @@ class WebSocketInstance internal constructor( isConnected = true reconnecting = false val oldResumeId = resumeId - val (_, helloResponseWebSocketMessage1) = LoganSquare.parse( - text, - HelloResponseOverallWebSocketMessage::class.java - ) + val (_, helloResponseWebSocketMessage1) = + LoganSquare + .parse( + text, + HelloResponseOverallWebSocketMessage::class.java + ) if (helloResponseWebSocketMessage1 != null) { resumeId = helloResponseWebSocketMessage1.resumeId sessionId = helloResponseWebSocketMessage1.sessionId @@ -382,9 +384,11 @@ class WebSocketInstance internal constructor( Log.d(TAG, " roomToken: $roomToken") Log.d(TAG, " session: $normalBackendSession") try { - val message = LoganSquare.serialize( - webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession) - ) + val message = + LoganSquare + .serialize( + webSocketConnectionHelper.getAssembledJoinOrLeaveRoomModel(roomToken, normalBackendSession) + ) if (roomToken == "") { Log.d(TAG, "sending 'leave room' via websocket") currentNormalBackendSession = "" @@ -404,9 +408,11 @@ class WebSocketInstance internal constructor( private fun sendCallMessage(ncSignalingMessage: NCSignalingMessage) { try { - val message = LoganSquare.serialize( - webSocketConnectionHelper.getAssembledCallMessageModel(ncSignalingMessage) - ) + val message = + LoganSquare + .serialize( + webSocketConnectionHelper.getAssembledCallMessageModel(ncSignalingMessage) + ) sendMessage(message) } catch (e: IOException) { Log.e(TAG, "Failed to serialize signaling message", e) diff --git a/app/src/main/java/third/parties/fresco/BetterImageSpan.kt b/app/src/main/java/third/parties/fresco/BetterImageSpan.kt index fbd003ed0e7..134a378d935 100644 --- a/app/src/main/java/third/parties/fresco/BetterImageSpan.kt +++ b/app/src/main/java/third/parties/fresco/BetterImageSpan.kt @@ -45,7 +45,9 @@ import androidx.annotation.IntDef * DynamicDrawableSpan (ImageSpan's parent) adjusts sizes as if alignment was ALIGN_BASELINE * which can lead to unnecessary whitespace. */ -open class BetterImageSpan @JvmOverloads constructor( +open class BetterImageSpan +@JvmOverloads +constructor( val drawable: Drawable, @param:BetterImageSpanAlignment private val mAlignment: Int = ALIGN_BASELINE ) : ReplacementSpan() { @@ -65,13 +67,7 @@ open class BetterImageSpan @JvmOverloads constructor( /** * Returns the width of the image span and increases the height if font metrics are available. */ - override fun getSize( - paint: Paint, - text: CharSequence, - start: Int, - end: Int, - fontMetrics: FontMetricsInt? - ): Int { + override fun getSize(paint: Paint, text: CharSequence, start: Int, end: Int, fontMetrics: FontMetricsInt?): Int { updateBounds() if (fontMetrics == null) { return mWidth @@ -125,6 +121,7 @@ open class BetterImageSpan @JvmOverloads constructor( val offset = (textHeight - mHeight) / 2 fm.ascent + offset } + ALIGN_BASELINE -> -mHeight else -> -mHeight }