初始代码
@@ -0,0 +1,42 @@
|
||||
plugins {
|
||||
id 'com.android.library'
|
||||
}
|
||||
|
||||
android {
|
||||
compileSdkVersion 30
|
||||
buildToolsVersion "30.0.3"
|
||||
namespace "com.tencent.qcloud.tuikit.tuitranslationplugin"
|
||||
defaultConfig {
|
||||
minSdkVersion 19
|
||||
targetSdkVersion 30
|
||||
versionCode 1
|
||||
versionName "1.0"
|
||||
renderscriptSupportModeEnabled false
|
||||
renderscriptTargetApi 30
|
||||
}
|
||||
buildFeatures {
|
||||
buildConfig = false
|
||||
}
|
||||
buildTypes {
|
||||
release {
|
||||
minifyEnabled true
|
||||
proguardFiles getDefaultProguardFile('proguard-android-optimize.txt'), 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility JavaVersion.VERSION_1_8
|
||||
targetCompatibility JavaVersion.VERSION_1_8
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
dependencies {
|
||||
|
||||
implementation 'androidx.appcompat:appcompat:1.3.0'
|
||||
implementation 'com.google.android.material:material:1.3.0'
|
||||
implementation 'com.github.bumptech.glide:glide:4.12.0'
|
||||
implementation 'androidx.viewpager2:viewpager2:1.0.0'
|
||||
annotationProcessor 'com.google.auto.service:auto-service:1.1.1'
|
||||
|
||||
api project(':tuichat')
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android">
|
||||
|
||||
<application>
|
||||
|
||||
</application>
|
||||
|
||||
</manifest>
|
||||
@@ -0,0 +1,107 @@
|
||||
package com.tencent.qcloud.tuikit.tuitranslationplugin;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import com.tencent.qcloud.tuicore.TUIThemeManager;
|
||||
import com.tencent.qcloud.tuicore.util.SPUtils;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TUITranslationConfigs {
|
||||
public static final String PREFERENCE_NAME = "translationLanguage";
|
||||
public static final String SP_KEY_LANGUAGE_NAME = "languageName";
|
||||
private String languageName;
|
||||
private ArrayList<String> targetLanguageNameList;
|
||||
private Map<String, String> targetLanguageMap;
|
||||
|
||||
private static TUITranslationConfigs instance;
|
||||
|
||||
private TUITranslationConfigs() {
|
||||
initTargetLanguage();
|
||||
}
|
||||
|
||||
public static TUITranslationConfigs getInstance() {
|
||||
if (instance == null) {
|
||||
instance = new TUITranslationConfigs();
|
||||
}
|
||||
return instance;
|
||||
}
|
||||
|
||||
public String getTargetLanguageName() {
|
||||
if (TextUtils.isEmpty(languageName)) {
|
||||
languageName = SPUtils.getInstance(PREFERENCE_NAME).getString(SP_KEY_LANGUAGE_NAME);
|
||||
}
|
||||
|
||||
if (TextUtils.isEmpty(languageName)) {
|
||||
String languageCode = TUIThemeManager.getInstance().getCurrentLanguage();
|
||||
for (String key : targetLanguageMap.keySet()) {
|
||||
if (TextUtils.equals(targetLanguageMap.get(key), languageCode)) {
|
||||
languageName = key;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (TextUtils.isEmpty(languageName)) {
|
||||
languageName = "English";
|
||||
}
|
||||
|
||||
return languageName;
|
||||
}
|
||||
|
||||
public void setTargetLanguageName(String languageName) {
|
||||
this.languageName = languageName;
|
||||
SPUtils.getInstance(PREFERENCE_NAME).put(SP_KEY_LANGUAGE_NAME, languageName);
|
||||
}
|
||||
|
||||
public String getTargetLanguageCode() {
|
||||
return targetLanguageMap.get(getTargetLanguageName());
|
||||
}
|
||||
|
||||
public ArrayList<String> getTargetLanguageNameList() {
|
||||
return targetLanguageNameList;
|
||||
}
|
||||
|
||||
public Map<String, String> getTargetLanguageMap() {
|
||||
return targetLanguageMap;
|
||||
}
|
||||
|
||||
private void initTargetLanguage() {
|
||||
targetLanguageNameList = new ArrayList<>();
|
||||
targetLanguageNameList.add("简体中文");
|
||||
targetLanguageNameList.add("繁體中文");
|
||||
targetLanguageNameList.add("English");
|
||||
targetLanguageNameList.add("日本語");
|
||||
targetLanguageNameList.add("한국어");
|
||||
targetLanguageNameList.add("Français");
|
||||
targetLanguageNameList.add("Español");
|
||||
targetLanguageNameList.add("Italiano");
|
||||
targetLanguageNameList.add("Deutsch");
|
||||
targetLanguageNameList.add("Türkçe");
|
||||
targetLanguageNameList.add("Русский");
|
||||
targetLanguageNameList.add("Português");
|
||||
targetLanguageNameList.add("Tiếng Việt");
|
||||
targetLanguageNameList.add("Bahasa Indonesia");
|
||||
targetLanguageNameList.add("ภาษาไทย");
|
||||
targetLanguageNameList.add("Bahasa Melayu");
|
||||
targetLanguageNameList.add("हिन्दी");
|
||||
|
||||
targetLanguageMap = new HashMap<>();
|
||||
targetLanguageMap.put("简体中文", "zh");
|
||||
targetLanguageMap.put("繁體中文", "zh-TW");
|
||||
targetLanguageMap.put("English", "en"); // English
|
||||
targetLanguageMap.put("日本語", "ja"); // Japanese
|
||||
targetLanguageMap.put("한국어", "ko"); // Korean
|
||||
targetLanguageMap.put("Français", "fr"); // French
|
||||
targetLanguageMap.put("Español", "es"); // Spanish
|
||||
targetLanguageMap.put("Italiano", "it"); // Italian
|
||||
targetLanguageMap.put("Deutsch", "de"); // German
|
||||
targetLanguageMap.put("Türkçe", "tr"); // Turkish
|
||||
targetLanguageMap.put("Русский", "ru"); // Russian
|
||||
targetLanguageMap.put("Português", "pt"); // Portuguese
|
||||
targetLanguageMap.put("Tiếng Việt", "vi"); // Vietnamese
|
||||
targetLanguageMap.put("Bahasa Indonesia", "id"); // Indonesian
|
||||
targetLanguageMap.put("ภาษาไทย", "th"); // Thai
|
||||
targetLanguageMap.put("Bahasa Melayu", "ms"); // Malaysian
|
||||
targetLanguageMap.put("हिन्दी", "hi"); // Hindi language
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,238 @@
|
||||
package com.tencent.qcloud.tuikit.tuitranslationplugin;
|
||||
|
||||
import android.content.Context;
|
||||
import android.os.Bundle;
|
||||
import android.text.TextUtils;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.google.auto.service.AutoService;
|
||||
import com.tencent.imsdk.v2.V2TIMMessage;
|
||||
import com.tencent.qcloud.tuicore.ServiceInitializer;
|
||||
import com.tencent.qcloud.tuicore.TUIConstants;
|
||||
import com.tencent.qcloud.tuicore.TUICore;
|
||||
import com.tencent.qcloud.tuicore.annotations.TUIInitializerDependency;
|
||||
import com.tencent.qcloud.tuicore.annotations.TUIInitializerID;
|
||||
import com.tencent.qcloud.tuicore.interfaces.ITUIExtension;
|
||||
import com.tencent.qcloud.tuicore.interfaces.TUIExtensionEventListener;
|
||||
import com.tencent.qcloud.tuicore.interfaces.TUIExtensionInfo;
|
||||
import com.tencent.qcloud.tuicore.interfaces.TUIInitializer;
|
||||
import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean;
|
||||
import com.tencent.qcloud.tuikit.timcommon.component.LineControllerView;
|
||||
import com.tencent.qcloud.tuikit.timcommon.component.MinimalistLineControllerView;
|
||||
import com.tencent.qcloud.tuikit.timcommon.component.activities.SelectionActivity;
|
||||
import com.tencent.qcloud.tuikit.tuichat.bean.message.QuoteMessageBean;
|
||||
import com.tencent.qcloud.tuikit.tuichat.bean.message.TextMessageBean;
|
||||
import com.tencent.qcloud.tuikit.tuichat.config.classicui.TUIChatConfigClassic;
|
||||
import com.tencent.qcloud.tuikit.tuichat.config.minimalistui.TUIChatConfigMinimalist;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.model.TranslationProvider;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.presenter.TranslationPresenter;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.widget.TranslationMessageLayoutProxy;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
@AutoService(TUIInitializer.class)
|
||||
@TUIInitializerDependency({"TUIChat"})
|
||||
@TUIInitializerID("TUITranslationPlugin")
|
||||
public class TUITranslationService implements TUIInitializer, ITUIExtension {
|
||||
public static final String TAG = TUITranslationService.class.getSimpleName();
|
||||
private static TUITranslationService instance;
|
||||
|
||||
public static TUITranslationService getInstance() {
|
||||
return instance;
|
||||
}
|
||||
|
||||
private Context appContext;
|
||||
private TranslationMessageLayoutProxy translationMessageLayoutProxy;
|
||||
|
||||
@Override
|
||||
public void init(Context context) {
|
||||
appContext = context;
|
||||
instance = this;
|
||||
initExtension();
|
||||
translationMessageLayoutProxy = new TranslationMessageLayoutProxy();
|
||||
}
|
||||
|
||||
private void initExtension() {
|
||||
TUICore.registerExtension(TUIConstants.TUIChat.Extension.MessagePopMenu.CLASSIC_EXTENSION_ID, this);
|
||||
TUICore.registerExtension(TUIConstants.TUIChat.Extension.MessagePopMenu.MINIMALIST_EXTENSION_ID, this);
|
||||
TUICore.registerExtension(TUIConstants.TUIChat.Extension.MessageBottom.CLASSIC_EXTENSION_ID, this);
|
||||
TUICore.registerExtension(TUIConstants.TUIChat.Extension.MessageBottom.MINIMALIST_EXTENSION_ID, this);
|
||||
TUICore.registerExtension(TUIConstants.TIMAppKit.Extension.ProfileSettings.CLASSIC_EXTENSION_ID, this);
|
||||
TUICore.registerExtension(TUIConstants.TIMAppKit.Extension.ProfileSettings.MINIMALIST_EXTENSION_ID, this);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<TUIExtensionInfo> onGetExtension(String extensionID, Map<String, Object> param) {
|
||||
if (TextUtils.equals(extensionID, TUIConstants.TUIChat.Extension.MessagePopMenu.CLASSIC_EXTENSION_ID)
|
||||
|| TextUtils.equals(extensionID, TUIConstants.TUIChat.Extension.MessagePopMenu.MINIMALIST_EXTENSION_ID)) {
|
||||
if (param == null || param.isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (TextUtils.equals(extensionID, TUIConstants.TUIChat.Extension.MessagePopMenu.CLASSIC_EXTENSION_ID)) {
|
||||
if (!TUIChatConfigClassic.isEnableTranslate()) {
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
if (!TUIChatConfigMinimalist.isEnableTranslate()) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
TUIMessageBean messageBean = (TUIMessageBean) param.get(TUIConstants.TUIChat.Extension.MessagePopMenu.MESSAGE_BEAN);
|
||||
TranslationPresenter presenter = new TranslationPresenter();
|
||||
if ((messageBean instanceof TextMessageBean || messageBean instanceof QuoteMessageBean)
|
||||
&& presenter.getTranslationStatus(messageBean.getV2TIMMessage()) != TranslationProvider.MSG_TRANSLATE_STATUS_SHOWN
|
||||
&& !messageBean.hasRiskContent()) {
|
||||
// only select-all-text can be translated
|
||||
String selectText = messageBean.getSelectText();
|
||||
if (!TextUtils.isEmpty(selectText)) {
|
||||
String text = messageBean.getExtra();
|
||||
if (!text.equals(selectText)) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
TUIExtensionInfo extensionInfo = new TUIExtensionInfo();
|
||||
extensionInfo.setWeight(3000);
|
||||
extensionInfo.setText(appContext.getString(R.string.translate_action));
|
||||
if (TextUtils.equals(extensionID, TUIConstants.TUIChat.Extension.MessagePopMenu.CLASSIC_EXTENSION_ID)) {
|
||||
extensionInfo.setIcon(R.drawable.pop_menu_translate);
|
||||
} else {
|
||||
extensionInfo.setIcon(R.drawable.minimalist_pop_menu_translation);
|
||||
}
|
||||
|
||||
extensionInfo.setExtensionListener(new TUIExtensionEventListener() {
|
||||
@Override
|
||||
public void onClicked(Map<String, Object> param) {
|
||||
translationMessageLayoutProxy.translateMessage(messageBean);
|
||||
}
|
||||
});
|
||||
return Collections.singletonList(extensionInfo);
|
||||
}
|
||||
} else if (TextUtils.equals(extensionID, TUIConstants.TIMAppKit.Extension.ProfileSettings.CLASSIC_EXTENSION_ID)
|
||||
|| TextUtils.equals(extensionID, TUIConstants.TIMAppKit.Extension.ProfileSettings.MINIMALIST_EXTENSION_ID)) {
|
||||
TUIExtensionInfo extensionInfo = new TUIExtensionInfo();
|
||||
View translationSelectorView = null;
|
||||
if (TextUtils.equals(extensionID, TUIConstants.TIMAppKit.Extension.ProfileSettings.CLASSIC_EXTENSION_ID)) {
|
||||
translationSelectorView = View.inflate(appContext, R.layout.profile_settings_translation, null);
|
||||
LineControllerView modifyTranslationTargetLanguageView = translationSelectorView.findViewById(R.id.modify_translation_target_language);
|
||||
modifyTranslationTargetLanguageView.setCanNav(true);
|
||||
modifyTranslationTargetLanguageView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
selectLanguage(TUIConstants.TUIChat.THEME_STYLE_CLASSIC, modifyTranslationTargetLanguageView);
|
||||
}
|
||||
});
|
||||
modifyTranslationTargetLanguageView.setContent(TUITranslationConfigs.getInstance().getTargetLanguageName());
|
||||
} else {
|
||||
translationSelectorView = View.inflate(appContext, R.layout.minimalist_profile_settings_translation, null);
|
||||
MinimalistLineControllerView modifyTranslationTargetLanguageView =
|
||||
translationSelectorView.findViewById(R.id.modify_translation_target_language);
|
||||
modifyTranslationTargetLanguageView.setCanNav(true);
|
||||
modifyTranslationTargetLanguageView.setOnClickListener(new View.OnClickListener() {
|
||||
@Override
|
||||
public void onClick(View view) {
|
||||
selectLanguage(TUIConstants.TUIChat.THEME_STYLE_MINIMALIST, modifyTranslationTargetLanguageView);
|
||||
}
|
||||
});
|
||||
modifyTranslationTargetLanguageView.setContent(TUITranslationConfigs.getInstance().getTargetLanguageName());
|
||||
}
|
||||
|
||||
HashMap<String, Object> paramMap = new HashMap<>();
|
||||
paramMap.put(TUIConstants.TIMAppKit.Extension.ProfileSettings.KEY_VIEW, translationSelectorView);
|
||||
extensionInfo.setData(paramMap);
|
||||
extensionInfo.setWeight(500);
|
||||
return Collections.singletonList(extensionInfo);
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean onRaiseExtension(String extensionID, View parentView, Map<String, Object> param) {
|
||||
if (TextUtils.equals(extensionID, TUIConstants.TUIChat.Extension.MessageBottom.CLASSIC_EXTENSION_ID)
|
||||
|| TextUtils.equals(extensionID, TUIConstants.TUIChat.Extension.MessageBottom.MINIMALIST_EXTENSION_ID)) {
|
||||
if (param == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
ViewGroup viewGroup = null;
|
||||
if (parentView instanceof ViewGroup) {
|
||||
viewGroup = (ViewGroup) parentView;
|
||||
}
|
||||
if (viewGroup == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
TUIMessageBean messageBean = (TUIMessageBean) param.get(TUIConstants.TUIChat.MESSAGE_BEAN);
|
||||
if (messageBean == null) {
|
||||
return false;
|
||||
}
|
||||
V2TIMMessage v2TIMMessage = messageBean.getV2TIMMessage();
|
||||
if (v2TIMMessage == null) {
|
||||
return false;
|
||||
}
|
||||
|
||||
if (v2TIMMessage.getElemType() != V2TIMMessage.V2TIM_ELEM_TYPE_TEXT) {
|
||||
return false;
|
||||
}
|
||||
|
||||
RecyclerView recyclerView = (RecyclerView) param.get(TUIConstants.TUIChat.CHAT_RECYCLER_VIEW);
|
||||
Fragment fragment = null;
|
||||
Object fragmentObject = param.get(TUIConstants.TUIChat.FRAGMENT);
|
||||
if (fragmentObject != null && fragmentObject instanceof Fragment) {
|
||||
fragment = (Fragment) fragmentObject;
|
||||
}
|
||||
|
||||
if (TextUtils.equals(extensionID, TUIConstants.TUIChat.Extension.MessageBottom.CLASSIC_EXTENSION_ID)) {
|
||||
translationMessageLayoutProxy.showTranslationView(fragment, TUIConstants.TUIChat.THEME_STYLE_CLASSIC, recyclerView, viewGroup, messageBean);
|
||||
} else {
|
||||
translationMessageLayoutProxy.showTranslationView(fragment, TUIConstants.TUIChat.THEME_STYLE_MINIMALIST, recyclerView, viewGroup, messageBean);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private void selectLanguage(String theme, View modifyTranslationTargetLanguageView) {
|
||||
Bundle bundle = new Bundle();
|
||||
bundle.putString(SelectionActivity.Selection.TITLE, appContext.getResources().getString(R.string.translation_target_language));
|
||||
bundle.putStringArrayList(SelectionActivity.Selection.LIST, TUITranslationConfigs.getInstance().getTargetLanguageNameList());
|
||||
String languageName = TUITranslationConfigs.getInstance().getTargetLanguageName();
|
||||
int languageIndex = TUITranslationConfigs.getInstance().getTargetLanguageNameList().indexOf(languageName);
|
||||
bundle.putInt(SelectionActivity.Selection.DEFAULT_SELECT_ITEM_INDEX, languageIndex);
|
||||
SelectionActivity.startListSelection(getAppContext(), bundle, new SelectionActivity.OnResultReturnListener() {
|
||||
@Override
|
||||
public void onReturn(Object text) {
|
||||
int index = (Integer) text;
|
||||
String languageName = TUITranslationConfigs.getInstance().getTargetLanguageNameList().get(index);
|
||||
TUITranslationConfigs.getInstance().setTargetLanguageName(languageName);
|
||||
if (TextUtils.equals(theme, TUIConstants.TUIChat.THEME_STYLE_CLASSIC)) {
|
||||
((LineControllerView) modifyTranslationTargetLanguageView).setContent(languageName);
|
||||
} else if (TextUtils.equals(theme, TUIConstants.TUIChat.THEME_STYLE_MINIMALIST)) {
|
||||
((MinimalistLineControllerView) modifyTranslationTargetLanguageView).setContent(languageName);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private Object getOrDefault(Object value, Object defaultValue) {
|
||||
if (value != null) {
|
||||
return value;
|
||||
}
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
public static Context getAppContext() {
|
||||
return ServiceInitializer.getAppContext();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,267 @@
|
||||
package com.tencent.qcloud.tuikit.tuitranslationplugin.model;
|
||||
|
||||
import static com.tencent.imsdk.v2.V2TIMMessage.V2TIM_ELEM_TYPE_TEXT;
|
||||
|
||||
import android.text.TextUtils;
|
||||
|
||||
import com.tencent.imsdk.BaseConstants;
|
||||
import com.tencent.imsdk.message.MessageAtInfo;
|
||||
import com.tencent.imsdk.v2.V2TIMGroupAtInfo;
|
||||
import com.tencent.imsdk.v2.V2TIMManager;
|
||||
import com.tencent.imsdk.v2.V2TIMMessage;
|
||||
import com.tencent.imsdk.v2.V2TIMTextElem;
|
||||
import com.tencent.imsdk.v2.V2TIMUserFullInfo;
|
||||
import com.tencent.imsdk.v2.V2TIMValueCallback;
|
||||
import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean;
|
||||
import com.tencent.qcloud.tuikit.timcommon.component.interfaces.IUIKitCallback;
|
||||
import com.tencent.qcloud.tuikit.tuichat.TUIChatService;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.R;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.TUITranslationConfigs;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.util.TUITranslationLog;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.util.TUITranslationUtils;
|
||||
|
||||
import org.json.JSONException;
|
||||
import org.json.JSONObject;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class TranslationProvider {
|
||||
private static final String TAG = "TranslationProvider";
|
||||
|
||||
/**
|
||||
* message translation unknown
|
||||
*/
|
||||
public static final int MSG_TRANSLATE_STATUS_UNKNOWN = 0;
|
||||
/**
|
||||
* message translation hidden
|
||||
*/
|
||||
public static final int MSG_TRANSLATE_STATUS_HIDDEN = 1;
|
||||
/**
|
||||
* message translation loading
|
||||
*/
|
||||
public static final int MSG_TRANSLATE_STATUS_LOADING = 2;
|
||||
/**
|
||||
* message translation shown
|
||||
*/
|
||||
public static final int MSG_TRANSLATE_STATUS_SHOWN = 3;
|
||||
|
||||
private static final String TRANSLATION_KEY = "translation";
|
||||
private static final String TRANSLATION_VIEW_STATUS_KEY = "translation_view_status";
|
||||
|
||||
private V2TIMMessage v2TIMMessage;
|
||||
|
||||
public TranslationProvider() {}
|
||||
|
||||
public void translateMessage(TUIMessageBean messageBean, IUIKitCallback<String> callback) {
|
||||
V2TIMMessage v2TIMMessage = messageBean.getV2TIMMessage();
|
||||
if (v2TIMMessage == null) {
|
||||
TUITranslationUtils.callbackOnError(callback, TAG, BaseConstants.ERR_INVALID_PARAMETERS, "translateMessage v2TIMMessage is null");
|
||||
return;
|
||||
}
|
||||
|
||||
if (v2TIMMessage.getElemType() != V2TIM_ELEM_TYPE_TEXT) {
|
||||
TUITranslationUtils.callbackOnError(callback, TAG, BaseConstants.ERR_INVALID_PARAMETERS, "translateMessage v2TIMMessage is not text type");
|
||||
return;
|
||||
}
|
||||
|
||||
if (getTranslationStatus(v2TIMMessage) == MSG_TRANSLATE_STATUS_HIDDEN) {
|
||||
setTranslationStatus(v2TIMMessage, MSG_TRANSLATE_STATUS_SHOWN);
|
||||
TUITranslationUtils.callbackOnSuccess(callback, getTranslationText(v2TIMMessage));
|
||||
return;
|
||||
}
|
||||
|
||||
setTranslationStatus(v2TIMMessage, MSG_TRANSLATE_STATUS_LOADING);
|
||||
String targetLanguageCode = TUITranslationConfigs.getInstance().getTargetLanguageCode();
|
||||
|
||||
List<String> atUserIDList = v2TIMMessage.getGroupAtUserList();
|
||||
List<String> atRealUserIDList = new ArrayList<>();
|
||||
List<String> groupAtUserNicknameList = new ArrayList<>();
|
||||
List<Integer> atAllIndexList = new ArrayList<>();
|
||||
for (int i = 0; i < atUserIDList.size(); i++) {
|
||||
String userID = atUserIDList.get(i);
|
||||
if (userID.equals(V2TIMGroupAtInfo.AT_ALL_TAG)) {
|
||||
atAllIndexList.add(i);
|
||||
} else {
|
||||
atRealUserIDList.add(userID);
|
||||
}
|
||||
}
|
||||
|
||||
if (atRealUserIDList.size() == 0) {
|
||||
for (Integer atAllIndex : atAllIndexList) {
|
||||
groupAtUserNicknameList.add(TUIChatService.getAppContext().getString(R.string.at_all));
|
||||
}
|
||||
|
||||
translateMessage(messageBean, groupAtUserNicknameList, targetLanguageCode, callback);
|
||||
} else {
|
||||
V2TIMManager.getInstance().getUsersInfo(atRealUserIDList, new V2TIMValueCallback<List<V2TIMUserFullInfo>>() {
|
||||
@Override
|
||||
public void onSuccess(List<V2TIMUserFullInfo> v2TIMUserFullInfos) {
|
||||
for (String userID : v2TIMMessage.getGroupAtUserList()) {
|
||||
for (V2TIMUserFullInfo userFullInfo : v2TIMUserFullInfos) {
|
||||
if (userID.equals(MessageAtInfo.AT_ALL_TAG)) {
|
||||
groupAtUserNicknameList.add(userID);
|
||||
break;
|
||||
}
|
||||
|
||||
if (userID.equals(userFullInfo.getUserID())) {
|
||||
groupAtUserNicknameList.add(userFullInfo.getNickName());
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
for (Integer atAllIndex : atAllIndexList) {
|
||||
groupAtUserNicknameList.set(atAllIndex, TUIChatService.getAppContext().getString(R.string.at_all));
|
||||
}
|
||||
|
||||
translateMessage(messageBean, groupAtUserNicknameList, targetLanguageCode, callback);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int code, String desc) {
|
||||
setTranslationStatus(v2TIMMessage, MSG_TRANSLATE_STATUS_UNKNOWN);
|
||||
TUITranslationLog.e(TAG, "translateMessage getUsersInfo error code = " + code + ",des = " + desc);
|
||||
TUITranslationUtils.callbackOnError(callback, TAG, BaseConstants.ERR_INVALID_PARAMETERS, "translateMessage-getUsersInfo failed");
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
private void translateMessage(TUIMessageBean messageBean, List<String> groupAtUserNicknameList, String targetLanguage, IUIKitCallback<String> callback) {
|
||||
V2TIMMessage v2TIMMessage = messageBean.getV2TIMMessage();
|
||||
V2TIMTextElem timTextElem = v2TIMMessage.getTextElem();
|
||||
HashMap<String, List<String>> splitMap = TUITranslationUtils.splitTextByEmojiAndAtUsers(timTextElem.getText(), groupAtUserNicknameList);
|
||||
List<String> toTranslateTextList = splitMap.get(TUITranslationUtils.SPLIT_TEXT_FOR_TRANSLATION);
|
||||
|
||||
if (toTranslateTextList == null || toTranslateTextList.isEmpty()) {
|
||||
List<String> splitTextList = splitMap.get(TUITranslationUtils.SPLIT_TEXT);
|
||||
String translateResult = "";
|
||||
if (splitTextList != null) {
|
||||
for (String result : splitTextList) {
|
||||
translateResult += result;
|
||||
}
|
||||
}
|
||||
saveTranslationResult(v2TIMMessage, translateResult, MSG_TRANSLATE_STATUS_SHOWN);
|
||||
TUITranslationUtils.callbackOnSuccess(callback, translateResult);
|
||||
return;
|
||||
}
|
||||
|
||||
V2TIMManager.getMessageManager().translateText(toTranslateTextList, null, targetLanguage, new V2TIMValueCallback<HashMap<String, String>>() {
|
||||
@Override
|
||||
public void onSuccess(HashMap<String, String> translateHashMap) {
|
||||
List<String> splitTextList = splitMap.get(TUITranslationUtils.SPLIT_TEXT);
|
||||
List<String> translationIndexList = splitMap.get(TUITranslationUtils.SPLIT_TEXT_INDEX_FOR_TRANSLATION);
|
||||
for (String indexString : translationIndexList) {
|
||||
int index = Integer.valueOf(indexString);
|
||||
String originText = splitTextList.get(index);
|
||||
String translatedResult = translateHashMap.get(originText);
|
||||
if (!TextUtils.isEmpty(translatedResult)) {
|
||||
splitTextList.set(index, translatedResult);
|
||||
}
|
||||
}
|
||||
|
||||
String translateResult = "";
|
||||
for (String result : splitTextList) {
|
||||
translateResult += result;
|
||||
}
|
||||
|
||||
saveTranslationResult(v2TIMMessage, translateResult, MSG_TRANSLATE_STATUS_SHOWN);
|
||||
TUITranslationUtils.callbackOnSuccess(callback, translateResult);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(int code, String desc) {
|
||||
setTranslationStatus(v2TIMMessage, MSG_TRANSLATE_STATUS_UNKNOWN);
|
||||
TUITranslationLog.e(TAG, "translateText error code = " + code + ",des = " + desc);
|
||||
TUITranslationUtils.callbackOnError(callback, code, desc);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
public void saveTranslationResult(V2TIMMessage v2TIMMessage, String text, int status) {
|
||||
if (v2TIMMessage != null) {
|
||||
String localCustomData = v2TIMMessage.getLocalCustomData();
|
||||
JSONObject customJson = new JSONObject();
|
||||
try {
|
||||
if (!TextUtils.isEmpty(localCustomData)) {
|
||||
customJson = new JSONObject(localCustomData);
|
||||
}
|
||||
customJson.put(TRANSLATION_KEY, text);
|
||||
customJson.put(TRANSLATION_VIEW_STATUS_KEY, status);
|
||||
v2TIMMessage.setLocalCustomData(customJson.toString());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void setTranslationStatus(V2TIMMessage v2TIMMessage, int status) {
|
||||
if (status != MSG_TRANSLATE_STATUS_UNKNOWN && status != MSG_TRANSLATE_STATUS_HIDDEN && status != MSG_TRANSLATE_STATUS_SHOWN
|
||||
&& status != MSG_TRANSLATE_STATUS_LOADING) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (v2TIMMessage != null) {
|
||||
String localCustomData = v2TIMMessage.getLocalCustomData();
|
||||
JSONObject customJson = new JSONObject();
|
||||
try {
|
||||
if (!TextUtils.isEmpty(localCustomData)) {
|
||||
customJson = new JSONObject(localCustomData);
|
||||
}
|
||||
|
||||
if (customJson.has(TRANSLATION_VIEW_STATUS_KEY)) {
|
||||
int oldTranslationStatus = customJson.getInt(TRANSLATION_VIEW_STATUS_KEY);
|
||||
if (oldTranslationStatus == status) {
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
customJson.put(TRANSLATION_VIEW_STATUS_KEY, status);
|
||||
v2TIMMessage.setLocalCustomData(customJson.toString());
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public String getTranslationText(V2TIMMessage v2TIMMessage) {
|
||||
String result = "";
|
||||
if (v2TIMMessage != null) {
|
||||
String localCustomData = v2TIMMessage.getLocalCustomData();
|
||||
if (TextUtils.isEmpty(localCustomData)) {
|
||||
return result;
|
||||
}
|
||||
try {
|
||||
JSONObject customJson = new JSONObject(localCustomData);
|
||||
if (customJson.has(TRANSLATION_KEY)) {
|
||||
result = customJson.getString(TRANSLATION_KEY);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
public int getTranslationStatus(V2TIMMessage v2TIMMessage) {
|
||||
int translationStatus = MSG_TRANSLATE_STATUS_UNKNOWN;
|
||||
if (v2TIMMessage != null) {
|
||||
String localCustomData = v2TIMMessage.getLocalCustomData();
|
||||
if (TextUtils.isEmpty(localCustomData)) {
|
||||
return translationStatus;
|
||||
}
|
||||
try {
|
||||
JSONObject customJson = new JSONObject(localCustomData);
|
||||
if (customJson.has(TRANSLATION_VIEW_STATUS_KEY)) {
|
||||
translationStatus = customJson.getInt(TRANSLATION_VIEW_STATUS_KEY);
|
||||
}
|
||||
} catch (JSONException e) {
|
||||
e.printStackTrace();
|
||||
}
|
||||
}
|
||||
return translationStatus;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,74 @@
|
||||
package com.tencent.qcloud.tuikit.tuitranslationplugin.presenter;
|
||||
|
||||
import com.tencent.imsdk.v2.V2TIMMessage;
|
||||
import com.tencent.qcloud.tuicore.TUIConstants;
|
||||
import com.tencent.qcloud.tuicore.TUICore;
|
||||
import com.tencent.qcloud.tuicore.util.ToastUtil;
|
||||
import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean;
|
||||
import com.tencent.qcloud.tuikit.timcommon.component.interfaces.IUIKitCallback;
|
||||
import com.tencent.qcloud.tuikit.tuichat.TUIChatConstants;
|
||||
import com.tencent.qcloud.tuikit.tuichat.TUIChatService;
|
||||
import com.tencent.qcloud.tuikit.tuichat.interfaces.IMessageRecyclerView;
|
||||
import com.tencent.qcloud.tuikit.tuichat.util.TUIChatLog;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.R;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.model.TranslationProvider;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.util.TUITranslationLog;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class TranslationPresenter {
|
||||
private static final String TAG = "TranslationPresenter";
|
||||
|
||||
public TranslationProvider provider;
|
||||
|
||||
public TranslationPresenter() {
|
||||
provider = new TranslationProvider();
|
||||
}
|
||||
|
||||
public void translateMessage(TUIMessageBean messageBean, IUIKitCallback<String> callback) {
|
||||
provider.translateMessage(messageBean, new IUIKitCallback<String>() {
|
||||
@Override
|
||||
public void onSuccess(String data) {
|
||||
TUIChatLog.d(TAG, "translateMessage success:" + data);
|
||||
// notify Chat that message has been changed
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put(TUIChatConstants.MESSAGE_BEAN, messageBean);
|
||||
param.put(TUIChatConstants.DATA_CHANGE_TYPE, IMessageRecyclerView.DATA_CHANGE_SCROLL_TO_POSITION_WITHOUT_HIGH_LIGHT);
|
||||
TUICore.notifyEvent(
|
||||
TUIConstants.TUIChat.EVENT_KEY_MESSAGE_EVENT, TUIConstants.TUIChat.EVENT_SUB_KEY_MESSAGE_INFO_CHANGED, param);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onError(String module, int errCode, String errMsg) {
|
||||
TUITranslationLog.e(TAG, "translateMessage failed code:" + errCode + ", msg:" + errMsg);
|
||||
if (errCode == 30007) {
|
||||
ToastUtil.toastShortMessage(TUIChatService.getAppContext().getString(R.string.translation_language_not_support));
|
||||
} else {
|
||||
ToastUtil.toastShortMessage(TUIChatService.getAppContext().getString(R.string.translation_fail));
|
||||
}
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put(TUIChatConstants.MESSAGE_BEAN, messageBean);
|
||||
param.put(TUIChatConstants.DATA_CHANGE_TYPE, IMessageRecyclerView.DATA_CHANGE_TYPE_UPDATE);
|
||||
TUICore.notifyEvent(
|
||||
TUIConstants.TUIChat.EVENT_KEY_MESSAGE_EVENT, TUIConstants.TUIChat.EVENT_SUB_KEY_MESSAGE_INFO_CHANGED, param);
|
||||
}
|
||||
});
|
||||
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put(TUIChatConstants.MESSAGE_BEAN, messageBean);
|
||||
param.put(TUIChatConstants.DATA_CHANGE_TYPE, IMessageRecyclerView.DATA_CHANGE_TYPE_UPDATE);
|
||||
TUICore.notifyEvent(TUIConstants.TUIChat.EVENT_KEY_MESSAGE_EVENT, TUIConstants.TUIChat.EVENT_SUB_KEY_MESSAGE_INFO_CHANGED, param);
|
||||
}
|
||||
|
||||
public int getTranslationStatus(V2TIMMessage v2TIMMessage) {
|
||||
return provider.getTranslationStatus(v2TIMMessage);
|
||||
}
|
||||
|
||||
public void setTranslationStatus(V2TIMMessage v2TIMMessage, int status) {
|
||||
provider.setTranslationStatus(v2TIMMessage, status);
|
||||
}
|
||||
|
||||
public String getTranslationText(V2TIMMessage v2TIMMessage) {
|
||||
return provider.getTranslationText(v2TIMMessage);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
package com.tencent.qcloud.tuikit.tuitranslationplugin.util;
|
||||
|
||||
import com.tencent.imsdk.common.IMLog;
|
||||
|
||||
public class TUITranslationLog extends IMLog {
|
||||
private static final String PRE = "TUITranslationLog-";
|
||||
|
||||
private static String mixTag(String tag) {
|
||||
return PRE + tag;
|
||||
}
|
||||
|
||||
/**
|
||||
* Print INFO level log
|
||||
*
|
||||
* @param strTag TAG
|
||||
* @param strInfo
|
||||
*/
|
||||
public static void v(String strTag, String strInfo) {
|
||||
IMLog.v(mixTag(strTag), strInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print DEBUG level log
|
||||
*
|
||||
* @param strTag TAG
|
||||
* @param strInfo
|
||||
*/
|
||||
public static void d(String strTag, String strInfo) {
|
||||
IMLog.d(mixTag(strTag), strInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print INFO level log
|
||||
*
|
||||
* @param strTag TAG
|
||||
* @param strInfo
|
||||
*/
|
||||
public static void i(String strTag, String strInfo) {
|
||||
IMLog.i(mixTag(strTag), strInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print WARN level log
|
||||
*
|
||||
* @param strTag TAG
|
||||
* @param strInfo
|
||||
*/
|
||||
public static void w(String strTag, String strInfo) {
|
||||
IMLog.w(mixTag(strTag), strInfo);
|
||||
}
|
||||
|
||||
/**
|
||||
* Print WARN level log
|
||||
*
|
||||
* @param strTag TAG
|
||||
* @param strInfo
|
||||
*/
|
||||
public static void w(String strTag, String strInfo, Throwable e) {
|
||||
IMLog.w(mixTag(strTag), strInfo + e.getMessage());
|
||||
}
|
||||
|
||||
/**
|
||||
* Print ERROR level log
|
||||
*
|
||||
* @param strTag TAG
|
||||
* @param strInfo
|
||||
*/
|
||||
public static void e(String strTag, String strInfo) {
|
||||
IMLog.e(mixTag(strTag), strInfo);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,149 @@
|
||||
package com.tencent.qcloud.tuikit.tuitranslationplugin.util;
|
||||
|
||||
import android.text.TextUtils;
|
||||
import com.tencent.qcloud.tuicore.util.ErrorMessageConverter;
|
||||
import com.tencent.qcloud.tuikit.timcommon.component.face.FaceManager;
|
||||
import com.tencent.qcloud.tuikit.timcommon.component.interfaces.IUIKitCallback;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
|
||||
public class TUITranslationUtils {
|
||||
public static final String SPLIT_TEXT = "split_result";
|
||||
public static final String SPLIT_TEXT_FOR_TRANSLATION = "split_translation";
|
||||
public static final String SPLIT_TEXT_INDEX_FOR_TRANSLATION = "split_translation_index";
|
||||
|
||||
public static <T> void callbackOnError(IUIKitCallback<T> callBack, String module, int errCode, String desc) {
|
||||
if (callBack != null) {
|
||||
callBack.onError(module, errCode, ErrorMessageConverter.convertIMError(errCode, desc));
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void callbackOnError(IUIKitCallback<T> callBack, int errCode, String desc) {
|
||||
if (callBack != null) {
|
||||
callBack.onError(null, errCode, ErrorMessageConverter.convertIMError(errCode, desc));
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> void callbackOnSuccess(IUIKitCallback<T> callBack, T data) {
|
||||
if (callBack != null) {
|
||||
callBack.onSuccess(data);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Slice string using both emoji and @user. For instance,
|
||||
* Origin string is "hello[Grin]world, @user1 see you!", and users is ["user1"];
|
||||
* Return value is:
|
||||
* {
|
||||
* "split_result": ["hello", "[Grin]", "world, ", "@user1", " see you!"],
|
||||
* "split_translation": ["hello", "world, ", "see you!"],
|
||||
* "split_translation_index": [0, 2, 4]
|
||||
* }
|
||||
* split_result contains all elements after splited.
|
||||
* split_translation contains all text elements in split_result, excluding emoji an @user infos.
|
||||
* split_translation_index contains the position of texts in textArray located in split_result.
|
||||
*
|
||||
*/
|
||||
public static HashMap<String, List<String>> splitTextByEmojiAndAtUsers(String text, List<String> userList) {
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
List<String> result = new ArrayList<>();
|
||||
List<String> atUsers = new ArrayList<>();
|
||||
if (userList != null && userList.size() > 0) {
|
||||
for (String user : userList) {
|
||||
String atUser = "@" + user;
|
||||
atUsers.add(atUser);
|
||||
}
|
||||
}
|
||||
|
||||
List<String> splitResultByAtUsers = splitByKeyList(atUsers, text);
|
||||
int textIndex = 0;
|
||||
List<String> needTranslationTextIndexList = new ArrayList<>();
|
||||
for (int i = 0; i < splitResultByAtUsers.size(); i++) {
|
||||
String splitString = splitResultByAtUsers.get(i);
|
||||
String atUser = "";
|
||||
if (atUsers.size() > 0) {
|
||||
atUser = atUsers.get(0);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(atUser) && splitString.equals(atUser)) {
|
||||
result.add(splitString);
|
||||
atUsers.remove(0);
|
||||
textIndex++;
|
||||
} else {
|
||||
List<String> emojiKeyList = FaceManager.findEmojiKeyListFromText(splitString);
|
||||
if (emojiKeyList != null && emojiKeyList.size() > 0) {
|
||||
List<String> splitByEmojiResultList = splitByKeyList(emojiKeyList, splitString);
|
||||
for (int j = 0; j < splitByEmojiResultList.size(); j++) {
|
||||
String splitStringByEmoji = splitByEmojiResultList.get(j);
|
||||
result.add(splitStringByEmoji);
|
||||
String emojiKey = "";
|
||||
if (emojiKeyList.size() > 0) {
|
||||
emojiKey = emojiKeyList.get(0);
|
||||
}
|
||||
|
||||
if (!TextUtils.isEmpty(emojiKey) && splitStringByEmoji.equals(emojiKey)) {
|
||||
emojiKeyList.remove(0);
|
||||
} else {
|
||||
needTranslationTextIndexList.add(String.valueOf(textIndex));
|
||||
}
|
||||
textIndex++;
|
||||
}
|
||||
} else {
|
||||
if (!TextUtils.isEmpty(splitString.trim())) {
|
||||
needTranslationTextIndexList.add(String.valueOf(textIndex));
|
||||
}
|
||||
result.add(splitString);
|
||||
textIndex++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
List<String> needTranslationTextList = new ArrayList<>();
|
||||
for (int i = 0; i < needTranslationTextIndexList.size(); i++) {
|
||||
int needTranslationIndex = Integer.valueOf(needTranslationTextIndexList.get(i));
|
||||
needTranslationTextList.add(result.get(needTranslationIndex));
|
||||
}
|
||||
HashMap<String, List<String>> resultMap = new HashMap<>();
|
||||
resultMap.put(SPLIT_TEXT, result);
|
||||
resultMap.put(SPLIT_TEXT_FOR_TRANSLATION, needTranslationTextList);
|
||||
resultMap.put(SPLIT_TEXT_INDEX_FOR_TRANSLATION, needTranslationTextIndexList);
|
||||
|
||||
return resultMap;
|
||||
}
|
||||
|
||||
public static List<String> splitByKeyList(List<String> keyList, String text) {
|
||||
List<String> splitResultByKeyList = new ArrayList<>();
|
||||
if (TextUtils.isEmpty(text)) {
|
||||
return splitResultByKeyList;
|
||||
}
|
||||
|
||||
if (keyList == null || keyList.isEmpty()) {
|
||||
splitResultByKeyList.add(text);
|
||||
return splitResultByKeyList;
|
||||
}
|
||||
|
||||
int fromIndex = 0;
|
||||
for (String key : keyList) {
|
||||
int keyIndex = text.indexOf(key, fromIndex);
|
||||
if (keyIndex >= 0) {
|
||||
if (fromIndex < keyIndex) {
|
||||
String resultBeforeKeyCharacter = text.substring(fromIndex, keyIndex);
|
||||
splitResultByKeyList.add(resultBeforeKeyCharacter);
|
||||
}
|
||||
splitResultByKeyList.add(key);
|
||||
fromIndex = keyIndex + key.length();
|
||||
}
|
||||
}
|
||||
|
||||
if (fromIndex < text.length()) {
|
||||
splitResultByKeyList.add(text.substring(fromIndex));
|
||||
}
|
||||
|
||||
return splitResultByKeyList;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,225 @@
|
||||
package com.tencent.qcloud.tuikit.tuitranslationplugin.widget;
|
||||
|
||||
import android.content.ClipData;
|
||||
import android.content.ClipboardManager;
|
||||
import android.content.Context;
|
||||
import android.graphics.Color;
|
||||
import android.text.TextUtils;
|
||||
import android.util.TypedValue;
|
||||
import android.view.LayoutInflater;
|
||||
import android.view.View;
|
||||
import android.view.ViewGroup;
|
||||
import android.view.animation.LinearInterpolator;
|
||||
import android.view.animation.RotateAnimation;
|
||||
import android.widget.ImageView;
|
||||
import android.widget.LinearLayout;
|
||||
import android.widget.TextView;
|
||||
|
||||
import androidx.fragment.app.Fragment;
|
||||
import androidx.recyclerview.widget.RecyclerView;
|
||||
|
||||
import com.tencent.qcloud.tuicore.TUIConstants;
|
||||
import com.tencent.qcloud.tuicore.TUICore;
|
||||
import com.tencent.qcloud.tuicore.util.ToastUtil;
|
||||
import com.tencent.qcloud.tuikit.timcommon.bean.TUIMessageBean;
|
||||
import com.tencent.qcloud.tuikit.timcommon.component.face.FaceManager;
|
||||
import com.tencent.qcloud.tuikit.timcommon.config.classicui.TUIConfigClassic;
|
||||
import com.tencent.qcloud.tuikit.tuichat.TUIChatConstants;
|
||||
import com.tencent.qcloud.tuikit.tuichat.TUIChatService;
|
||||
import com.tencent.qcloud.tuikit.tuichat.classicui.component.popmenu.ChatPopMenu;
|
||||
import com.tencent.qcloud.tuikit.tuichat.classicui.page.TUIBaseChatFragment;
|
||||
import com.tencent.qcloud.tuikit.tuichat.config.classicui.TUIChatConfigClassic;
|
||||
import com.tencent.qcloud.tuikit.tuichat.interfaces.IMessageRecyclerView;
|
||||
import com.tencent.qcloud.tuikit.tuichat.minimalistui.page.TUIBaseChatMinimalistFragment;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.R;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.model.TranslationProvider;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.presenter.TranslationPresenter;
|
||||
import com.tencent.qcloud.tuikit.tuitranslationplugin.util.TUITranslationLog;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
public class TranslationMessageLayoutProxy {
|
||||
private static final String TAG = "PollMessageLayoutProxy";
|
||||
|
||||
protected List<ChatPopMenu.ChatPopMenuAction> mTranslationPopActions = new ArrayList<>();
|
||||
private ChatPopMenu mTranslationChatPopMenu;
|
||||
|
||||
public TranslationMessageLayoutProxy() {}
|
||||
|
||||
public void showTranslationView(Fragment fragment, String themeStyle, RecyclerView recyclerView, ViewGroup viewGroup, TUIMessageBean tuiMessageBean) {
|
||||
if (viewGroup == null || tuiMessageBean == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
setTranslationContent(fragment, themeStyle, recyclerView, viewGroup, tuiMessageBean);
|
||||
}
|
||||
|
||||
public void translateMessage(TUIMessageBean tuiMessageBean) {
|
||||
TranslationPresenter translationPresenter = new TranslationPresenter();
|
||||
translationPresenter.translateMessage(tuiMessageBean, null);
|
||||
}
|
||||
|
||||
protected void setTranslationContent(Fragment fragment, String themeStyle, RecyclerView recyclerView, ViewGroup viewGroup, TUIMessageBean msg) {
|
||||
LayoutInflater.from(viewGroup.getContext()).inflate(R.layout.translation_contant_layout, viewGroup);
|
||||
ImageView translationLoadingImage = viewGroup.findViewById(R.id.translate_loading_iv);
|
||||
LinearLayout translationResultLayout = viewGroup.findViewById(R.id.translate_result_ll);
|
||||
|
||||
TranslationPresenter presenter = new TranslationPresenter();
|
||||
int translationStatus = presenter.getTranslationStatus(msg.getV2TIMMessage());
|
||||
if (translationStatus == TranslationProvider.MSG_TRANSLATE_STATUS_SHOWN) {
|
||||
ViewGroup parentViewGroup = (ViewGroup) viewGroup.getParent();
|
||||
parentViewGroup.setVisibility(View.VISIBLE);
|
||||
viewGroup.setVisibility(View.VISIBLE);
|
||||
stopTranslationLoading(translationLoadingImage);
|
||||
translationResultLayout.setVisibility(View.VISIBLE);
|
||||
TextView translationText = viewGroup.findViewById(R.id.translate_tv);
|
||||
if (TextUtils.equals(themeStyle, TUIConstants.TUIChat.THEME_STYLE_MINIMALIST)) {
|
||||
translationText.setTextSize(TypedValue.COMPLEX_UNIT_PX,
|
||||
translationText.getResources().getDimension(com.tencent.qcloud.tuikit.timcommon.R.dimen.chat_minimalist_message_text_size));
|
||||
} else {
|
||||
if (msg.hasRiskContent()) {
|
||||
translationText.setText(R.string.translation_risk_content_failed_tip);
|
||||
translationText.setTextColor(viewGroup.getResources().getColor(R.color.translation_risk_tip_color));
|
||||
return;
|
||||
}
|
||||
translationText.setTextColor(Color.BLACK);
|
||||
}
|
||||
|
||||
if (msg.isSelf()) {
|
||||
int sendTextMessageFontSize = TUIChatConfigClassic.getSendTextMessageFontSize();
|
||||
if (sendTextMessageFontSize != TUIChatConfigClassic.UNDEFINED) {
|
||||
translationText.setTextSize(sendTextMessageFontSize);
|
||||
}
|
||||
} else {
|
||||
int receiveTextMessageFontSize = TUIChatConfigClassic.getReceiveTextMessageFontSize();
|
||||
if (receiveTextMessageFontSize != TUIChatConfigClassic.UNDEFINED) {
|
||||
translationText.setTextSize(receiveTextMessageFontSize);
|
||||
}
|
||||
}
|
||||
|
||||
FaceManager.handlerEmojiText(translationText, presenter.getTranslationText(msg.getV2TIMMessage()), false);
|
||||
if (fragment != null) {
|
||||
viewGroup.setOnLongClickListener(new View.OnLongClickListener() {
|
||||
@Override
|
||||
public boolean onLongClick(View view) {
|
||||
showTranslationItemPopMenu(fragment, themeStyle, recyclerView, view, msg);
|
||||
return true;
|
||||
}
|
||||
});
|
||||
}
|
||||
} else if (translationStatus == TranslationProvider.MSG_TRANSLATE_STATUS_LOADING) {
|
||||
ViewGroup parentViewGroup = (ViewGroup) viewGroup.getParent();
|
||||
parentViewGroup.setVisibility(View.VISIBLE);
|
||||
viewGroup.setVisibility(View.VISIBLE);
|
||||
startTranslationLoading(translationLoadingImage);
|
||||
translationResultLayout.setVisibility(View.GONE);
|
||||
viewGroup.setOnLongClickListener(null);
|
||||
} else {
|
||||
stopTranslationLoading(translationLoadingImage);
|
||||
viewGroup.setVisibility(View.GONE);
|
||||
viewGroup.setOnLongClickListener(null);
|
||||
}
|
||||
}
|
||||
|
||||
private void showTranslationItemPopMenu(Fragment fragment, String themeStyle, RecyclerView recyclerView, View view, TUIMessageBean messageInfo) {
|
||||
initTranslationPopActions(fragment, themeStyle, messageInfo);
|
||||
if (mTranslationPopActions.size() == 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (mTranslationChatPopMenu != null) {
|
||||
mTranslationChatPopMenu.hide();
|
||||
mTranslationChatPopMenu = null;
|
||||
}
|
||||
mTranslationChatPopMenu = new ChatPopMenu(view.getContext());
|
||||
mTranslationChatPopMenu.setShowFaces(false);
|
||||
mTranslationChatPopMenu.setChatPopMenuActionList(mTranslationPopActions);
|
||||
|
||||
int[] location = new int[2];
|
||||
recyclerView.getLocationOnScreen(location);
|
||||
mTranslationChatPopMenu.show(view, location[1]);
|
||||
}
|
||||
|
||||
private void initTranslationPopActions(Fragment fragment, String themeStyle, TUIMessageBean msg) {
|
||||
if (msg == null) {
|
||||
return;
|
||||
}
|
||||
|
||||
TranslationPresenter presenter = new TranslationPresenter();
|
||||
ChatPopMenu.ChatPopMenuAction copyAction = new ChatPopMenu.ChatPopMenuAction();
|
||||
copyAction.setActionName(fragment.getContext().getString(R.string.copy_action));
|
||||
copyAction.setActionIcon(com.tencent.qcloud.tuikit.tuichat.R.drawable.pop_menu_copy);
|
||||
copyAction.setActionClickListener(() -> onCopyTranslationClick(presenter.getTranslationText(msg.getV2TIMMessage())));
|
||||
ChatPopMenu.ChatPopMenuAction forwardAction = null;
|
||||
if (msg.getStatus() != TUIMessageBean.MSG_STATUS_SEND_FAIL) {
|
||||
forwardAction = new ChatPopMenu.ChatPopMenuAction();
|
||||
forwardAction.setActionName(fragment.getContext().getString(R.string.forward_action));
|
||||
forwardAction.setActionIcon(R.drawable.pop_menu_forward);
|
||||
forwardAction.setActionClickListener(() -> onForwardTranslationClick(fragment, themeStyle, msg));
|
||||
}
|
||||
ChatPopMenu.ChatPopMenuAction hiddenAction = new ChatPopMenu.ChatPopMenuAction();
|
||||
hiddenAction.setActionName(fragment.getContext().getString(R.string.hide_action));
|
||||
hiddenAction.setActionIcon(R.drawable.pop_menu_hide);
|
||||
hiddenAction.setActionClickListener(() -> onHideTranslationClick(msg));
|
||||
|
||||
mTranslationPopActions.clear();
|
||||
mTranslationPopActions.add(copyAction);
|
||||
if (forwardAction != null) {
|
||||
mTranslationPopActions.add(forwardAction);
|
||||
}
|
||||
mTranslationPopActions.add(hiddenAction);
|
||||
}
|
||||
|
||||
private void onCopyTranslationClick(String translationContent) {
|
||||
ClipboardManager clipboard = (ClipboardManager) TUIChatService.getAppContext().getSystemService(Context.CLIPBOARD_SERVICE);
|
||||
if (clipboard == null || TextUtils.isEmpty(translationContent)) {
|
||||
return;
|
||||
}
|
||||
ClipData clip = ClipData.newPlainText("message", translationContent);
|
||||
clipboard.setPrimaryClip(clip);
|
||||
String copySuccess = TUIChatService.getAppContext().getResources().getString(R.string.copy_success_tip);
|
||||
ToastUtil.toastShortMessage(copySuccess);
|
||||
}
|
||||
|
||||
private void onHideTranslationClick(TUIMessageBean messageBean) {
|
||||
TranslationPresenter presenter = new TranslationPresenter();
|
||||
presenter.setTranslationStatus(messageBean.getV2TIMMessage(), TranslationProvider.MSG_TRANSLATE_STATUS_HIDDEN);
|
||||
Map<String, Object> param = new HashMap<>();
|
||||
param.put(TUIChatConstants.MESSAGE_BEAN, messageBean);
|
||||
param.put(TUIChatConstants.DATA_CHANGE_TYPE, IMessageRecyclerView.DATA_CHANGE_TYPE_UPDATE);
|
||||
TUICore.notifyEvent(TUIConstants.TUIChat.EVENT_KEY_MESSAGE_EVENT, TUIConstants.TUIChat.EVENT_SUB_KEY_MESSAGE_INFO_CHANGED, param);
|
||||
}
|
||||
|
||||
private void onForwardTranslationClick(Fragment fragment, String themeStyle, TUIMessageBean messageBean) {
|
||||
if (fragment == null) {
|
||||
TUITranslationLog.e(TAG, "onForwardTranslationClick fragment is null!");
|
||||
return;
|
||||
}
|
||||
TranslationPresenter presenter = new TranslationPresenter();
|
||||
if (themeStyle != null) {
|
||||
if (themeStyle.equals(TUIConstants.TUIChat.THEME_STYLE_CLASSIC) && fragment instanceof TUIBaseChatFragment) {
|
||||
((TUIBaseChatFragment) fragment).getChatView().forwardText(presenter.getTranslationText(messageBean.getV2TIMMessage()));
|
||||
} else if (themeStyle.equals(TUIConstants.TUIChat.THEME_STYLE_MINIMALIST) && fragment instanceof TUIBaseChatMinimalistFragment) {
|
||||
((TUIBaseChatMinimalistFragment) fragment).getChatView().forwardText(presenter.getTranslationText(messageBean.getV2TIMMessage()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private void startTranslationLoading(ImageView translationLoadingImage) {
|
||||
translationLoadingImage.setVisibility(View.VISIBLE);
|
||||
RotateAnimation translationRotateAnimation =
|
||||
new RotateAnimation(0, 360, RotateAnimation.RELATIVE_TO_SELF, 0.5f, RotateAnimation.RELATIVE_TO_SELF, 0.5f);
|
||||
translationRotateAnimation.setRepeatCount(-1);
|
||||
translationRotateAnimation.setDuration(1000);
|
||||
translationRotateAnimation.setInterpolator(new LinearInterpolator());
|
||||
translationLoadingImage.startAnimation(translationRotateAnimation);
|
||||
}
|
||||
|
||||
private void stopTranslationLoading(ImageView translationLoadingImage) {
|
||||
translationLoadingImage.clearAnimation();
|
||||
translationLoadingImage.setVisibility(View.GONE);
|
||||
}
|
||||
}
|
||||
|
After Width: | Height: | Size: 437 B |
|
After Width: | Height: | Size: 719 B |
|
After Width: | Height: | Size: 638 B |
|
After Width: | Height: | Size: 2.0 KiB |
|
After Width: | Height: | Size: 827 B |
|
After Width: | Height: | Size: 439 B |
|
After Width: | Height: | Size: 580 B |
@@ -0,0 +1,19 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.tencent.qcloud.tuikit.timcommon.component.MinimalistLineControllerView
|
||||
android:id="@+id/modify_translation_target_language"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/profile_item_height"
|
||||
android:layout_marginTop="10dp"
|
||||
app:name="@string/translation_target_language" />
|
||||
<View
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="1dp"
|
||||
android:layout_marginStart="16dp"
|
||||
android:layout_marginEnd="16dp"
|
||||
android:background="#1A000000"/>
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:app="http://schemas.android.com/apk/res-auto"
|
||||
android:orientation="vertical"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="match_parent">
|
||||
<com.tencent.qcloud.tuikit.timcommon.component.LineControllerView
|
||||
android:id="@+id/modify_translation_target_language"
|
||||
android:layout_width="match_parent"
|
||||
android:layout_height="@dimen/profile_item_height"
|
||||
android:layout_marginTop="10dp"
|
||||
app:name="@string/translation_target_language" />
|
||||
|
||||
</LinearLayout>
|
||||
@@ -0,0 +1,59 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<RelativeLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
xmlns:tools="http://schemas.android.com/tools"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content">
|
||||
<ImageView
|
||||
android:id="@+id/translate_loading_iv"
|
||||
android:layout_width="15dp"
|
||||
android:layout_height="15dp"
|
||||
android:layout_margin="10dp"
|
||||
android:background="@drawable/translation_loading"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" />
|
||||
|
||||
<LinearLayout
|
||||
android:id="@+id/translate_result_ll"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:orientation="vertical"
|
||||
android:padding="10dp"
|
||||
android:visibility="gone"
|
||||
tools:visibility="visible" >
|
||||
|
||||
<TextView
|
||||
android:id="@+id/translate_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:lineHeight="20.16sp"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="15.5sp"
|
||||
tools:text="test" />
|
||||
|
||||
<RelativeLayout
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_marginTop="10dp">
|
||||
|
||||
<ImageView
|
||||
android:id="@+id/translate_iv"
|
||||
android:layout_width="13dp"
|
||||
android:layout_height="13dp"
|
||||
android:layout_centerVertical="true"
|
||||
android:alpha="0.4"
|
||||
android:background="@drawable/ic_translate_support" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/translate_supporter_tv"
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_centerVertical="true"
|
||||
android:layout_marginStart="4dp"
|
||||
android:layout_toEndOf="@id/translate_iv"
|
||||
android:alpha="0.4"
|
||||
android:text="@string/translation_support"
|
||||
android:textColor="@color/black"
|
||||
android:textSize="12sp" />
|
||||
</RelativeLayout>
|
||||
</LinearLayout>
|
||||
</RelativeLayout>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="translation_target_language">ترجمة الرسالة إلى</string>
|
||||
<string name="translate_action">ترجمة</string>
|
||||
<string name="translation_support">تم توفير دعم الترجمة من قبل Tencent Cloud Chat</string>
|
||||
<string name="translation_fail">فشل الترجمة</string>
|
||||
<string name="translation_language_not_support">اللغة المستخدمة للترجمة غير مدعومة حاليًا، يرجى التحويل إلى لغة أخرى</string>
|
||||
<string name="at_all">الجميع</string>
|
||||
<string name="copy_action">نسخ</string>
|
||||
<string name="forward_action">إعادة توجيه</string>
|
||||
<string name="hide_action">إخفاء</string>
|
||||
<string name="copy_success_tip">تم النسخ بنجاح</string>
|
||||
<string name="translation_risk_content_failed_tip">يحتوي على محتوى حساس، فشل الترجمة</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="translation_target_language">將消息翻譯為</string>
|
||||
<string name="translate_action">翻譯</string>
|
||||
<string name="translation_support">由騰訊雲 IM 提供翻譯支持</string>
|
||||
<string name="translation_fail">翻譯失敗</string>
|
||||
<string name="translation_language_not_support">當前翻譯語言不支持,請切換至其他語言</string>
|
||||
<string name="at_all">所有人</string>
|
||||
<string name="copy_action">複製</string>
|
||||
<string name="forward_action">轉發</string>
|
||||
<string name="hide_action">隱藏</string>
|
||||
<string name="copy_success_tip">複製成功</string>
|
||||
<string name="translation_risk_content_failed_tip">涉及敏感內容,翻譯失敗</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="translation_target_language">将消息翻译为</string>
|
||||
<string name="translate_action">翻译</string>
|
||||
<string name="translation_support">由腾讯云 IM 提供翻译支持</string>
|
||||
<string name="translation_fail">翻译失败</string>
|
||||
<string name="translation_language_not_support">当前翻译语言不支持,请切换至其他语言</string>
|
||||
<string name="at_all">所有人</string>
|
||||
<string name="copy_action">复制</string>
|
||||
<string name="forward_action">转发</string>
|
||||
<string name="hide_action">隐藏</string>
|
||||
<string name="copy_success_tip">复制成功</string>
|
||||
<string name="translation_risk_content_failed_tip">涉及敏感内容,翻译失败</string>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<color name="translation_risk_tip_color">#DA2222</color>
|
||||
</resources>
|
||||
@@ -0,0 +1,4 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<dimen name="profile_item_height">46.08dp</dimen>
|
||||
</resources>
|
||||
@@ -0,0 +1,14 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<resources>
|
||||
<string name="translation_target_language">Translate message to</string>
|
||||
<string name="translate_action">Translate</string>
|
||||
<string name="translation_support">Translated by Tencent Cloud Chat</string>
|
||||
<string name="translation_fail">Translation failed</string>
|
||||
<string name="translation_language_not_support">Translation is not supported between current languages, please switch to another language.</string>
|
||||
<string name="at_all">All</string>
|
||||
<string name="copy_action">Copy</string>
|
||||
<string name="forward_action">Forward</string>
|
||||
<string name="hide_action">Hide</string>
|
||||
<string name="copy_success_tip">Copy Success</string>
|
||||
<string name="translation_risk_content_failed_tip">Contains sensitive content, translation failed</string>
|
||||
</resources>
|
||||