diff --git a/app/build.gradle b/app/build.gradle
index b7d6aeba..9b13c96f 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -16,8 +16,8 @@ android {
applicationId "io.agora.chatdemo"
minSdkVersion 21
targetSdkVersion 34
- versionCode 12
- versionName "1.2.0"
+ versionCode 130
+ versionName "1.3.0"
multiDexEnabled true
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
@@ -48,7 +48,6 @@ android {
//
// externalNativeBuild {
// ndkBuild {
-//// arguments "NDK_LIBS_OUT=libs", "all"
// abiFilters "arm64-v8a","armeabi-v7a"
// arguments '-j8'
// }
@@ -111,8 +110,8 @@ dependencies {
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.3.1'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.3.1'
testImplementation 'junit:junit:4.+'
- androidTestImplementation 'androidx.test.ext:junit:1.1.1'
- androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
+ androidTestImplementation 'androidx.test.ext:junit:1.1.5'
+ androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'
//ViewModel and LiveData
implementation 'androidx.lifecycle:lifecycle-extensions:2.2.0'
// Google firebase cloud messaging
@@ -139,8 +138,8 @@ dependencies {
implementation 'com.google.code.gson:gson:2.6.2'
// Agora Chat Uikit
- implementation 'io.agora.rtc:chat-uikit:1.2.0'
- implementation 'io.agora.rtc:chat-callkit:1.2.0'
+ implementation 'io.agora.rtc:chat-uikit:1.3.0'
+ implementation 'io.agora.rtc:chat-callkit:1.3.0'
// implementation project(path: ':chat-uikit')
// implementation project(path: ':chat-callkit')
// url preview
diff --git a/app/jni/Android.mk b/app/jni/Android.mk
index 129fd8b7..f094fe4e 100644
--- a/app/jni/Android.mk
+++ b/app/jni/Android.mk
@@ -19,5 +19,6 @@ include $(CLEAR_VARS)
PB_LITE=1
ENABLE_CALL=0
USE_SQLCIPHER=1
+ENABLE_AGORA=1
#libhyphenate.so
include $(LOCAL_PATH)/../../../emclient-linux/Android.mk
diff --git a/app/src/main/AndroidManifest.xml b/app/src/main/AndroidManifest.xml
index f5bb1895..61da85f7 100644
--- a/app/src/main/AndroidManifest.xml
+++ b/app/src/main/AndroidManifest.xml
@@ -175,14 +175,18 @@
android:exported="false"
android:configChanges="orientation|keyboardHidden|screenSize"
android:label="@string/demo_activity_label_video_call"
+ android:taskAffinity=".SingleCallTask"
android:launchMode="singleInstance"
+ android:excludeFromRecents="true"
android:screenOrientation="portrait" />
{
+ viewModel.getReportMessageObservable().observe(this, response->{
parseResource(response, new OnResourceParseCallback() {
@Override
public void onSuccess(@Nullable Boolean data) {
diff --git a/app/src/main/java/io/agora/chatdemo/chat/CustomChatFragment.java b/app/src/main/java/io/agora/chatdemo/chat/CustomChatFragment.java
index 7dac0f92..aa0aa1f6 100644
--- a/app/src/main/java/io/agora/chatdemo/chat/CustomChatFragment.java
+++ b/app/src/main/java/io/agora/chatdemo/chat/CustomChatFragment.java
@@ -1,6 +1,7 @@
package io.agora.chatdemo.chat;
import static io.agora.chat.uikit.menu.EaseChatType.SINGLE_CHAT;
+import static io.agora.chatdemo.general.utils.ToastUtils.showToast;
import android.Manifest;
import android.app.Activity;
@@ -14,7 +15,6 @@
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.ForegroundColorSpan;
-import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.View;
@@ -35,6 +35,8 @@
import androidx.recyclerview.widget.RecyclerView;
import androidx.swiperefreshlayout.widget.SwipeRefreshLayout;
+import com.google.android.gms.common.util.CollectionUtils;
+
import java.util.ArrayList;
import java.util.HashSet;
import java.util.Iterator;
@@ -44,11 +46,13 @@
import java.util.regex.Matcher;
import java.util.regex.Pattern;
+import io.agora.MessageListener;
import io.agora.chat.ChatClient;
import io.agora.chat.ChatMessage;
import io.agora.chat.ChatRoom;
import io.agora.chat.CustomMessageBody;
import io.agora.chat.LocationMessageBody;
+import io.agora.chat.MessagePinInfo;
import io.agora.chat.TextMessageBody;
import io.agora.chat.uikit.chat.EaseChatFragment;
import io.agora.chat.uikit.chat.adapter.EaseMessageAdapter;
@@ -65,15 +69,22 @@
import io.agora.chatdemo.R;
import io.agora.chatdemo.chat.adapter.CustomMessageAdapter;
import io.agora.chatdemo.chat.viewmodel.ChatViewModel;
+import io.agora.chatdemo.general.callbacks.OnResourceParseCallback;
import io.agora.chatdemo.general.constant.DemoConstant;
import io.agora.chatdemo.general.dialog.AlertDialog;
+import io.agora.chatdemo.general.dialog.SimpleDialog;
import io.agora.chatdemo.general.enums.Status;
import io.agora.chatdemo.general.interfaces.TranslationListener;
import io.agora.chatdemo.general.livedatas.EaseEvent;
import io.agora.chatdemo.general.livedatas.LiveDataBus;
+import io.agora.chatdemo.general.net.Resource;
import io.agora.chatdemo.general.permission.PermissionCompat;
import io.agora.chatdemo.general.permission.PermissionsManager;
import io.agora.chatdemo.general.utils.RecyclerViewUtils;
+import io.agora.chatdemo.general.utils.ToastUtils;
+import io.agora.chatdemo.general.utils.UIUtils;
+import io.agora.chatdemo.general.widget.PinInfoView;
+import io.agora.chatdemo.general.widget.PinMessageListViewGroup;
import io.agora.chatdemo.group.GroupHelper;
import io.agora.chatdemo.group.model.MemberAttributeBean;
import io.agora.chatdemo.group.viewmodel.GroupDetailViewModel;
@@ -82,7 +93,7 @@
import io.agora.chatdemo.me.TranslationSettingsActivity;
import io.agora.util.EMLog;
-public class CustomChatFragment extends EaseChatFragment {
+public class CustomChatFragment extends EaseChatFragment implements MessageListener {
private static final int REQUEST_CODE_STORAGE_PICTURE = 111;
private static final int REQUEST_CODE_STORAGE_VIDEO = 112;
private static final int REQUEST_CODE_STORAGE_FILE = 113;
@@ -100,6 +111,7 @@ public class CustomChatFragment extends EaseChatFragment {
, result -> onRequestResult(result, REQUEST_CODE_STORAGE_VIDEO));
private final ActivityResultLauncher requestFilePermission = registerForActivityResult(new ActivityResultContracts.RequestMultiplePermissions()
, result -> onRequestResult(result, REQUEST_CODE_STORAGE_FILE));
+ private PinInfoView pinInfoView;
@Override
public void onCreate(@Nullable Bundle savedInstanceState) {
@@ -109,8 +121,8 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
result -> {
if (result.getResultCode() == Activity.RESULT_OK) {
boolean enable = DemoHelper.getInstance().getModel().getDemandTranslationEnable();
- if (enable && !TextUtils.isEmpty(getPreferredLanguageCode())){
- translationMessage(translationMsg,getPreferredLanguageCode());
+ if (enable && !TextUtils.isEmpty(getPreferredLanguageCode())) {
+ translationMessage(translationMsg, getPreferredLanguageCode());
}
}
}
@@ -120,53 +132,130 @@ public void onCreate(@Nullable Bundle savedInstanceState) {
@Override
public void initData() {
super.initData();
- groupDetailViewModel = new ViewModelProvider((AppCompatActivity)mContext).get(GroupDetailViewModel.class);
- groupDetailViewModel.getFetchMemberAttributesObservable().observe(this,response ->{
- if(response == null || isDestroy) {
+ groupDetailViewModel = new ViewModelProvider((AppCompatActivity) mContext).get(GroupDetailViewModel.class);
+ groupDetailViewModel.getFetchMemberAttributesObservable().observe(this, response -> {
+ if (response == null || isDestroy) {
return;
}
- if(response.status == Status.SUCCESS) {
+ if (response.status == Status.SUCCESS) {
chatLayout.getChatMessageListLayout().refreshMessages();
}
});
viewModel = new ViewModelProvider(this).get(ChatViewModel.class);
- viewModel.getTranslationObservable().observe(this,response ->{
- if(response == null || isDestroy) {
+ viewModel.getTranslationObservable().observe(this, response -> {
+ if (response == null || isDestroy) {
return;
}
- if(response.status == Status.SUCCESS) {
+ if (response.status == Status.SUCCESS) {
chatLayout.getChatMessageListLayout().refreshMessages();
- }else {
- EMLog.e("translationMessage","onError: " + response.errorCode + " - " + response.getMessage());
+ } else {
+ EMLog.e("translationMessage", "onError: " + response.errorCode + " - " + response.getMessage());
}
});
+ viewModel.pinMessageObservable().observe(this, response -> {
+ parseResource(response, new OnResourceParseCallback() {
+ @Override
+ public void onSuccess(ChatMessage message) {
+ updatePinMessage(message,ChatClient.getInstance().getCurrentUser());
+ }
+
+ @Override
+ public void onError(int code, String message) {
+ super.onError(code, message);
+ showToast(message);
+ }
+ });
+ });
LiveDataBus.get().with(DemoConstant.GROUP_MEMBER_ATTRIBUTE_CHANGE, EaseEvent.class).observe(getViewLifecycleOwner(), event -> {
- if(event == null || isDestroy) {
+ if (event == null || isDestroy) {
return;
}
chatLayout.getChatMessageListLayout().refreshMessages();
});
LiveDataBus.get().with(DemoConstant.MESSAGE_CHANGE_CHANGE, EaseEvent.class).observe(getViewLifecycleOwner(), event -> {
- if(event == null || isDestroy) {
+ if (event == null || isDestroy) {
return;
}
- if(event.isMessageChange()) {
+ if (event.isMessageChange()) {
chatLayout.getChatMessageListLayout().refreshMessages();
}
});
LiveDataBus.get().with(DemoConstant.EVENT_CHAT_MODEL_TO_NORMAL, EaseEvent.class).observe(this, event -> {
- if(event == null || isDestroy) {
+ if (event == null || isDestroy) {
return;
}
- if(event.type == EaseEvent.TYPE.NOTIFY && TextUtils.isEmpty(event.message)) {
+ if (event.type == EaseEvent.TYPE.NOTIFY && TextUtils.isEmpty(event.message)) {
IChatTopExtendMenu chatTopExtendMenu = chatLayout.getChatInputMenu().getChatTopExtendMenu();
- if(chatTopExtendMenu instanceof EaseChatMultiSelectView) {
+ if (chatTopExtendMenu instanceof EaseChatMultiSelectView) {
((EaseChatMultiSelectView) chatTopExtendMenu).dismissSelectView(null);
}
titleBar.setVisibility(View.GONE);
}
});
+
+ viewModel.getPinMessageObservable().observe(this, response -> {
+ parseResource(response, new OnResourceParseCallback>() {
+ @Override
+ public void onSuccess(List messages) {
+ if (CollectionUtils.isEmpty(messages)) {
+ pinInfoView.setVisibility(View.GONE);
+ } else {
+ pinInfoView.setData(messages);
+ }
+ }
+
+ @Override
+ public void onError(int code, String message) {
+ super.onError(code, message);
+ }
+ });
+ });
+
+ if (chatType != SINGLE_CHAT) {
+ viewModel.getPinnedMessagesFromServer(conversationId);
+ }
+ }
+
+ private void updatePinMessage(ChatMessage message,String operationUser) {
+ runOnUiThread(()->{
+ boolean isPined = message.pinnedInfo()==null||TextUtils.isEmpty(message.pinnedInfo().operatorId());
+ ToastUtils.showToast((isPined ? "unpin success" : "pin success"));
+ if(isPined){
+ pinInfoView.removeData(message);
+ }else{
+ pinInfoView.addData(message);
+ }
+ //insert pin message info in local
+ insertPinNotificationInLocal(message, operationUser);
+ chatLayout.getChatMessageListLayout().refreshToLatest();
+ });
+ }
+
+ private void insertPinNotificationInLocal(ChatMessage msg,String operationUser) {
+ ChatMessage msgNotification = ChatMessage.createReceiveMessage(ChatMessage.Type.TXT);
+ String content;
+ if(msg.pinnedInfo()==null||TextUtils.isEmpty(msg.pinnedInfo().operatorId())) {
+ content = operationUser+" removed a pin message";
+ }else{
+ content = operationUser+" pinned a message";
+ }
+ if(TextUtils.equals(operationUser, ChatClient.getInstance().getCurrentUser())){
+ content = content.replace(operationUser, "You");
+ }
+ TextMessageBody txtBody = new TextMessageBody(content);
+ msgNotification.addBody(txtBody);
+ msgNotification.setFrom(msg.getFrom());
+ msgNotification.setTo(msg.getTo());
+ msgNotification.setUnread(false);
+ msgNotification.setMsgTime(System.currentTimeMillis());
+ msgNotification.setLocalTime(System.currentTimeMillis());
+ msgNotification.setChatType(msg.getChatType());
+ //Just to reuse the recall layout
+ msgNotification.setAttribute(EaseConstant.MESSAGE_TYPE_RECALL, true);
+ msgNotification.setStatus(ChatMessage.Status.SUCCESS);
+ msgNotification.setIsChatThreadMessage(msg.isChatThreadMessage());
+ ChatClient.getInstance().chatManager().saveMessage(msgNotification);
}
@Override
@@ -174,11 +263,11 @@ public void initListener() {
super.initListener();
listenerRecyclerViewItemFinishLayout();
EditText editText = chatLayout.getChatInputMenu().getPrimaryMenu().getEditText();
- if (editText != null){
+ if (editText != null) {
editText.setOnKeyListener(new View.OnKeyListener() {
@Override
public boolean onKey(View v, int keyCode, KeyEvent event) {
- return removePickAt(v,keyCode,event);
+ return removePickAt(v, keyCode, event);
}
});
editText.addTextChangedListener(new TextWatcher() {
@@ -189,20 +278,20 @@ public void beforeTextChanged(CharSequence s, int start, int count, int after) {
@Override
public void onTextChanged(CharSequence s, int start, int before, int count) {
- if(!chatLayout.getChatMessageListLayout().isGroupChat()) {
+ if (!chatLayout.getChatMessageListLayout().isGroupChat()) {
return;
}
- if(count == 1 && "@".equals(String.valueOf(s.charAt(start)))){
+ if (count == 1 && "@".equals(String.valueOf(s.charAt(start)))) {
Bundle bundle = new Bundle();
bundle.putString(EaseConstant.EXTRA_CONVERSATION_ID, conversationId);
PickAtUserDialogFragment fragment = new PickAtUserDialogFragment();
fragment.setPickAtSelectListener(username -> {
- chatLayout.inputAtUsername(username,false);
+ chatLayout.inputAtUsername(username, false);
});
fragment.setArguments(bundle);
- if (getActivity() != null){
+ if (getActivity() != null) {
fragment.show(getActivity().getSupportFragmentManager(), "pick_at_user");
- if (getActivity() != null){
+ if (getActivity() != null) {
new Handler().postDelayed(new Runnable() {
@Override
public void run() {
@@ -210,7 +299,7 @@ public void run() {
editText.requestFocus();
imm.toggleSoftInput(InputMethodManager.SHOW_FORCED, 0);
}
- },200);
+ }, 200);
}
}
}
@@ -222,6 +311,53 @@ public void afterTextChanged(Editable editable) {
}
});
}
+
+ if(pinInfoView!=null){
+ pinInfoView.setOnItemClickListener(new PinMessageListViewGroup.OnItemClickListener() {
+ @Override
+ public void onItemClick(ChatMessage message) {
+ pinInfoView.restView();
+ //click for pin message list
+ List messageList = chatLayout.getChatMessageListLayout().getMessageAdapter().getData();
+ boolean isExist = false;
+ for (int i = 0; i < messageList.size(); i++) {
+ ChatMessage chatMessage = messageList.get(i);
+ if (chatMessage.getMsgId().equals(message.getMsgId())) {
+ isExist = true;
+ break;
+ }
+ }
+ if (!isExist) {
+ ToastUtils.showToast(getString(R.string.pin_skip_not_exist));
+ }else{
+ chatLayout.getChatMessageListLayout().moveToTarget(message);
+ }
+ }
+ });
+ pinInfoView.setOnItemSubViewClickListener(new EaseMessageAdapter.OnItemSubViewClickListener() {
+ @Override
+ public void onItemSubViewClick(View view, int position) {
+ ChatMessage message=pinInfoView.getPinMessages().get(position);
+ showUnPinConfirmDialog(message);
+ }
+ });
+ }
+
+ ChatClient.getInstance().chatManager().addMessageListener(this);
+ }
+
+ private void showUnPinConfirmDialog(ChatMessage message) {
+ new SimpleDialog.Builder(getActivity())
+ .setTitle(R.string.unpin_confirm_message)
+ .showCancelButton(true)
+ .hideConfirmButton(false)
+ .setOnConfirmClickListener(R.string.dialog_btn_to_confirm, new SimpleDialog.OnConfirmClickListener() {
+ @Override
+ public void onConfirmClick(View view) {
+ viewModel.pinMessage(message, false);
+ }
+ })
+ .show();
}
@@ -234,28 +370,46 @@ public void onViewCreated(@NonNull View view, @Nullable Bundle savedInstanceStat
@Override
public void initView() {
super.initView();
+ MenuItemBean pinItemBean = new MenuItemBean(0, R.id.action_chat_pin, 76, getResources().getString(R.string.ease_action_pin));
+ pinItemBean.setResourceId(R.drawable.chat_item_menu_pin);
MenuItemBean menuItemBean = new MenuItemBean(0, R.id.action_chat_report, 99, getResources().getString(R.string.ease_action_report));
menuItemBean.setResourceId(R.drawable.chat_item_menu_report);
- MenuItemBean menuTranslationBean = new MenuItemBean(0, R.id.action_chat_translation,88, getResources().getString(R.string.ease_action_translation));
+ MenuItemBean menuTranslationBean = new MenuItemBean(0, R.id.action_chat_translation, 88, getResources().getString(R.string.ease_action_translation));
menuTranslationBean.setResourceId(R.drawable.chat_item_menu_translation);
- MenuItemBean menuReTranslationBean = new MenuItemBean(0, R.id.action_chat_re_translation,111, getResources().getString(R.string.ease_action_re_translation));
+ MenuItemBean menuReTranslationBean = new MenuItemBean(0, R.id.action_chat_re_translation, 111, getResources().getString(R.string.ease_action_re_translation));
menuReTranslationBean.setResourceId(R.drawable.chat_item_menu_translation);
+ chatLayout.getMenuHelper().addItemMenu(pinItemBean);
chatLayout.getMenuHelper().addItemMenu(menuItemBean);
chatLayout.getMenuHelper().addItemMenu(menuTranslationBean);
chatLayout.getMenuHelper().addItemMenu(menuReTranslationBean);
chatLayout.setPresenter(new ChatCustomPresenter());
EaseMessageAdapter adapter = chatLayout.getChatMessageListLayout().getMessageAdapter();
- if (adapter instanceof CustomMessageAdapter){
- ((CustomMessageAdapter)adapter).setTranslationListener(new TranslationListener() {
+ if (adapter instanceof CustomMessageAdapter) {
+ ((CustomMessageAdapter) adapter).setTranslationListener(new TranslationListener() {
@Override
- public void onTranslationRetry(ChatMessage message,String languageCode) {
- if (message.getBody() instanceof TextMessageBody){
- translationMessage(message,languageCode);
+ public void onTranslationRetry(ChatMessage message, String languageCode) {
+ if (message.getBody() instanceof TextMessageBody) {
+ translationMessage(message, languageCode);
}
}
});
}
+
+ if(chatType!=SINGLE_CHAT){
+ pinInfoView = new PinInfoView(getContext());
+ RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.WRAP_CONTENT);
+ pinInfoView.setVisibility(View.GONE);
+ chatLayout.addView(pinInfoView, layoutParams);
+ chatLayout.post(new Runnable() {
+ @Override
+ public void run() {
+ pinInfoView.setInnerLayoutMaxHeight(chatLayout.getHeight()- UIUtils.dp2px(getContext(), 145));
+ }
+ });
+ }
}
@Override
@@ -290,14 +444,16 @@ public void onPreMenu(EasePopupWindowHelper helper, ChatMessage message) {
helper.findItemVisible(R.id.action_chat_translation, false);
helper.findItemVisible(R.id.action_chat_re_translation, false);
}
+ helper.findItem(R.id.action_chat_pin).setTitle((message.pinnedInfo()==null||TextUtils.isEmpty(message.pinnedInfo().operatorId())) ? getString(R.string.ease_action_pin):getString(R.string.ease_action_unpin));
+ helper.findItemVisible(R.id.action_chat_pin, chatType==SINGLE_CHAT?false:true);
}
@Override
public boolean onMenuItemClick(MenuItemBean item, ChatMessage message) {
- switch (item.getItemId()){
+ switch (item.getItemId()) {
case R.id.action_chat_report:
if (message.status() == ChatMessage.Status.SUCCESS)
- ChatReportActivity.actionStart(getActivity(),message.getMsgId());
+ ChatReportActivity.actionStart(getActivity(), message.getMsgId());
break;
case R.id.action_chat_select:
showSelectModelTitle();
@@ -306,19 +462,22 @@ public boolean onMenuItemClick(MenuItemBean item, ChatMessage message) {
case R.id.action_chat_translation:
case R.id.action_chat_re_translation:
translationMsg = message;
- if (!TextUtils.isEmpty(getPreferredLanguageCode())){
+ if (!TextUtils.isEmpty(getPreferredLanguageCode())) {
boolean enable = DemoHelper.getInstance().getModel().getDemandTranslationEnable();
- if (enable){
- translationMessage(message,getPreferredLanguageCode());
+ if (enable) {
+ translationMessage(message, getPreferredLanguageCode());
break;
- }else {
+ } else {
translationType = DemoConstant.TRANSLATION_DEMAND_ENABLE;
}
- }else {
+ } else {
translationType = DemoConstant.TRANSLATION_NO_LANGUAGE;
}
showTranslationDialog();
break;
+ case R.id.action_chat_pin:
+ viewModel.pinMessage(message, message.pinnedInfo()==null||TextUtils.isEmpty(message.pinnedInfo().operatorId()));
+ break;
}
return super.onMenuItemClick(item, message);
}
@@ -334,17 +493,17 @@ public boolean onChatExtendMenuItemClick(View view, int itemId) {
}
break;
case R.id.extend_item_picture:
- if(!PermissionCompat.checkMediaPermission(mContext, requestImagePermission, Manifest.permission.READ_MEDIA_IMAGES)) {
+ if (!PermissionCompat.checkMediaPermission(mContext, requestImagePermission, Manifest.permission.READ_MEDIA_IMAGES)) {
return true;
}
break;
case R.id.extend_item_video:
- if(!PermissionCompat.checkMediaPermission(mContext, requestVideoPermission, Manifest.permission.READ_MEDIA_VIDEO, Manifest.permission.CAMERA)) {
+ if (!PermissionCompat.checkMediaPermission(mContext, requestVideoPermission, Manifest.permission.READ_MEDIA_VIDEO, Manifest.permission.CAMERA)) {
return true;
}
break;
case R.id.extend_item_file:
- if(!PermissionCompat.checkMediaPermission(mContext, requestFilePermission, Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO)) {
+ if (!PermissionCompat.checkMediaPermission(mContext, requestFilePermission, Manifest.permission.READ_MEDIA_IMAGES, Manifest.permission.READ_MEDIA_VIDEO)) {
return true;
}
break;
@@ -353,16 +512,16 @@ public boolean onChatExtendMenuItemClick(View view, int itemId) {
}
private void onRequestResult(Map result, int requestCode) {
- if(result != null && result.size() > 0) {
+ if (result != null && result.size() > 0) {
for (Map.Entry entry : result.entrySet()) {
EMLog.e("chat", "onRequestResult: " + entry.getKey() + " " + entry.getValue());
}
- if(PermissionCompat.getMediaAccess(mContext) != PermissionCompat.StorageAccess.Denied) {
- if(requestCode == REQUEST_CODE_STORAGE_PICTURE) {
+ if (PermissionCompat.getMediaAccess(mContext) != PermissionCompat.StorageAccess.Denied) {
+ if (requestCode == REQUEST_CODE_STORAGE_PICTURE) {
selectPicFromLocal();
- }else if(requestCode == REQUEST_CODE_STORAGE_VIDEO) {
+ } else if (requestCode == REQUEST_CODE_STORAGE_VIDEO) {
selectVideoFromLocal();
- }else if(requestCode == REQUEST_CODE_STORAGE_FILE) {
+ } else if (requestCode == REQUEST_CODE_STORAGE_FILE) {
selectFileFromLocal();
}
}
@@ -378,9 +537,9 @@ private void showSelectModelTitle() {
titleBar.getIcon().setVisibility(View.VISIBLE);
titleBar.getLeftLayout().setVisibility(View.GONE);
ViewParent parent = titleBar.getTitle().getParent();
- if(parent instanceof ViewGroup) {
+ if (parent instanceof ViewGroup) {
ViewGroup.LayoutParams params = ((ViewGroup) parent).getLayoutParams();
- if(params instanceof RelativeLayout.LayoutParams) {
+ if (params instanceof RelativeLayout.LayoutParams) {
((RelativeLayout.LayoutParams) params).leftMargin = (int) EaseUtils.dip2px(mContext, 12);
}
}
@@ -390,9 +549,9 @@ public void onRightClick(View view) {
LiveDataBus.get().with(DemoConstant.EVENT_CHAT_MODEL_TO_NORMAL).postValue(EaseEvent.create(DemoConstant.EVENT_CHAT_MODEL_TO_NORMAL, EaseEvent.TYPE.NOTIFY));
}
});
- if(chatType != SINGLE_CHAT) {
+ if (chatType != SINGLE_CHAT) {
boolean hasProvided = DemoHelper.getInstance().setGroupInfo(mContext, conversationId, titleBar.getTitle(), titleBar.getIcon());
- if(!hasProvided) {
+ if (!hasProvided) {
setGroupInfo();
}
} else {
@@ -404,16 +563,16 @@ public void onRightClick(View view) {
private void setGroupInfo() {
String title = "";
- if(chatType == EaseChatType.GROUP_CHAT) {
+ if (chatType == EaseChatType.GROUP_CHAT) {
title = GroupHelper.getGroupName(conversationId);
titleBar.getIcon().setImageResource(R.drawable.icon);
- }else if(chatType == EaseChatType.CHATROOM) {
+ } else if (chatType == EaseChatType.CHATROOM) {
titleBar.getIcon().setImageResource(R.drawable.icon);
ChatRoom room = ChatClient.getInstance().chatroomManager().getChatRoom(conversationId);
- if(room == null) {
+ if (room == null) {
return;
}
- title = TextUtils.isEmpty(room.getName()) ? conversationId : room.getName();
+ title = TextUtils.isEmpty(room.getName()) ? conversationId : room.getName();
}
titleBar.getTitle().setText(title);
}
@@ -500,22 +659,22 @@ public void onModifyMessageSuccess(ChatMessage messageModified) {
}
- private void translationMessage(ChatMessage message,String language){
+ private void translationMessage(ChatMessage message, String language) {
List list = new ArrayList<>();
list.add(language);
- viewModel.translationMessage(message,list);
+ viewModel.translationMessage(message, list);
}
@Override
public void addMsgAttrsBeforeSend(ChatMessage message) {
super.addMsgAttrsBeforeSend(message);
String[] autoLanguage = TranslationHelper.getLanguageByType(DemoConstant.TRANSLATION_TYPE_AUTO, conversationId);
- if (!TextUtils.isEmpty(autoLanguage[0])){
- translationMessage(message,autoLanguage[0]);
+ if (!TextUtils.isEmpty(autoLanguage[0])) {
+ translationMessage(message, autoLanguage[0]);
}
}
- private void setPickAtContentStyle(Editable editable){
+ private void setPickAtContentStyle(Editable editable) {
Pattern pattern = Pattern.compile("@([^\\s]+)");
Matcher matcher = pattern.matcher(editable);
while (matcher.find()) {
@@ -528,18 +687,18 @@ private void setPickAtContentStyle(Editable editable){
}
}
- private boolean removePickAt(View v, int keyCode, KeyEvent event){
+ private boolean removePickAt(View v, int keyCode, KeyEvent event) {
if (keyCode == KeyEvent.KEYCODE_DEL && event.getAction() == KeyEvent.ACTION_DOWN && v instanceof EditText) {
- int selectionStart = ((EditText)v).getSelectionStart();
- int selectionEnd = ((EditText)v).getSelectionEnd();
- SpannableStringBuilder text = (SpannableStringBuilder) ((EditText)v).getText();
+ int selectionStart = ((EditText) v).getSelectionStart();
+ int selectionEnd = ((EditText) v).getSelectionEnd();
+ SpannableStringBuilder text = (SpannableStringBuilder) ((EditText) v).getText();
ForegroundColorSpan[] spans = text.getSpans(0, text.length(), ForegroundColorSpan.class);
for (ForegroundColorSpan span : spans) {
int spanStart = text.getSpanStart(span);
int spanEnd = text.getSpanEnd(span);
if (selectionStart >= spanStart && selectionEnd <= spanEnd) {
- if (spanStart != -1 && spanEnd != -1){
- text.delete(spanStart+1, spanEnd);
+ if (spanStart != -1 && spanEnd != -1) {
+ text.delete(spanStart + 1, spanEnd);
}
}
}
@@ -547,14 +706,16 @@ private boolean removePickAt(View v, int keyCode, KeyEvent event){
return false;
}
- private void showTranslationDialog(){
- if (translationType == 0){ return;}
+ private void showTranslationDialog() {
+ if (translationType == 0) {
+ return;
+ }
translationDialog = new AlertDialog.Builder(mContext)
.setContentView(R.layout.dialog_auto_translation)
.setText(R.id.tv_content,
translationType == DemoConstant.TRANSLATION_NO_LANGUAGE ?
getString(R.string.translation_auto_about_info)
- : getString(R.string.translation_unable)
+ : getString(R.string.translation_unable)
)
.setText(R.id.btn_ok, getString(R.string.translation_setting))
.setLayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewGroup.LayoutParams.WRAP_CONTENT)
@@ -564,11 +725,11 @@ private void showTranslationDialog(){
@Override
public void onClick(View v) {
Intent starter;
- if (translationType == DemoConstant.TRANSLATION_NO_LANGUAGE){
+ if (translationType == DemoConstant.TRANSLATION_NO_LANGUAGE) {
starter = new Intent(mContext, LanguageActivity.class);
starter.putExtra(DemoConstant.TRANSLATION_TYPE, DemoConstant.TRANSLATION_TYPE_MESSAGE);
starter.putExtra(DemoConstant.TRANSLATION_SELECT_MAX_COUNT, 1);
- }else {
+ } else {
starter = new Intent(mContext, TranslationSettingsActivity.class);
}
launcher.launch(starter);
@@ -583,7 +744,7 @@ public void onClick(View v) {
translationDialog.show();
}
- private String getPreferredLanguageCode(){
+ private String getPreferredLanguageCode() {
String[] language = TranslationHelper.getLanguageByType(DemoConstant.TRANSLATION_TYPE_MESSAGE, "");
return language[0];
}
@@ -597,8 +758,53 @@ public void onResume() {
@Override
public void onPause() {
super.onPause();
- if(mContext != null && mContext.isFinishing()) {
+ if (mContext != null && mContext.isFinishing()) {
isDestroy = true;
}
}
+
+ /**
+ * Parse Resource
+ *
+ * @param response
+ * @param callback
+ * @param
+ */
+ public void parseResource(Resource response, @NonNull OnResourceParseCallback callback) {
+ if (response == null) {
+ return;
+ }
+ if (response.status == Status.SUCCESS) {
+ callback.onHideLoading();
+ callback.onSuccess(response.data);
+ } else if (response.status == Status.ERROR) {
+ callback.onHideLoading();
+ callback.onError(response.errorCode, response.getMessage());
+ } else if (response.status == Status.LOADING) {
+ callback.onLoading(response.data);
+ }
+ }
+
+ @Override
+ public void onMessageReceived(List messages) {
+
+ }
+
+ @Override
+ public void onMessagePinChanged(String messageId, String conversationId, MessagePinInfo.PinOperation pinOperation, MessagePinInfo pinInfo) {
+ ChatMessage message = ChatClient.getInstance().chatManager().getMessage(messageId);
+ if(message!=null) {
+ updatePinMessage(message,pinInfo.operatorId());
+ }else{
+ viewModel.getPinnedMessagesFromServer(conversationId);
+ }
+ }
+
+
+ @Override
+ public void onDestroy() {
+ super.onDestroy();
+ ChatClient.getInstance().chatManager().removeMessageListener(this);
+ }
+
}
diff --git a/app/src/main/java/io/agora/chatdemo/chat/PinListItemSpaceDecoration.java b/app/src/main/java/io/agora/chatdemo/chat/PinListItemSpaceDecoration.java
new file mode 100644
index 00000000..6cc676f9
--- /dev/null
+++ b/app/src/main/java/io/agora/chatdemo/chat/PinListItemSpaceDecoration.java
@@ -0,0 +1,29 @@
+package io.agora.chatdemo.chat;
+
+import android.graphics.Rect;
+import android.view.View;
+
+import androidx.recyclerview.widget.RecyclerView;
+
+public class PinListItemSpaceDecoration extends RecyclerView.ItemDecoration {
+ private int space;
+
+ public PinListItemSpaceDecoration(int space) {
+ this.space = space;
+ }
+
+ @Override
+ public void getItemOffsets(Rect outRect, View view, RecyclerView parent, RecyclerView.State state) {
+ outRect.left = space;
+ outRect.right = space;
+ outRect.bottom = space;
+
+
+ if (parent.getChildAdapterPosition(view) == 0) {
+ outRect.top = space;
+ } else {
+ outRect.top = 0;
+ }
+ }
+}
+
diff --git a/app/src/main/java/io/agora/chatdemo/chat/adapter/PinMessageListAdapter.java b/app/src/main/java/io/agora/chatdemo/chat/adapter/PinMessageListAdapter.java
new file mode 100644
index 00000000..a8bf64ba
--- /dev/null
+++ b/app/src/main/java/io/agora/chatdemo/chat/adapter/PinMessageListAdapter.java
@@ -0,0 +1,44 @@
+package io.agora.chatdemo.chat.adapter;
+
+import android.view.LayoutInflater;
+import android.view.ViewGroup;
+
+import androidx.annotation.NonNull;
+
+import io.agora.chat.ChatMessage;
+import io.agora.chat.uikit.adapter.EaseBaseRecyclerViewAdapter;
+import io.agora.chatdemo.R;
+import io.agora.chatdemo.chat.viewholder.pinmessage.PinDefaultViewHolder;
+import io.agora.chatdemo.chat.viewholder.pinmessage.PinImageMessageViewHolder;
+import io.agora.chatdemo.chat.viewholder.pinmessage.PinTextMessageViewHolder;
+
+
+public class PinMessageListAdapter extends EaseBaseRecyclerViewAdapter {
+
+ @Override
+ public int getItemNotEmptyViewType(int position) {
+ return mData.get(position).getType().ordinal();
+ }
+
+ @Override
+ public ViewHolder getViewHolder(ViewGroup parent, int viewType) {
+ ViewHolder viewHolder ;
+ switch (ChatMessage.Type.values()[viewType]) {
+ case TXT:
+ viewHolder=new PinTextMessageViewHolder(mItemSubViewListener,LayoutInflater.from(mContext).inflate(R.layout.pinlist_text, parent, false));
+ break;
+ case IMAGE:
+ viewHolder=new PinImageMessageViewHolder(mItemSubViewListener,LayoutInflater.from(mContext).inflate(R.layout.pinlist_image, parent, false));
+ break;
+ default:
+ viewHolder=new PinDefaultViewHolder(mItemSubViewListener,LayoutInflater.from(mContext).inflate(R.layout.pinlist_default, parent, false));
+ break;
+ }
+ return viewHolder;
+ }
+
+ @Override
+ public void onBindViewHolder(@NonNull ViewHolder holder, int position) {
+ super.onBindViewHolder(holder, position);
+ }
+}
diff --git a/app/src/main/java/io/agora/chatdemo/chat/viewholder/pinmessage/PinDefaultViewHolder.java b/app/src/main/java/io/agora/chatdemo/chat/viewholder/pinmessage/PinDefaultViewHolder.java
new file mode 100644
index 00000000..6d55031a
--- /dev/null
+++ b/app/src/main/java/io/agora/chatdemo/chat/viewholder/pinmessage/PinDefaultViewHolder.java
@@ -0,0 +1,68 @@
+package io.agora.chatdemo.chat.viewholder.pinmessage;
+
+
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import io.agora.chat.ChatMessage;
+import io.agora.chat.FileMessageBody;
+import io.agora.chat.uikit.adapter.EaseBaseRecyclerViewAdapter;
+import io.agora.chatdemo.R;
+
+public class PinDefaultViewHolder extends EaseBaseRecyclerViewAdapter.ViewHolder {
+ private final EaseBaseRecyclerViewAdapter.OnItemSubViewClickListener mItemSubViewListener;
+ private TextView from;
+ private TextView content;
+ private TextView time;
+ private ImageView state;
+ public PinDefaultViewHolder(EaseBaseRecyclerViewAdapter.OnItemSubViewClickListener mItemSubViewListener, @NonNull View itemView) {
+ super(itemView);
+ this.mItemSubViewListener = mItemSubViewListener;
+ }
+
+ @Override
+ public void initView(View itemView) {
+ super.initView(itemView);
+ from = findViewById(R.id.tv_from);
+ content = findViewById(R.id.tv_content);
+ time = findViewById(R.id.tv_time);
+ state=findViewById(R.id.iv_state);
+ }
+
+ @Override
+ public void setData(ChatMessage message, int position) {
+ String operatorId = message.pinnedInfo().operatorId();
+ long pinTime = message.pinnedInfo().pinTime();
+
+ from.setText(operatorId +" pinned "+message.getFrom()+"'s message");
+ if(message.getType()== ChatMessage.Type.FILE) {
+ FileMessageBody body= (FileMessageBody) message.getBody();
+ content.setText("[File]"+body.displayName());
+ }else if(message.getType()== ChatMessage.Type.CUSTOM) {
+ content.setText("[Custom]");
+ }else{
+ content.setText("["+message.getType().name()+"]");
+ }
+
+ SimpleDateFormat sdf = new SimpleDateFormat("MM-dd, HH:mm");
+ sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
+ String formattedDate = sdf.format(new Date(pinTime));
+ time.setText(formattedDate);
+
+ state.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if(mItemSubViewListener!=null) {
+ mItemSubViewListener.onItemSubViewClick(v, position);
+ }
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/io/agora/chatdemo/chat/viewholder/pinmessage/PinImageMessageViewHolder.java b/app/src/main/java/io/agora/chatdemo/chat/viewholder/pinmessage/PinImageMessageViewHolder.java
new file mode 100644
index 00000000..9dbcb0fb
--- /dev/null
+++ b/app/src/main/java/io/agora/chatdemo/chat/viewholder/pinmessage/PinImageMessageViewHolder.java
@@ -0,0 +1,65 @@
+package io.agora.chatdemo.chat.viewholder.pinmessage;
+
+
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import com.bumptech.glide.Glide;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import io.agora.chat.ChatMessage;
+import io.agora.chat.ImageMessageBody;
+import io.agora.chat.uikit.adapter.EaseBaseRecyclerViewAdapter;
+import io.agora.chatdemo.R;
+
+public class PinImageMessageViewHolder extends EaseBaseRecyclerViewAdapter.ViewHolder {
+ private final EaseBaseRecyclerViewAdapter.OnItemSubViewClickListener mItemSubViewListener;
+ private TextView from;
+ private ImageView content;
+ private TextView time;
+ private ImageView state;
+ public PinImageMessageViewHolder(EaseBaseRecyclerViewAdapter.OnItemSubViewClickListener mItemSubViewListener, @NonNull View itemView) {
+ super(itemView);
+ this.mItemSubViewListener = mItemSubViewListener;
+ }
+
+ @Override
+ public void initView(View itemView) {
+ super.initView(itemView);
+ from = findViewById(R.id.tv_from);
+ content = findViewById(R.id.iv_content);
+ time = findViewById(R.id.tv_time);
+ state=findViewById(R.id.iv_state);
+ }
+
+ @Override
+ public void setData(ChatMessage message, int position) {
+ String operatorId = message.pinnedInfo().operatorId();
+ long pinTime = message.pinnedInfo().pinTime();
+
+ from.setText(operatorId +" pinned "+message.getFrom()+"'s message");
+
+ ImageMessageBody body = (ImageMessageBody) message.getBody();
+ Glide.with(itemView.getContext()).load(body.getRemoteUrl()).into(content);
+
+ SimpleDateFormat sdf = new SimpleDateFormat("MM-dd, HH:mm");
+ sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
+ String formattedDate = sdf.format(new Date(pinTime));
+ time.setText(formattedDate);
+
+ state.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if(mItemSubViewListener!=null) {
+ mItemSubViewListener.onItemSubViewClick(v, position);
+ }
+ }
+ });
+ }
+}
diff --git a/app/src/main/java/io/agora/chatdemo/chat/viewholder/pinmessage/PinTextMessageViewHolder.java b/app/src/main/java/io/agora/chatdemo/chat/viewholder/pinmessage/PinTextMessageViewHolder.java
new file mode 100644
index 00000000..c713744f
--- /dev/null
+++ b/app/src/main/java/io/agora/chatdemo/chat/viewholder/pinmessage/PinTextMessageViewHolder.java
@@ -0,0 +1,68 @@
+package io.agora.chatdemo.chat.viewholder.pinmessage;
+
+
+import android.view.View;
+import android.widget.ImageView;
+import android.widget.TextView;
+
+import androidx.annotation.NonNull;
+
+import java.text.SimpleDateFormat;
+import java.util.Date;
+import java.util.TimeZone;
+
+import io.agora.chat.ChatMessage;
+import io.agora.chat.TextMessageBody;
+import io.agora.chat.uikit.adapter.EaseBaseRecyclerViewAdapter;
+import io.agora.chatdemo.R;
+
+public class PinTextMessageViewHolder extends EaseBaseRecyclerViewAdapter.ViewHolder {
+
+ private final EaseBaseRecyclerViewAdapter.OnItemSubViewClickListener mItemSubViewListener;
+ private TextView from;
+ private TextView content;
+ private TextView time;
+ private ImageView state;
+
+ public PinTextMessageViewHolder(EaseBaseRecyclerViewAdapter.OnItemSubViewClickListener mItemSubViewListener, @NonNull View itemView) {
+ super(itemView);
+ this.mItemSubViewListener = mItemSubViewListener;
+ }
+
+ @Override
+ public void initView(View itemView) {
+ super.initView(itemView);
+ from = findViewById(R.id.tv_from);
+ content = findViewById(R.id.tv_content);
+ time = findViewById(R.id.tv_time);
+ state=findViewById(R.id.iv_state);
+
+ }
+
+ @Override
+ public void setData(ChatMessage message, int position) {
+ String operatorId = message.pinnedInfo().operatorId();
+ long pinTime = message.pinnedInfo().pinTime();
+
+ from.setText(operatorId +" pinned "+message.getFrom()+"'s message");
+
+ TextMessageBody body = (TextMessageBody) message.getBody();
+ content.setText(body.getMessage());
+
+ SimpleDateFormat sdf = new SimpleDateFormat("MM-dd, HH:mm");
+ sdf.setTimeZone(TimeZone.getTimeZone("GMT+8"));
+ String formattedDate = sdf.format(new Date(pinTime));
+ time.setText(formattedDate);
+
+ state.setOnClickListener(new View.OnClickListener() {
+ @Override
+ public void onClick(View v) {
+ if(mItemSubViewListener!=null) {
+ mItemSubViewListener.onItemSubViewClick(v, position);
+ }
+ }
+ });
+
+
+ }
+}
diff --git a/app/src/main/java/io/agora/chatdemo/chat/viewmodel/ChatViewModel.java b/app/src/main/java/io/agora/chatdemo/chat/viewmodel/ChatViewModel.java
index 7311714d..8684ec3f 100644
--- a/app/src/main/java/io/agora/chatdemo/chat/viewmodel/ChatViewModel.java
+++ b/app/src/main/java/io/agora/chatdemo/chat/viewmodel/ChatViewModel.java
@@ -29,9 +29,11 @@ public class ChatViewModel extends AndroidViewModel {
private SingleSourceLiveData> makeConversationReadObservable;
private SingleSourceLiveData>> getNoPushUsersObservable;
private SingleSourceLiveData> setNoPushUsersObservable;
- private SingleSourceLiveData> chatManagerObservable;
+ private SingleSourceLiveData> reportMessageObservable;
private SingleSourceLiveData> removeMessagesObservable;
private SingleSourceLiveData> translationMessagesObservable;
+ private SingleSourceLiveData> pinMessageObservable;
+ private SingleSourceLiveData>> getPinMessageObservable;
public ChatViewModel(@NonNull Application application) {
super(application);
@@ -43,9 +45,11 @@ public ChatViewModel(@NonNull Application application) {
getNoPushUsersObservable = new SingleSourceLiveData<>();
setNoPushUsersObservable = new SingleSourceLiveData<>();
presenceObservable = new SingleSourceLiveData<>();
- chatManagerObservable = new SingleSourceLiveData<>();
+ reportMessageObservable = new SingleSourceLiveData<>();
removeMessagesObservable = new SingleSourceLiveData<>();
translationMessagesObservable = new SingleSourceLiveData<>();
+ pinMessageObservable = new SingleSourceLiveData<>();
+ getPinMessageObservable = new SingleSourceLiveData<>();
}
public LiveData>> getPresenceObservable(){
return presenceObservable;
@@ -56,8 +60,8 @@ public void fetchPresenceStatus(List userIds){
public LiveData> getChatRoomObservable() {
return chatRoomObservable;
}
- public LiveData> getChatManagerObservable(){
- return chatManagerObservable;
+ public LiveData> getReportMessageObservable(){
+ return reportMessageObservable;
}
public LiveData>> getNoPushUsersObservable() {
return getNoPushUsersObservable;
@@ -104,7 +108,7 @@ public LiveData> getMakeConversationReadObservable() {
}
public void reportMessage(String reportMsgId, String reportType, String reportReason ){
- chatManagerObservable.setSource(chatManagerRepository.reportMessage(reportMsgId,reportType,reportReason));
+ reportMessageObservable.setSource(chatManagerRepository.reportMessage(reportMsgId,reportType,reportReason));
}
public LiveData> getRemoveMessagesObservable() {
@@ -129,4 +133,19 @@ public void removeMessagesFromServer(String conversationId, Conversation.Convers
public void translationMessage(ChatMessage message,List targetLanguage){
translationMessagesObservable.setSource(chatManagerRepository.translationMessage(message,targetLanguage));
}
+
+ public void pinMessage(ChatMessage message,boolean isPinned) {
+ pinMessageObservable.setSource(chatManagerRepository.pinMessage(message,isPinned));
+ }
+
+ public LiveData> pinMessageObservable(){
+ return pinMessageObservable;
+ }
+
+ public void getPinnedMessagesFromServer(String conversationId) {
+ getPinMessageObservable.setSource(chatManagerRepository.getPinnedMessagesFromServer(conversationId));
+ }
+ public LiveData>> getPinMessageObservable(){
+ return getPinMessageObservable;
+ }
}
diff --git a/app/src/main/java/io/agora/chatdemo/conversation/ConversationListFragment.java b/app/src/main/java/io/agora/chatdemo/conversation/ConversationListFragment.java
index 3cfaa511..a72238f1 100644
--- a/app/src/main/java/io/agora/chatdemo/conversation/ConversationListFragment.java
+++ b/app/src/main/java/io/agora/chatdemo/conversation/ConversationListFragment.java
@@ -114,7 +114,6 @@ public void afterTextChanged(Editable s) {
presenceView = titleBarLayout.findViewById(R.id.presence_view);
if(presenceView != null) {
presenceView.setVisibility(View.VISIBLE);
- presenceView.setPresenceTextViewArrowVisible(true);
presenceView.setNameTextViewVisibility(View.INVISIBLE);
RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) presenceView.getLayoutParams();
params.setMargins(UIUtils.dp2px(mContext, 16), 0, 0, 0);
diff --git a/app/src/main/java/io/agora/chatdemo/general/dialog/SimpleDialog.java b/app/src/main/java/io/agora/chatdemo/general/dialog/SimpleDialog.java
index 6f1d523d..98b7029c 100644
--- a/app/src/main/java/io/agora/chatdemo/general/dialog/SimpleDialog.java
+++ b/app/src/main/java/io/agora/chatdemo/general/dialog/SimpleDialog.java
@@ -19,9 +19,9 @@
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.annotation.StringRes;
-import androidx.appcompat.app.AppCompatActivity;
import androidx.constraintlayout.widget.Group;
import androidx.core.content.ContextCompat;
+import androidx.fragment.app.FragmentActivity;
import androidx.fragment.app.FragmentTransaction;
import java.lang.reflect.Field;
@@ -256,12 +256,12 @@ public T getViewById(int viewId) {
}
public static class Builder {
- public AppCompatActivity context;
+ public FragmentActivity context;
private OnConfirmClickListener listener;
private onCancelClickListener cancelClickListener;
protected Bundle bundle;
- public Builder(AppCompatActivity context) {
+ public Builder(FragmentActivity context) {
this.context = context;
this.bundle = new Bundle();
}
diff --git a/app/src/main/java/io/agora/chatdemo/general/manager/UsersManager.java b/app/src/main/java/io/agora/chatdemo/general/manager/UsersManager.java
index 78583b23..394fc92c 100644
--- a/app/src/main/java/io/agora/chatdemo/general/manager/UsersManager.java
+++ b/app/src/main/java/io/agora/chatdemo/general/manager/UsersManager.java
@@ -281,6 +281,7 @@ public void updateUserPresenceView(String username,EasePresenceView presenceView
Presence presence = DemoHelper.getInstance().getPresences().get(username);
if(presence!=null && presenceView != null) {
presenceView.setVisibility(View.VISIBLE);
+ presenceView.setPresenceTextViewArrowVisible(true);
presenceView.setPresenceData(getUserInfo(username).getAvatar(),presence);
}
}
diff --git a/app/src/main/java/io/agora/chatdemo/general/repositories/EMChatManagerRepository.java b/app/src/main/java/io/agora/chatdemo/general/repositories/EMChatManagerRepository.java
index 02ef2fd1..92e984ae 100644
--- a/app/src/main/java/io/agora/chatdemo/general/repositories/EMChatManagerRepository.java
+++ b/app/src/main/java/io/agora/chatdemo/general/repositories/EMChatManagerRepository.java
@@ -277,4 +277,56 @@ public void onError(int error, String errorMsg) {
}.asLiveData();
}
+ public LiveData> pinMessage(@NonNull ChatMessage message,boolean isPined) {
+ return new NetworkOnlyResource() {
+ @Override
+ protected void createCall(@NonNull ResultCallBack> callBack) {
+ if(isPined) {
+ getChatManager().asyncPinMessage(message.getMsgId(), new CallBack() {
+ @Override
+ public void onSuccess() {
+ callBack.onSuccess(createLiveData(message));
+ }
+
+ @Override
+ public void onError(int code, String error) {
+ callBack.onError(code, error);
+ }
+ });
+ }else{
+ getChatManager().asyncUnPinMessage(message.getMsgId(), new CallBack() {
+ @Override
+ public void onSuccess() {
+ callBack.onSuccess(createLiveData(message));
+ }
+
+ @Override
+ public void onError(int code, String error) {
+ callBack.onError(code, error);
+ }
+ });
+ }
+
+ }
+ }.asLiveData();
+ }
+
+ public LiveData>> getPinnedMessagesFromServer(String conversationId) {
+ return new NetworkOnlyResource>() {
+ @Override
+ protected void createCall(@NonNull ResultCallBack>> callBack) {
+ getChatManager().asyncGetPinnedMessagesFromServer(conversationId, new ValueCallBack>() {
+ @Override
+ public void onSuccess(List value) {
+ callBack.onSuccess(createLiveData(value));
+ }
+
+ @Override
+ public void onError(int error, String errorMsg) {
+ callBack.onError(error,errorMsg);
+ }
+ });
+ }
+ }.asLiveData();
+ }
}
diff --git a/app/src/main/java/io/agora/chatdemo/general/repositories/EMClientRepository.java b/app/src/main/java/io/agora/chatdemo/general/repositories/EMClientRepository.java
index d5c0cd9e..7448f6e3 100644
--- a/app/src/main/java/io/agora/chatdemo/general/repositories/EMClientRepository.java
+++ b/app/src/main/java/io/agora/chatdemo/general/repositories/EMClientRepository.java
@@ -41,7 +41,6 @@
import io.agora.chatdemo.general.net.ErrorCode;
import io.agora.chatdemo.general.net.Resource;
import io.agora.chatdemo.general.utils.CommonUtils;
-import io.agora.chatdemo.sign.SignInActivity;
import io.agora.cloud.HttpClientManager;
import io.agora.cloud.HttpResponse;
import io.agora.exceptions.ChatException;
@@ -408,7 +407,8 @@ protected void createCall(@NonNull ResultCallBack> callBack) {
ChatClient.getInstance().login(username, pwd, new CallBack() {
@Override
public void onSuccess() {
- success(username, callBack);
+ DemoHelper.getInstance().getUsersManager().setCurrentUser(username);
+ success(pwd, callBack);
}
@Override
diff --git a/app/src/main/java/io/agora/chatdemo/general/widget/PinInfoView.java b/app/src/main/java/io/agora/chatdemo/general/widget/PinInfoView.java
new file mode 100644
index 00000000..7818670c
--- /dev/null
+++ b/app/src/main/java/io/agora/chatdemo/general/widget/PinInfoView.java
@@ -0,0 +1,118 @@
+package io.agora.chatdemo.general.widget;
+
+
+import android.content.Context;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.View;
+import android.view.ViewGroup;
+import android.widget.RelativeLayout;
+
+import com.google.android.gms.common.util.CollectionUtils;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import io.agora.chat.ChatMessage;
+import io.agora.chat.uikit.adapter.EaseBaseRecyclerViewAdapter;
+import io.agora.chatdemo.R;
+import io.agora.util.EMLog;
+
+public class PinInfoView extends RelativeLayout {
+
+ private List pinMessages=new ArrayList<>();
+ private View primaryView;
+ private PinMessageListViewGroup pinMessageListView;
+
+ public PinInfoView(Context context) {
+ super(context);
+ init();
+ }
+
+ public PinInfoView(Context context, AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ primaryView = LayoutInflater.from(getContext()).inflate(R.layout.pin_info_view, this, false);
+ addView(primaryView);
+ findViewById(R.id.tv_info2).setOnClickListener(v->{
+ showPinListView();
+ });
+
+ pinMessageListView = new PinMessageListViewGroup(getContext());
+ RelativeLayout.LayoutParams layoutParams = new RelativeLayout.LayoutParams(
+ RelativeLayout.LayoutParams.MATCH_PARENT,
+ ViewGroup.LayoutParams.MATCH_PARENT);
+ pinMessageListView.setVisibility(View.GONE);
+ addView(pinMessageListView, layoutParams);
+ }
+
+ private void showPinListView() {
+ primaryView.setVisibility(GONE);
+ pinMessageListView.show(pinMessages);
+ }
+ private void showPrimaryView() {
+ setVisibility(VISIBLE);
+ primaryView.setVisibility(VISIBLE);
+ pinMessageListView.setVisibility(GONE);
+ }
+
+ public void setData(List messages) {
+ pinMessages.clear();
+ if(!CollectionUtils.isEmpty(messages)) {
+ pinMessages.addAll(0,messages);
+ }
+ setVisibility(VISIBLE);
+ }
+
+ public void removeData(ChatMessage message) {
+ if(message!=null) {
+ for (int i = 0; i < pinMessages.size(); i++) {
+ if(pinMessages.get(i).getMsgId().equals(message.getMsgId())) {
+ pinMessages.remove(message);
+ break;
+ }
+ }
+ pinMessageListView.removeData(message);
+ if(pinMessages.isEmpty()) {
+ setVisibility(GONE);
+ }
+ }
+ }
+
+ public void addData(ChatMessage message) {
+ if(message!=null) {
+ pinMessages.add(0,message);
+ }
+ showPrimaryView();
+ }
+
+ public void restView() {
+ primaryView.setVisibility(VISIBLE);
+ pinMessageListView.setVisibility(GONE);
+ }
+
+ public List getPinMessages() {
+ return pinMessages;
+ }
+
+ public void setOnItemClickListener(PinMessageListViewGroup.OnItemClickListener listener) {
+ pinMessageListView.setOnItemClickListener(listener);
+ }
+
+ public void setOnItemSubViewClickListener(EaseBaseRecyclerViewAdapter.OnItemSubViewClickListener onItemSubViewClickListener) {
+ pinMessageListView.setOnItemSubViewClickListener(onItemSubViewClickListener);
+ }
+
+ public void setInnerLayoutMaxHeight(int height) {
+ if(pinMessageListView!=null) {
+ EMLog.d("PinInfoView", "setInnerLayoutMaxHeight: " + height);
+ pinMessageListView.setConstraintLayoutMaxHeight(height);
+ }
+ }
+}
+
+
+
diff --git a/app/src/main/java/io/agora/chatdemo/general/widget/PinMessageListViewGroup.java b/app/src/main/java/io/agora/chatdemo/general/widget/PinMessageListViewGroup.java
new file mode 100644
index 00000000..779de9e4
--- /dev/null
+++ b/app/src/main/java/io/agora/chatdemo/general/widget/PinMessageListViewGroup.java
@@ -0,0 +1,164 @@
+package io.agora.chatdemo.general.widget;
+
+import android.content.Context;
+import android.graphics.Color;
+import android.util.AttributeSet;
+import android.view.LayoutInflater;
+import android.view.MotionEvent;
+import android.view.View;
+import android.widget.LinearLayout;
+import android.widget.TextView;
+
+import androidx.annotation.Nullable;
+import androidx.constraintlayout.widget.ConstraintLayout;
+import androidx.constraintlayout.widget.ConstraintSet;
+import androidx.recyclerview.widget.LinearLayoutManager;
+
+import com.google.android.gms.common.util.CollectionUtils;
+
+import java.util.List;
+
+import io.agora.chat.ChatMessage;
+import io.agora.chat.uikit.adapter.EaseBaseRecyclerViewAdapter;
+import io.agora.chat.uikit.widget.EaseRecyclerView;
+import io.agora.chatdemo.R;
+import io.agora.chatdemo.chat.PinListItemSpaceDecoration;
+import io.agora.chatdemo.chat.adapter.PinMessageListAdapter;
+
+public class PinMessageListViewGroup extends LinearLayout {
+
+ private ConstraintLayout constraintLayout;
+ private EaseRecyclerView recyclerView;
+ private PinMessageListAdapter adapter;
+ private OnItemClickListener itemClickListener;
+ private TextView tvCount;
+ private View clBottom;
+ private EaseBaseRecyclerViewAdapter.OnItemSubViewClickListener itemSubViewClickListener;
+
+ public PinMessageListViewGroup(Context context) {
+ super(context);
+ init();
+ }
+
+ public PinMessageListViewGroup(Context context, @Nullable AttributeSet attrs) {
+ super(context, attrs);
+ init();
+ }
+
+ private void init() {
+ setClickable(true);
+ setBackgroundColor(Color.parseColor("#80000000"));
+
+ constraintLayout = (ConstraintLayout) LayoutInflater.from(getContext()).inflate(R.layout.pin_message_list_view_group, this, false);
+ addView(constraintLayout);
+
+ tvCount = findViewById(R.id.tv_count);
+ recyclerView = findViewById(R.id.rv_list);
+ clBottom = findViewById(R.id.cl_bottom);
+
+
+ adapter = new PinMessageListAdapter();
+
+
+ int space = dpToPx(8);
+ PinListItemSpaceDecoration itemDecoration = new PinListItemSpaceDecoration(space);
+ recyclerView.addItemDecoration(itemDecoration);
+ recyclerView.setLayoutManager(new LinearLayoutManager(getContext()));
+
+ adapter.setOnItemClickListener(new io.agora.chat.uikit.interfaces.OnItemClickListener() {
+ @Override
+ public void onItemClick(View view, int position) {
+ if (itemClickListener != null) {
+ itemClickListener.onItemClick(adapter.getItem(position));
+ }
+ }
+ });
+ adapter.setOnItemSubViewClickListener(new EaseBaseRecyclerViewAdapter.OnItemSubViewClickListener() {
+ @Override
+ public void onItemSubViewClick(View view, int position) {
+ if (itemSubViewClickListener != null) {
+ itemSubViewClickListener.onItemSubViewClick(view, position);
+ }
+ }
+ });
+
+ recyclerView.setAdapter(adapter);
+
+ }
+
+ long startY = 0;
+ @Override
+ public boolean dispatchTouchEvent(MotionEvent event) {
+ switch (event.getAction()) {
+ case MotionEvent.ACTION_DOWN:
+ startY = (long) event.getY();
+ break;
+ case MotionEvent.ACTION_UP:
+ if (!recyclerView.canScrollVertically(-1) && startY-event.getY()> 20) {
+ ((PinInfoView) getParent()).restView();
+ return true;
+ }
+ if (event.getX() < 0 || event.getX() > getWidth() ||
+ event.getY() < 0 || event.getY() > getHeight()
+ || event.getY() > clBottom.getTop()) {
+ ((PinInfoView) getParent()).restView();
+ return true;
+ }
+ break;
+ }
+ return super.dispatchTouchEvent(event);
+ }
+
+ public void setData(List data) {
+ tvCount.setText(String.valueOf(data.size()) + " Pin Message");
+ adapter.setData(data);
+ }
+
+ public void setOnItemClickListener(OnItemClickListener listener) {
+ this.itemClickListener = listener;
+ }
+
+ public void setOnItemSubViewClickListener(EaseBaseRecyclerViewAdapter.OnItemSubViewClickListener listener) {
+ this.itemSubViewClickListener = listener;
+ }
+
+ public void show(List messages) {
+ setVisibility(VISIBLE);
+ setData(messages);
+ }
+
+ public void removeData(ChatMessage message) {
+ List messageList = adapter.getData();
+ if (messageList != null && message != null) {
+ for (int i = 0; i < messageList.size(); i++) {
+ if (messageList.get(i).getMsgId().equals(message.getMsgId())) {
+ messageList.remove(message);
+ break;
+ }
+ }
+ adapter.notifyDataSetChanged();
+ }
+ if (CollectionUtils.isEmpty(adapter.getData())) {
+ setVisibility(GONE);
+ }
+ }
+
+ public void setConstraintLayoutMaxHeight(int height) {
+ ConstraintSet constraintSet = new ConstraintSet();
+ constraintSet.clone(constraintLayout);
+ int recyclerViewId = R.id.rv_list;
+ int rvHeight = height - tvCount.getHeight() - clBottom.getHeight();
+ constraintSet.constrainMaxHeight(recyclerViewId, rvHeight);
+ constraintSet.applyTo(constraintLayout);
+ }
+
+ public interface OnItemClickListener {
+ void onItemClick(ChatMessage message);
+ }
+
+ private int dpToPx(int dp) {
+ float density = getResources().getDisplayMetrics().density;
+ return (int) (dp * density + 0.5f);
+ }
+}
+
diff --git a/app/src/main/res/drawable-xxhdpi/chat_item_menu_pin.png b/app/src/main/res/drawable-xxhdpi/chat_item_menu_pin.png
new file mode 100644
index 00000000..63482071
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/chat_item_menu_pin.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/chat_pin_onlight.png b/app/src/main/res/drawable-xxhdpi/chat_pin_onlight.png
new file mode 100644
index 00000000..168700d7
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/chat_pin_onlight.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/chat_pin_rectangle.png b/app/src/main/res/drawable-xxhdpi/chat_pin_rectangle.png
new file mode 100644
index 00000000..74b1e283
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/chat_pin_rectangle.png differ
diff --git a/app/src/main/res/drawable-xxhdpi/chat_pininfo_icon.png b/app/src/main/res/drawable-xxhdpi/chat_pininfo_icon.png
new file mode 100644
index 00000000..9d966f0f
Binary files /dev/null and b/app/src/main/res/drawable-xxhdpi/chat_pininfo_icon.png differ
diff --git a/app/src/main/res/drawable/shape_gray_ebebeb_corner_8.xml b/app/src/main/res/drawable/shape_gray_ebebeb_corner_8.xml
new file mode 100644
index 00000000..7435f4ae
--- /dev/null
+++ b/app/src/main/res/drawable/shape_gray_ebebeb_corner_8.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/drawable/shape_gray_f5f5f5_corner_8.xml b/app/src/main/res/drawable/shape_gray_f5f5f5_corner_8.xml
new file mode 100644
index 00000000..51bef490
--- /dev/null
+++ b/app/src/main/res/drawable/shape_gray_f5f5f5_corner_8.xml
@@ -0,0 +1,7 @@
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/pin_info_view.xml b/app/src/main/res/layout/pin_info_view.xml
new file mode 100644
index 00000000..c4a475be
--- /dev/null
+++ b/app/src/main/res/layout/pin_info_view.xml
@@ -0,0 +1,40 @@
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/pin_message_list_view_group.xml b/app/src/main/res/layout/pin_message_list_view_group.xml
new file mode 100644
index 00000000..89f66640
--- /dev/null
+++ b/app/src/main/res/layout/pin_message_list_view_group.xml
@@ -0,0 +1,46 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/pinlist_default.xml b/app/src/main/res/layout/pinlist_default.xml
new file mode 100644
index 00000000..313a185b
--- /dev/null
+++ b/app/src/main/res/layout/pinlist_default.xml
@@ -0,0 +1,52 @@
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/pinlist_image.xml b/app/src/main/res/layout/pinlist_image.xml
new file mode 100644
index 00000000..0ad95f3d
--- /dev/null
+++ b/app/src/main/res/layout/pinlist_image.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/layout/pinlist_text.xml b/app/src/main/res/layout/pinlist_text.xml
new file mode 100644
index 00000000..0d0eac24
--- /dev/null
+++ b/app/src/main/res/layout/pinlist_text.xml
@@ -0,0 +1,53 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/app/src/main/res/values/colors.xml b/app/src/main/res/values/colors.xml
index 128d8f9a..379132dd 100644
--- a/app/src/main/res/values/colors.xml
+++ b/app/src/main/res/values/colors.xml
@@ -70,6 +70,8 @@
#979797
#999999
#E6E6E6
+ #F5F5F5
+ #EbEbEb
#EBF2FF
diff --git a/app/src/main/res/values/ids.xml b/app/src/main/res/values/ids.xml
index 8bf1a06a..f376d544 100644
--- a/app/src/main/res/values/ids.xml
+++ b/app/src/main/res/values/ids.xml
@@ -15,4 +15,5 @@
+
\ No newline at end of file
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 17ad949e..92d2a273 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -403,6 +403,8 @@
%d/10
0/10
Report
+ Pin
+ UnPin
Delete %1$d messages
My Alias in Group
@@ -444,5 +446,8 @@
Translation failed Retry
Translated by Agora Chat View Original Text
Translated by Agora Chat View Translation
+ Pin Message
+ Confirm to remove pinned message?
+ The quoted message does not exist
\ No newline at end of file
diff --git a/settings.gradle b/settings.gradle
index 3d4a09ce..09cf7fd3 100644
--- a/settings.gradle
+++ b/settings.gradle
@@ -4,4 +4,6 @@ include ':app'
//include ':chat-callkit'
//project(':chat-callkit').projectDir = new File('../AgoraChat-CallKit-android/chat-callkit')
//include ':hyphenatechatsdk'
-//project(':hyphenatechatsdk').projectDir = new File('../emclient-android/hyphenatechatsdk')
\ No newline at end of file
+//project(':hyphenatechatsdk').projectDir = new File('../emclient-android/hyphenatechatsdk')
+//include ':ease-linux'
+//project(':ease-linux').projectDir = new File('../emclient-linux')
\ No newline at end of file