diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml index c9ce563ec..05ab0ba8e 100644 --- a/app/src/main/AndroidManifest.xml +++ b/app/src/main/AndroidManifest.xml @@ -189,6 +189,10 @@ android:name=".location.GeocodingActivity" android:theme="@style/AppTheme" /> + + diff --git a/app/src/main/java/com/nextcloud/talk/api/NcApi.java b/app/src/main/java/com/nextcloud/talk/api/NcApi.java index 6204f5218..6bf7f984c 100644 --- a/app/src/main/java/com/nextcloud/talk/api/NcApi.java +++ b/app/src/main/java/com/nextcloud/talk/api/NcApi.java @@ -44,6 +44,7 @@ import com.nextcloud.talk.models.json.search.ContactsByNumberOverall; import com.nextcloud.talk.models.json.signaling.SignalingOverall; import com.nextcloud.talk.models.json.signaling.settings.SignalingSettingsOverall; import com.nextcloud.talk.models.json.status.StatusOverall; +import com.nextcloud.talk.models.json.translations.TranslationsOverall; import com.nextcloud.talk.models.json.unifiedsearch.UnifiedSearchOverall; import com.nextcloud.talk.models.json.userprofile.UserProfileFieldsOverall; import com.nextcloud.talk.models.json.userprofile.UserProfileOverall; @@ -654,4 +655,12 @@ public interface NcApi { @DELETE Observable sendCommonDeleteRequest(@Header("Authorization") String authorization, @Url String url); + + + @POST + Observable translateMessage(@Header("Authorization") String authorization, + @Url String url, + @Query("text") String text, + @Query("toLanguage") String toLanguage, + @Nullable @Query("fromLanguage") String fromLanguage); } 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 292911191..0a23661bf 100644 --- a/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt +++ b/app/src/main/java/com/nextcloud/talk/chat/ChatActivity.kt @@ -150,6 +150,7 @@ import com.nextcloud.talk.remotefilebrowser.activities.RemoteFileBrowserActivity import com.nextcloud.talk.repositories.reactions.ReactionsRepository import com.nextcloud.talk.shareditems.activities.SharedItemsActivity import com.nextcloud.talk.signaling.SignalingMessageReceiver +import com.nextcloud.talk.translate.TranslateActivity import com.nextcloud.talk.ui.bottom.sheet.ProfileBottomSheet import com.nextcloud.talk.ui.dialog.AttachmentDialog import com.nextcloud.talk.ui.dialog.MessageActionsDialog @@ -3231,6 +3232,15 @@ class ChatActivity : clipboardManager.setPrimaryClip(clipData) } + fun translateMessage(message: IMessage?) { + val bundle = Bundle() + bundle.putString(BundleKeys.KEY_TRANSLATE_MESSAGE, message?.text) + + val intent = Intent(this, TranslateActivity::class.java) + intent.putExtras(bundle) + startActivity(intent) + } + private fun hasVisibleItems(message: ChatMessage): Boolean { return !message.isDeleted || // copy message message.replyable || // reply to diff --git a/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateData.kt b/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateData.kt new file mode 100644 index 000000000..9ebfdd971 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateData.kt @@ -0,0 +1,37 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.models.json.translations + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class TranslateData( + @JsonField(name = ["text"]) + var text: String?, + @JsonField(name = ["from"]) + var fromLanguage: String? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null, null) +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateOCS.kt b/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateOCS.kt new file mode 100644 index 000000000..74ba31327 --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslateOCS.kt @@ -0,0 +1,38 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.models.json.translations + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import com.nextcloud.talk.models.json.generic.GenericMeta +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +data class TranslateOCS( // TODO finish this model + @JsonField(name = ["meta"]) + var meta: GenericMeta?, + @JsonField(name = ["data"]) + var data: TranslateData? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null, TranslateData()) +} diff --git a/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslationsOverall.kt b/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslationsOverall.kt new file mode 100644 index 000000000..1b832e9cd --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/models/json/translations/TranslationsOverall.kt @@ -0,0 +1,35 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * Copyright (C) 2023 Julius Linus + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.models.json.translations + +import android.os.Parcelable +import com.bluelinelabs.logansquare.annotation.JsonField +import com.bluelinelabs.logansquare.annotation.JsonObject +import kotlinx.parcelize.Parcelize + +@Parcelize +@JsonObject +class TranslationsOverall( + @JsonField(name = ["ocs"]) + var ocs: TranslateOCS? +) : Parcelable { + // This constructor is added to work with the 'com.bluelinelabs.logansquare.annotation.JsonObject' + constructor() : this(null) +} diff --git a/app/src/main/java/com/nextcloud/talk/translate/TranslateActivity.kt b/app/src/main/java/com/nextcloud/talk/translate/TranslateActivity.kt new file mode 100644 index 000000000..b9bb17e8e --- /dev/null +++ b/app/src/main/java/com/nextcloud/talk/translate/TranslateActivity.kt @@ -0,0 +1,279 @@ +/* + * Nextcloud Talk application + * + * @author Julius Linus + * @author Andy Scherzinger + * Copyright (C) 2023 Julius Linus + * Copyright (C) 2023 Andy Scherzinger + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.nextcloud.talk.translate + +import android.graphics.drawable.ColorDrawable +import android.os.Bundle +import android.text.method.ScrollingMovementMethod +import android.util.Log +import android.view.View +import android.widget.AdapterView +import android.widget.ArrayAdapter +import androidx.appcompat.app.AlertDialog +import autodagger.AutoInjector +import com.google.android.material.dialog.MaterialAlertDialogBuilder +import com.nextcloud.talk.R +import com.nextcloud.talk.activities.BaseActivity +import com.nextcloud.talk.api.NcApi +import com.nextcloud.talk.application.NextcloudTalkApplication +import com.nextcloud.talk.data.user.model.User +import com.nextcloud.talk.databinding.ActivityTranslateBinding +import com.nextcloud.talk.models.json.translations.TranslationsOverall +import com.nextcloud.talk.users.UserManager +import com.nextcloud.talk.utils.ApiUtils +import com.nextcloud.talk.utils.bundle.BundleKeys +import com.nextcloud.talk.utils.database.user.CapabilitiesUtilNew +import io.reactivex.Observer +import io.reactivex.android.schedulers.AndroidSchedulers +import io.reactivex.disposables.Disposable +import io.reactivex.schedulers.Schedulers +import org.json.JSONArray +import java.util.Locale +import javax.inject.Inject + +@AutoInjector(NextcloudTalkApplication::class) +class TranslateActivity : BaseActivity() { + private lateinit var binding: ActivityTranslateBinding + + @Inject + lateinit var ncApi: NcApi + + @Inject + lateinit var userManager: UserManager + + var fromLanguages = arrayOf() + + var toLanguages = arrayOf() + + var text: String? = null + + var check: Int = 0 + + override fun onCreate(savedInstanceState: Bundle?) { + super.onCreate(savedInstanceState) + NextcloudTalkApplication.sharedApplication!!.componentApplication.inject(this) + binding = ActivityTranslateBinding.inflate(layoutInflater) + + setupActionBar() + setContentView(binding.root) + setupSystemColors() + setupTextViews() + setupSpinners() + getLanguageOptions() + translate(null, Locale.getDefault().language) + } + + private fun setupActionBar() { + setSupportActionBar(binding.translationToolbar) + binding.translationToolbar.setNavigationOnClickListener { + onBackPressed() + } + supportActionBar?.setDisplayHomeAsUpEnabled(true) + supportActionBar?.setDisplayShowHomeEnabled(true) + supportActionBar?.setIcon(ColorDrawable(resources!!.getColor(R.color.transparent))) + supportActionBar?.title = resources!!.getString(R.string.translation) + viewThemeUtils.material.themeToolbar(binding.translationToolbar) + } + + private fun setupTextViews() { + val original = binding.originalMessageTextview + val translation = binding.translatedMessageTextview + + viewThemeUtils.talk.themeIncomingMessageBubble(original, grouped = true, deleted = false) + viewThemeUtils.talk.themeIncomingMessageBubble(translation, grouped = true, deleted = false) + + original.movementMethod = ScrollingMovementMethod() + translation.movementMethod = ScrollingMovementMethod() + + val bundle = intent.extras + binding.originalMessageTextview.text = bundle?.getString(BundleKeys.KEY_TRANSLATE_MESSAGE) + text = bundle?.getString(BundleKeys.KEY_TRANSLATE_MESSAGE) + } + + private fun getLanguageOptions() { + val currentUser: User = userManager.currentUser.blockingGet() + val json = JSONArray(CapabilitiesUtilNew.getLanguages(currentUser).toString()) + + val fromLanguagesSet = mutableSetOf(resources.getString(R.string.translation_detect_language)) + val toLanguagesSet = mutableSetOf(resources.getString(R.string.translation_device_settings)) + + for (i in 0 until json.length()) { + val current = json.getJSONObject(i) + if (current.getString(FROM_ID) != Locale.getDefault().language) { + toLanguagesSet.add(current.getString(FROM_LABEL)) + } + + fromLanguagesSet.add(current.getString(TO_LABEL)) + } + + fromLanguages = fromLanguagesSet.toTypedArray() + toLanguages = toLanguagesSet.toTypedArray() + + fillSpinners() + } + + private fun enableSpinners(value: Boolean) { + binding.fromLanguageInputLayout.isEnabled = value + binding.toLanguageInputLayout.isEnabled = value + } + + private fun translate(fromLanguage: String?, toLanguage: String) { + val currentUser: User = userManager.currentUser.blockingGet() + val credentials: String = ApiUtils.getCredentials(currentUser.username, currentUser.token) + val translateURL = ApiUtils.getUrlForTranslation(currentUser.baseUrl) + val calculatedFromLanguage = if (fromLanguage == null || fromLanguage == "") { + null + } else { + fromLanguage + } + + Log.i(TAG, "Url is: $translateURL") + ncApi.translateMessage(credentials, translateURL, text, toLanguage, calculatedFromLanguage) + ?.subscribeOn(Schedulers.io()) + ?.observeOn(AndroidSchedulers.mainThread()) + ?.subscribe(object : Observer { + override fun onSubscribe(d: Disposable) { + enableSpinners(false) + binding.translatedMessageTextview.visibility = View.GONE + binding.progressBar.visibility = View.VISIBLE + } + + override fun onNext(translationOverall: TranslationsOverall) { + binding.progressBar.visibility = View.GONE + binding.translatedMessageTextview.visibility = View.VISIBLE + binding.translatedMessageTextview.text = translationOverall.ocs?.data?.text + } + + override fun onError(e: Throwable) { + Log.w(TAG, "Error while translating message", e) + binding.progressBar.visibility = View.GONE + val dialogBuilder = MaterialAlertDialogBuilder(this@TranslateActivity) + .setIcon( + viewThemeUtils.dialog.colorMaterialAlertDialogIcon( + context, + R.drawable.ic_warning_white + ) + ) + .setTitle(R.string.translation_error_title) + .setMessage(R.string.translation_error_message) + .setPositiveButton(R.string.nc_ok) { dialog, _ -> + dialog.dismiss() + } + + viewThemeUtils.dialog.colorMaterialAlertDialogBackground(context, dialogBuilder) + + val dialog = dialogBuilder.show() + + viewThemeUtils.platform.colorTextButtons( + dialog.getButton(AlertDialog.BUTTON_POSITIVE) + ) + } + + override fun onComplete() { + // nothing? + } + }) + + enableSpinners(true) + } + + private fun getISOFromLanguage(language: String): String { + if (resources.getString(R.string.translation_device_settings).equals(language)) { + return Locale.getDefault().language + } + + return getISOFromServer(language) + } + + private fun getISOFromServer(language: String): String { + val currentUser: User = userManager.currentUser.blockingGet() + val json = JSONArray(CapabilitiesUtilNew.getLanguages(currentUser).toString()) + + for (i in 0..json.length() - 1) { + val current = json.getJSONObject(i) + if (current.getString(FROM_LABEL) == language) { + return current.getString(FROM_ID) + } + } + + return "" + } + + private fun setupSpinners() { + viewThemeUtils.material.colorTextInputLayout(binding.fromLanguageInputLayout) + viewThemeUtils.material.colorTextInputLayout(binding.toLanguageInputLayout) + fillSpinners() + + binding.fromLanguage.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + if (++check > 1) { + val fromLabel: String = getISOFromLanguage(parent.getItemAtPosition(position).toString()) + val toLabel: String = getISOFromLanguage(binding.fromLanguage.text.toString()) + Log.i(TAG, "fromLanguageSpinner :: $FROM_LABEL = $fromLabel, $TO_LABEL = $ count: $check") + translate(fromLabel, toLabel) + } + } + + override fun onNothingSelected(parent: AdapterView<*>) { + // write code to perform some action + } + } + + binding.toLanguage.onItemSelectedListener = object : AdapterView.OnItemSelectedListener { + override fun onItemSelected(parent: AdapterView<*>, view: View, position: Int, id: Long) { + if (++check > 2) { + val toLabel: String = getISOFromLanguage(parent.getItemAtPosition(position).toString()) + val fromLabel: String = getISOFromLanguage(binding.fromLanguage.text.toString()) + Log.i(TAG, "toLanguageSpinner :: $FROM_LABEL = $fromLabel, $TO_LABEL = $toLabel count: $check") + translate(fromLabel, toLabel) + } + } + + override fun onNothingSelected(parent: AdapterView<*>) { + // write code to perform some action + } + } + } + + private fun fillSpinners() { + binding.fromLanguage.setAdapter( + ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, fromLanguages) + ) + if (fromLanguages.isNotEmpty()) { + binding.fromLanguage.setText(fromLanguages[0]) + } + + binding.toLanguage.setAdapter( + ArrayAdapter(this, android.R.layout.simple_spinner_dropdown_item, toLanguages) + ) + if (toLanguages.isNotEmpty()) { + binding.toLanguage.setText(toLanguages[0]) + } + } + + companion object { + private val TAG = TranslateActivity::class.simpleName + private const val FROM_ID = "from" + private const val FROM_LABEL = "fromLabel" + private const val TO_LABEL = "toLabel" + } +} 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 9b93117fc..87384a286 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 @@ -55,6 +55,7 @@ import io.reactivex.Observer import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.disposables.Disposable import io.reactivex.schedulers.Schedulers +import org.json.JSONArray import javax.inject.Inject @AutoInjector(NextcloudTalkApplication::class) @@ -88,6 +89,12 @@ class MessageActionsDialog( viewThemeUtils.platform.themeDialog(dialogMessageActionsBinding.root) initEmojiBar(hasChatPermission) initMenuItemCopy(!message.isDeleted) + initMenuItemTranslate( + !message.isDeleted && + ChatMessage.MessageType.REGULAR_TEXT_MESSAGE == message.getCalculateMessageType() && + CapabilitiesUtilNew.isTranslationsSupported(user) && + JSONArray(CapabilitiesUtilNew.getLanguages(user).toString()).length() > 0 + ) initMenuReplyToMessage(message.replyable && hasChatPermission) initMenuReplyPrivately( message.replyable && @@ -296,6 +303,17 @@ class MessageActionsDialog( dialogMessageActionsBinding.menuCopyMessage.visibility = getVisibility(visible) } + private fun initMenuItemTranslate(visible: Boolean) { + if (visible) { + dialogMessageActionsBinding.menuTranslateMessage.setOnClickListener { + chatActivity.translateMessage(message) + dismiss() + } + } + + dialogMessageActionsBinding.menuTranslateMessage.visibility = getVisibility(visible) + } + private fun getVisibility(visible: Boolean): Int { return if (visible) { View.VISIBLE 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 b4b78bd81..93e833f7c 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 @@ -28,7 +28,6 @@ import android.graphics.drawable.Drawable import android.graphics.drawable.LayerDrawable import android.os.Build import android.view.View -import android.view.ViewGroup import android.widget.LinearLayout import android.widget.TextView import androidx.annotation.ColorInt @@ -65,7 +64,7 @@ class TalkSpecificViewThemeUtils @Inject constructor( private val appcompat: AndroidXViewThemeUtils ) : ViewThemeUtilsBase(schemes) { - fun themeIncomingMessageBubble(bubble: ViewGroup, grouped: Boolean, deleted: Boolean) { + fun themeIncomingMessageBubble(bubble: View, grouped: Boolean, deleted: Boolean) { val resources = bubble.resources var bubbleResource = R.drawable.shape_incoming_message @@ -88,7 +87,7 @@ class TalkSpecificViewThemeUtils @Inject constructor( ViewCompat.setBackground(bubble, bubbleDrawable) } - fun themeOutgoingMessageBubble(bubble: ViewGroup, grouped: Boolean, deleted: Boolean) { + fun themeOutgoingMessageBubble(bubble: View, grouped: Boolean, deleted: Boolean) { withScheme(bubble) { scheme -> val bgBubbleColor = if (deleted) { ColorUtils.setAlphaComponent(scheme.surfaceVariant, HALF_ALPHA_INT) diff --git a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java index bc4b04027..7c209e574 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java +++ b/app/src/main/java/com/nextcloud/talk/utils/ApiUtils.java @@ -526,4 +526,8 @@ public class ApiUtils { public static String getUrlForConversationDescription(int version, String baseUrl, String token) { return getUrlForRoom(version, baseUrl, token) + "/description"; } + + public static String getUrlForTranslation(String baseUrl) { + return baseUrl + ocsApiVersion + "/translation/translate"; + } } diff --git a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt index dc6a893d3..88f8acb4a 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/bundle/BundleKeys.kt @@ -29,6 +29,7 @@ object BundleKeys { const val KEY_SELECTED_EMAILS = "KEY_SELECTED_EMAILS" const val KEY_USERNAME = "KEY_USERNAME" const val KEY_TOKEN = "KEY_TOKEN" + const val KEY_TRANSLATE_MESSAGE = "KEY_TRANSLATE_MESSAGE" const val KEY_BASE_URL = "KEY_BASE_URL" const val KEY_IS_ACCOUNT_IMPORT = "KEY_IS_ACCOUNT_IMPORT" const val KEY_ORIGINAL_PROTOCOL = "KEY_ORIGINAL_PROTOCOL" diff --git a/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt b/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt index 2c1802faf..d91a4178f 100644 --- a/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt +++ b/app/src/main/java/com/nextcloud/talk/utils/database/user/CapabilitiesUtilNew.kt @@ -199,5 +199,24 @@ object CapabilitiesUtilNew { return false } + fun isTranslationsSupported(user: User?): Boolean { + if (user?.capabilities != null) { + val capabilities = user.capabilities + return capabilities?.spreedCapability?.config?.containsKey("chat") == true && + capabilities.spreedCapability!!.config!!["chat"] != null && + capabilities.spreedCapability!!.config!!["chat"]!!.containsKey("translations") + } + + return false + } + + fun getLanguages(user: User?): Any? { + return if (isTranslationsSupported(user)) { + user!!.capabilities!!.spreedCapability!!.config!!["chat"]!!["translations"] + } else { + null + } + } + const val DEFAULT_CHAT_SIZE = 1000 } diff --git a/app/src/main/res/drawable-night/ic_chevron_right.xml b/app/src/main/res/drawable-night/ic_chevron_right.xml new file mode 100644 index 000000000..253ff96a2 --- /dev/null +++ b/app/src/main/res/drawable-night/ic_chevron_right.xml @@ -0,0 +1,25 @@ + + + + diff --git a/app/src/main/res/drawable/ic_baseline_translate_24.xml b/app/src/main/res/drawable/ic_baseline_translate_24.xml new file mode 100644 index 000000000..4b511684c --- /dev/null +++ b/app/src/main/res/drawable/ic_baseline_translate_24.xml @@ -0,0 +1,26 @@ + + + + diff --git a/app/src/main/res/drawable/ic_chevron_right.xml b/app/src/main/res/drawable/ic_chevron_right.xml new file mode 100644 index 000000000..91ec3a701 --- /dev/null +++ b/app/src/main/res/drawable/ic_chevron_right.xml @@ -0,0 +1,25 @@ + + + + diff --git a/app/src/main/res/layout-land/activity_translate.xml b/app/src/main/res/layout-land/activity_translate.xml new file mode 100644 index 000000000..144fd2010 --- /dev/null +++ b/app/src/main/res/layout-land/activity_translate.xml @@ -0,0 +1,145 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/activity_translate.xml b/app/src/main/res/layout/activity_translate.xml new file mode 100644 index 000000000..8dbdd7977 --- /dev/null +++ b/app/src/main/res/layout/activity_translate.xml @@ -0,0 +1,148 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/app/src/main/res/layout/dialog_message_actions.xml b/app/src/main/res/layout/dialog_message_actions.xml index ebd2987f0..ebbf99827 100644 --- a/app/src/main/res/layout/dialog_message_actions.xml +++ b/app/src/main/res/layout/dialog_message_actions.xml @@ -287,6 +287,39 @@ + + + + + + + + You are not allowed to activate audio! You are not allowed to activate video! Scroll to bottom + Translate + Translation + From + To + Detect language + Device settings + Translation failed + Could not detect language