初始代码

This commit is contained in:
2025-09-03 18:52:07 +08:00
parent 2d21a47e87
commit 6e3e30efa5
3249 changed files with 239147 additions and 0 deletions
@@ -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);
}
}
Binary file not shown.

After

Width:  |  Height:  |  Size: 437 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 638 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 827 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 439 B

Binary file not shown.

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>