From a644db35fa915cba3b6830f6cff98e32b3a4e241 Mon Sep 17 00:00:00 2001 From: Doo Date: Wed, 26 Jan 2022 14:56:06 +0900 Subject: [PATCH] Added 2.2.3 --- CHANGELOG.md | 5 + gradle.properties | 2 +- uikit-custom-sample/build.gradle | 2 +- .../uikit/customsample/SettingsFragment.java | 6 +- .../community/CreateCommunityActivity.java | 3 +- uikit-sample/build.gradle | 2 +- .../SettingsFragment.java | 6 +- .../community/CreateCommunityActivity.java | 3 +- uikit/build.gradle | 2 +- .../uikit/fragments/BannedListFragment.java | 2 +- .../uikit/fragments/ChannelFragment.java | 283 +++++++++++------- .../uikit/fragments/ChannelListFragment.java | 5 +- .../fragments/ChannelSettingsFragment.java | 6 +- .../uikit/fragments/DialogListAdapter.java | 10 +- .../sendbird/uikit/fragments/DialogView.java | 4 +- .../uikit/fragments/MemberListFragment.java | 3 +- .../fragments/MemberTypeListFragment.java | 2 +- .../uikit/fragments/MessageAnchorDialog.java | 12 +- .../fragments/MessageSearchFragment.java | 2 +- .../fragments/MutedMemberListFragment.java | 2 +- .../uikit/fragments/OpenChannelFragment.java | 242 +++++++++------ .../OpenChannelSettingsFragment.java | 6 +- .../uikit/fragments/OperatorListFragment.java | 2 +- .../fragments/ParticipantsListFragment.java | 2 +- .../uikit/fragments/SelectUserFragment.java | 2 +- .../fragments/SendBirdDialogFragment.java | 4 +- .../uikit/fragments/UserTypeListFragment.java | 2 +- .../sendbird/uikit/model/DialogListItem.java | 59 +++- .../com/sendbird/uikit/utils/DialogUtils.java | 6 +- .../uikit/vm/ChannelListViewModel.java | 6 + .../sendbird/uikit/vm/ChannelViewModel.java | 82 ++--- .../uikit/vm/OpenChannelViewModel.java | 59 ++-- .../uikit/vm/PendingMessageRepository.java | 24 ++ .../uikit/widgets/MessageInputView.java | 10 +- 34 files changed, 551 insertions(+), 317 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index ec989470..a761415f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,10 @@ # Change Log +### v2.2.3 (Jan 26, 2022) with Core SDK `v3.1.5` +* Added `List makeMessageContextMenu(BaseMessage)`, `boolean onMessageContextMenuItemClicked(BaseMessage, View, int, DialogListItem)`, `saveFileMessage(FileMessage)` in `ChannelFragment`, `OpenChannelFragment`. +* Changed `ViewModelStoreOwner` from `Activity` to `Fragment`. +* Improved stability. + ### v2.2.2 (Dec 21, 2021) with Core SDK `v3.1.3` * Improved stability. diff --git a/gradle.properties b/gradle.properties index 532e38a4..9f549d51 100644 --- a/gradle.properties +++ b/gradle.properties @@ -17,6 +17,6 @@ org.gradle.jvmargs=-Xmx1536m android.useAndroidX=true android.enablerD8=true -UIKIT_VERSION = 2.2.2 +UIKIT_VERSION = 2.2.3 UIKIT_VERSION_CODE = 1 diff --git a/uikit-custom-sample/build.gradle b/uikit-custom-sample/build.gradle index 1eaa81e6..759c3cc5 100644 --- a/uikit-custom-sample/build.gradle +++ b/uikit-custom-sample/build.gradle @@ -36,7 +36,7 @@ android { dependencies { implementation fileTree(dir: "libs", include: ["*.jar"]) // implementation project(":uikit") - implementation "com.sendbird.sdk:uikit:2.2.2" + implementation "com.sendbird.sdk:uikit:2.2.3" implementation "androidx.multidex:multidex:2.0.1" implementation 'com.google.firebase:firebase-messaging:21.0.0' diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/SettingsFragment.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/SettingsFragment.java index 5920dbcb..e8ee14c0 100644 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/SettingsFragment.java +++ b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/SettingsFragment.java @@ -240,7 +240,8 @@ private void showEditProfileDialog() { new DialogListItem(R.string.text_settings_change_user_profile_image) }; - DialogUtils.buildItemsBottom(items, (v, p, key) -> { + DialogUtils.buildItemsBottom(items, (v, p, item) -> { + final int key = item.getKey(); if (key == R.string.text_settings_change_user_nickname) { Logger.dev("change user nickname"); OnEditTextResultListener listener = result -> { @@ -322,8 +323,9 @@ private void showMediaSelectDialog() { DialogUtils.buildItems(getString(com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image), (int) getResources().getDimension(R.dimen.sb_dialog_width_280), - items, (v, p, key) -> { + items, (v, p, item) -> { try { + final int key = item.getKey(); SendBird.setAutoBackgroundDetection(false); if (key == com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image_camera) { takeCamera(); diff --git a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CreateCommunityActivity.java b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CreateCommunityActivity.java index 133db614..2b1258c5 100644 --- a/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CreateCommunityActivity.java +++ b/uikit-custom-sample/src/main/java/com/sendbird/uikit/customsample/openchannel/community/CreateCommunityActivity.java @@ -217,8 +217,9 @@ private void showMediaSelectDialog() { items = new DialogListItem[]{delete, camera, gallery}; } - DialogUtils.buildItemsBottom(items, (view, position, key) -> { + DialogUtils.buildItemsBottom(items, (view, position, item) -> { try { + final int key = item.getKey(); SendBird.setAutoBackgroundDetection(false); if (key == com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image_camera) { takeCamera(); diff --git a/uikit-sample/build.gradle b/uikit-sample/build.gradle index 81afbd22..df1166e9 100644 --- a/uikit-sample/build.gradle +++ b/uikit-sample/build.gradle @@ -33,7 +33,7 @@ android { dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // implementation project(":uikit") - implementation "com.sendbird.sdk:uikit:2.2.2" + implementation "com.sendbird.sdk:uikit:2.2.3" implementation "androidx.multidex:multidex:2.0.1" implementation 'com.google.firebase:firebase-messaging:21.0.0' diff --git a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/SettingsFragment.java b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/SettingsFragment.java index 0984cb1f..07ade538 100644 --- a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/SettingsFragment.java +++ b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/SettingsFragment.java @@ -285,7 +285,8 @@ private void showEditProfileDialog() { new DialogListItem(R.string.text_settings_change_user_profile_image) }; - DialogUtils.buildItemsBottom(items, (v, p, key) -> { + DialogUtils.buildItemsBottom(items, (v, p, item) -> { + final int key = item.getKey(); if (key == R.string.text_settings_change_user_nickname) { Logger.dev("change user nickname"); OnEditTextResultListener listener = result -> { @@ -399,8 +400,9 @@ private void showMediaSelectDialog() { DialogUtils.buildItems(getString(com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image), (int) getResources().getDimension(R.dimen.sb_dialog_width_280), - items, (v, p, key) -> { + items, (v, p, item) -> { try { + final int key = item.getKey(); SendBird.setAutoBackgroundDetection(false); if (key == com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image_camera) { takeCamera(); diff --git a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/openchannel/community/CreateCommunityActivity.java b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/openchannel/community/CreateCommunityActivity.java index 7005f3b2..9d4ce28d 100644 --- a/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/openchannel/community/CreateCommunityActivity.java +++ b/uikit-sample/src/main/java/com/sendbird/uikit_messaging_android/openchannel/community/CreateCommunityActivity.java @@ -219,8 +219,9 @@ private void showMediaSelectDialog() { items = new DialogListItem[]{delete, camera, gallery}; } - DialogUtils.buildItemsBottom(items, (view, position, key) -> { + DialogUtils.buildItemsBottom(items, (view, position, item) -> { try { + final int key = item.getKey(); SendBird.setAutoBackgroundDetection(false); if (key == com.sendbird.uikit.R.string.sb_text_channel_settings_change_channel_image_camera) { takeCamera(); diff --git a/uikit/build.gradle b/uikit/build.gradle index 7e33c6d4..57d926dc 100644 --- a/uikit/build.gradle +++ b/uikit/build.gradle @@ -57,7 +57,7 @@ dependencies { implementation fileTree(dir: 'libs', include: ['*.jar']) // Sendbird - api 'com.sendbird.sdk:sendbird-android-sdk:3.1.3' + api 'com.sendbird.sdk:sendbird-android-sdk:3.1.5' implementation 'com.github.bumptech.glide:glide:4.11.0' annotationProcessor 'com.github.bumptech.glide:compiler:4.11.0' diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/BannedListFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/BannedListFragment.java index 8540e680..74a742d9 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/BannedListFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/BannedListFragment.java @@ -49,7 +49,7 @@ protected void onActionItemClicked(View view, int position, User user) { items = new DialogListItem[]{unbanMember}; DialogUtils.buildItems(user.getNickname(), (int) getResources().getDimension(R.dimen.sb_dialog_width_280), - items, (v, p, key) -> unbanUser(user.getUserId())).showSingle(getFragmentManager()); + items, (v, p, item) -> unbanUser(user.getUserId())).showSingle(getFragmentManager()); } @Override diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelFragment.java index f5ea593d..6e30deff 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelFragment.java @@ -96,6 +96,7 @@ import java.io.File; import java.util.ArrayList; +import java.util.Arrays; import java.util.HashMap; import java.util.List; import java.util.Locale; @@ -232,7 +233,7 @@ protected void onDrawPage() { } private ChannelViewModel createViewModel(GroupChannel channel) { - return new ViewModelProvider(getActivity(), new ViewModelFactory(channel)).get(channel.getUrl(), ChannelViewModel.class); + return new ViewModelProvider(getViewModelStore(), new ViewModelFactory(channel)).get(channel.getUrl(), ChannelViewModel.class); } private void drawChannel(GroupChannel channel) { @@ -790,7 +791,8 @@ protected void showMediaSelectDialog() { new DialogListItem(R.string.sb_text_channel_input_document, R.drawable.icon_document) }; hideKeyboard(); - DialogUtils.buildItemsBottom(items, (view, position, key) -> { + DialogUtils.buildItemsBottom(items, (view, position, item) -> { + final int key = item.getKey(); try { if (key == R.string.sb_text_channel_input_camera) { takeCamera(); @@ -1220,83 +1222,186 @@ public void onIdentifiableItemLongClick(View itemView, String identifier, int po final BaseMessage.SendingStatus status = message.getSendingStatus(); if (status == BaseMessage.SendingStatus.PENDING) return; - MessageType type = MessageViewHolderFactory.getMessageType(message); - DialogListItem copy = new DialogListItem(R.string.sb_text_channel_anchor_copy, R.drawable.icon_copy); - DialogListItem edit = new DialogListItem(R.string.sb_text_channel_anchor_edit, R.drawable.icon_edit); - DialogListItem save = new DialogListItem(R.string.sb_text_channel_anchor_save, R.drawable.icon_download); - DialogListItem delete = new DialogListItem(R.string.sb_text_channel_anchor_delete, R.drawable.icon_delete, false, !MessageUtils.isDeletableMessage(message)); - DialogListItem reply = new DialogListItem(R.string.sb_text_channel_anchor_reply, R.drawable.icon_reply, false, MessageUtils.hasParentMessage(message)); - DialogListItem retry = new DialogListItem(R.string.sb_text_channel_anchor_retry, 0); - DialogListItem deleteFailed = new DialogListItem(R.string.sb_text_channel_anchor_delete, 0); - - DialogListItem[] actions = null; - switch (type) { - case VIEW_TYPE_USER_MESSAGE_ME: - if (status == BaseMessage.SendingStatus.SUCCEEDED) { - if (replyType == ReplyType.NONE) { - actions = new DialogListItem[]{copy, edit, delete}; - } else { - actions = new DialogListItem[]{copy, edit, delete, reply}; - } - } else if (MessageUtils.isFailed(message)) { - actions = new DialogListItem[]{retry, deleteFailed}; - } - break; - case VIEW_TYPE_USER_MESSAGE_OTHER: + final List items = makeMessageContextMenu(message); + showMessageContextMenu(itemView, message, items); + } + } + + private void showMessageContextMenu(@NonNull View anchorView, @NonNull BaseMessage message, @NonNull List items) { + int size = items.size(); + final DialogListItem[] actions = items.toArray(new DialogListItem[size]); + + if (!ReactionUtils.canSendReaction(viewModel.getChannel())) { + if (getContext() != null) { + MessageAnchorDialog messageAnchorDialog = new MessageAnchorDialog.Builder(anchorView, binding.mrvMessageList, actions) + .setOnItemClickListener(createMessageActionListener(message)) + .setOnDismissListener(() -> anchorDialogShowing = false) + .build(); + messageAnchorDialog.show(); + anchorDialogShowing = true; + } + } else if (MessageUtils.isUnknownType(message)) { + if (getContext() == null || getFragmentManager() == null) return; + DialogUtils + .buildItemsBottom(actions, createMessageActionListener(message)) + .showSingle(getFragmentManager()); + } else { + showEmojiActionsDialog(message, actions); + } + } + + /** + * Make context menu items that are shown when the message is long clicked. + * + * @param message A clicked message. + * @return Collection of {@link DialogListItem} + * @since 2.2.3 + */ + @NonNull + protected List makeMessageContextMenu(@NonNull BaseMessage message) { + final List items = new ArrayList<>(); + final BaseMessage.SendingStatus status = message.getSendingStatus(); + if (status == BaseMessage.SendingStatus.PENDING) return items; + + MessageType type = MessageViewHolderFactory.getMessageType(message); + DialogListItem copy = new DialogListItem(R.string.sb_text_channel_anchor_copy, R.drawable.icon_copy); + DialogListItem edit = new DialogListItem(R.string.sb_text_channel_anchor_edit, R.drawable.icon_edit); + DialogListItem save = new DialogListItem(R.string.sb_text_channel_anchor_save, R.drawable.icon_download); + DialogListItem delete = new DialogListItem(R.string.sb_text_channel_anchor_delete, R.drawable.icon_delete, false, !MessageUtils.isDeletableMessage(message)); + DialogListItem reply = new DialogListItem(R.string.sb_text_channel_anchor_reply, R.drawable.icon_reply, false, MessageUtils.hasParentMessage(message)); + DialogListItem retry = new DialogListItem(R.string.sb_text_channel_anchor_retry, 0); + DialogListItem deleteFailed = new DialogListItem(R.string.sb_text_channel_anchor_delete, 0); + + DialogListItem[] actions = null; + switch (type) { + case VIEW_TYPE_USER_MESSAGE_ME: + if (status == BaseMessage.SendingStatus.SUCCEEDED) { if (replyType == ReplyType.NONE) { - actions = new DialogListItem[]{copy}; - } else { - actions = new DialogListItem[]{copy, reply}; - } - break; - case VIEW_TYPE_FILE_MESSAGE_VIDEO_ME: - case VIEW_TYPE_FILE_MESSAGE_IMAGE_ME: - case VIEW_TYPE_FILE_MESSAGE_ME: - if (MessageUtils.isFailed(message)) { - actions = new DialogListItem[]{retry, deleteFailed}; + actions = new DialogListItem[]{copy, edit, delete}; } else { - if (replyType == ReplyType.NONE) { - actions = new DialogListItem[]{delete, save}; - } else { - actions = new DialogListItem[]{delete, save, reply}; - } + actions = new DialogListItem[]{copy, edit, delete, reply}; } - break; - case VIEW_TYPE_FILE_MESSAGE_VIDEO_OTHER: - case VIEW_TYPE_FILE_MESSAGE_IMAGE_OTHER: - case VIEW_TYPE_FILE_MESSAGE_OTHER: + } else if (MessageUtils.isFailed(message)) { + actions = new DialogListItem[]{retry, deleteFailed}; + } + break; + case VIEW_TYPE_USER_MESSAGE_OTHER: + if (replyType == ReplyType.NONE) { + actions = new DialogListItem[]{copy}; + } else { + actions = new DialogListItem[]{copy, reply}; + } + break; + case VIEW_TYPE_FILE_MESSAGE_VIDEO_ME: + case VIEW_TYPE_FILE_MESSAGE_IMAGE_ME: + case VIEW_TYPE_FILE_MESSAGE_ME: + if (MessageUtils.isFailed(message)) { + actions = new DialogListItem[]{retry, deleteFailed}; + } else { if (replyType == ReplyType.NONE) { - actions = new DialogListItem[]{save}; + actions = new DialogListItem[]{delete, save}; } else { - actions = new DialogListItem[]{save, reply}; + actions = new DialogListItem[]{delete, save, reply}; } - break; - case VIEW_TYPE_UNKNOWN_MESSAGE_ME: - actions = new DialogListItem[]{delete}; - default: - break; - } - - if (actions != null) { - if (!ReactionUtils.canSendReaction(viewModel.getChannel())) { - if (getContext() != null) { - MessageAnchorDialog messageAnchorDialog = new MessageAnchorDialog.Builder(itemView, binding.mrvMessageList, actions) - .setOnItemClickListener(createMessageActionListener(message)) - .setOnDismissListener(() -> anchorDialogShowing = false) - .build(); - messageAnchorDialog.show(); - anchorDialogShowing = true; - } - } else if (MessageUtils.isUnknownType(message)) { - if (getContext() == null || getFragmentManager() == null) return; - DialogUtils - .buildItemsBottom(actions, createMessageActionListener(message)) - .showSingle(getFragmentManager()); + } + break; + case VIEW_TYPE_FILE_MESSAGE_VIDEO_OTHER: + case VIEW_TYPE_FILE_MESSAGE_IMAGE_OTHER: + case VIEW_TYPE_FILE_MESSAGE_OTHER: + if (replyType == ReplyType.NONE) { + actions = new DialogListItem[]{save}; } else { - showEmojiActionsDialog(message, actions); + actions = new DialogListItem[]{save, reply}; } + break; + case VIEW_TYPE_UNKNOWN_MESSAGE_ME: + actions = new DialogListItem[]{delete}; + default: + break; + } + + if (actions != null) { + items.addAll(Arrays.asList(actions)); + } + return items; + } + + /** + * It will be called when the message context menu was clicked. + * + * @param message A clicked message. + * @param view The view that was clicked. + * @param position The position that was clicked. + * @param item {@link DialogListItem} that was clicked. + * @return true if long click event was handled, false otherwise. + * @since 2.2.3 + */ + protected boolean onMessageContextMenuItemClicked(@NonNull BaseMessage message, @NonNull View view, int position, @NonNull DialogListItem item) { + final int key = item.getKey(); + if (key == R.string.sb_text_channel_anchor_copy) { + copyTextToClipboard(message.getMessage()); + return true; + } else if (key == R.string.sb_text_channel_anchor_edit) { + targetMessage = message; + binding.vgInputBox.setInputMode(MessageInputView.Mode.EDIT); + return true; + } else if (key == R.string.sb_text_channel_anchor_delete) { + if (MessageUtils.isFailed(message)) { + Logger.dev("delete"); + deleteMessage(message); + } else { + showWarningDialog(message); + } + return true; + } else if (key == R.string.sb_text_channel_anchor_save) { + if (message instanceof FileMessage) { + saveFileMessage((FileMessage) message); } + return true; + } else if (key == R.string.sb_text_channel_anchor_reply) { + this.targetMessage = message; + binding.vgInputBox.setInputMode(MessageInputView.Mode.QUOTE_REPLY); + return true; + } else if (key == R.string.sb_text_channel_anchor_retry) { + resendMessage(message); + return true; } + + return false; + } + + /** + * Download {@link FileMessage} into external storage. + * It needs to have a permission. + * If current application needs permission, the request of permission will call automatically. + * After permission is granted, the download will be also called automatically. + * + * @param message A file message to download contents. + * @since 2.2.3 + */ + protected void saveFileMessage(@NonNull FileMessage message) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + download(message); + } else { + checkPermission(PERMISSION_REQUEST_STORAGE, new PermissionFragment.IPermissionHandler() { + @Override + @NonNull + public String[] getPermissions(int requestCode) { + return new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE}; + } + + @Override + public void onPermissionGranted(int requestCode) { + download(message); + } + }); + } + } + + @NonNull + private OnItemClickListener createMessageActionListener(@NonNull BaseMessage message) { + return (view, position, item) -> onMessageContextMenuItemClicked(message, view, position, item); } private void showEmojiActionsDialog(BaseMessage message, DialogListItem[] actions) { @@ -1377,46 +1482,6 @@ private void hideKeyboard() { } } - private OnItemClickListener createMessageActionListener(BaseMessage message) { - return (view, position, key) -> { - if (key == R.string.sb_text_channel_anchor_copy) { - copyTextToClipboard(message.getMessage()); - } else if (key == R.string.sb_text_channel_anchor_edit) { - targetMessage = message; - binding.vgInputBox.setInputMode(MessageInputView.Mode.EDIT); - } else if (key == R.string.sb_text_channel_anchor_delete) { - if (MessageUtils.isFailed(message)) { - Logger.dev("delete"); - deleteMessage(message); - } else { - showWarningDialog(message); - } - } else if (key == R.string.sb_text_channel_anchor_save) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { - download((FileMessage) message); - } else { - checkPermission(PERMISSION_REQUEST_STORAGE, new IPermissionHandler() { - @Override - public String[] getPermissions(int requestCode) { - return new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.READ_EXTERNAL_STORAGE}; - } - - @Override - public void onPermissionGranted(int requestCode) { - download((FileMessage) message); - } - }); - } - } else if (key == R.string.sb_text_channel_anchor_reply) { - this.targetMessage = message; - binding.vgInputBox.setInputMode(MessageInputView.Mode.QUOTE_REPLY); - } else if (key == R.string.sb_text_channel_anchor_retry) { - resendMessage(message); - } - }; - } - private void download(@NonNull FileMessage fileMessage) { toastSuccess(R.string.sb_text_toast_success_start_download_file); TaskQueue.addTask(new JobResultTask() { diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelListFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelListFragment.java index d704f9f8..09a9369a 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelListFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelListFragment.java @@ -210,7 +210,7 @@ private void initChannelList() { query.setIncludeEmpty(includeEmpty); } - viewModel = new ViewModelProvider(this, new ViewModelFactory()).get(ChannelListViewModel.class); + viewModel = new ViewModelProvider(getViewModelStore(), new ViewModelFactory()).get(ChannelListViewModel.class); initAdapter(); binding.rvGroupChannelList.setAdapter(adapter); @@ -293,7 +293,8 @@ private void initAdapter() { if (isActive() && getFragmentManager() != null) { DialogUtils.buildItems(ChannelUtils.makeTitleText(getContext(), channel), (int) getResources().getDimension(R.dimen.sb_dialog_width_280), - items, (v, p, key) -> { + items, (v, p, item) -> { + final int key = item.getKey(); if (key == R.string.sb_text_channel_list_leave) { Logger.dev("leave channel"); leaveChannel(channel); diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelSettingsFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelSettingsFragment.java index 794b751c..fde8edb3 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelSettingsFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/ChannelSettingsFragment.java @@ -272,7 +272,8 @@ private void initHeaderOnReady(GroupChannel channel) { }; if (getContext() == null || getFragmentManager() == null) return; - DialogUtils.buildItemsBottom(items, (view, p, key) -> { + DialogUtils.buildItemsBottom(items, (view, p, item) -> { + final int key = item.getKey(); if (key == R.string.sb_text_channel_settings_change_channel_name) { if (getContext() == null || getFragmentManager() == null) return; @@ -371,8 +372,9 @@ private void showMediaSelectDialog() { DialogUtils.buildItems(getString(R.string.sb_text_channel_settings_change_channel_image), (int) getResources().getDimension(R.dimen.sb_dialog_width_280), - items, (v, p, key) -> { + items, (v, p, item) -> { try { + final int key = item.getKey(); SendBird.setAutoBackgroundDetection(false); if (key == R.string.sb_text_channel_settings_change_channel_image_camera) { takeCamera(); diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/DialogListAdapter.java b/uikit/src/main/java/com/sendbird/uikit/fragments/DialogListAdapter.java index de0ef26b..0eef74ed 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/DialogListAdapter.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/DialogListAdapter.java @@ -21,11 +21,11 @@ class DialogListAdapter extends RecyclerView.Adapter { private final DialogListItem[] items; - private final OnItemClickListener listener; + private final OnItemClickListener listener; private int nameMarginLeft = R.dimen.sb_size_24; private final boolean isIconLeft; - DialogListAdapter(DialogListItem[] items, OnItemClickListener listener, boolean isIconLeft) { + DialogListAdapter(DialogListItem[] items, OnItemClickListener listener, boolean isIconLeft) { this.items = items; this.listener = listener; this.isIconLeft = isIconLeft; @@ -61,11 +61,11 @@ static class ListViewHolder extends RecyclerView.ViewHolder { private final Context context; private final ColorStateList buttonTint; - private final OnItemClickListener listener; + private final OnItemClickListener listener; private final boolean isIconLeft; private ListViewHolder(SbViewDialogListItemBinding binding, - OnItemClickListener listener, + OnItemClickListener listener, int nameMarginLeft, boolean isIconLeft) { super(binding.getRoot()); this.binding = binding; @@ -115,7 +115,7 @@ private void bind(@NonNull DialogListItem item) { binding.getRoot().setOnClickListener((v) -> { if (listener != null && item.getKey() != 0) { - listener.onItemClick(binding.getRoot(), getAdapterPosition(), item.getKey()); + listener.onItemClick(binding.getRoot(), getAdapterPosition(), item); } }); diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/DialogView.java b/uikit/src/main/java/com/sendbird/uikit/fragments/DialogView.java index 0338a036..2bba7d15 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/DialogView.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/DialogView.java @@ -171,13 +171,13 @@ void setEditText(DialogEditTextParams params, OnEditTextResultListener editTextR this.editTextResultListener = editTextResultListener; } - void setItems(DialogListItem[] items, @NonNull OnItemClickListener itemClickListener, boolean isLeft) { + void setItems(DialogListItem[] items, @NonNull OnItemClickListener itemClickListener, boolean isLeft) { if (items == null) return; binding.rvSelectView.setAdapter(new DialogListAdapter(items, itemClickListener, isLeft) ); binding.rvSelectView.setVisibility(VISIBLE); } - void setItems(DialogListItem[] items, @NonNull OnItemClickListener itemClickListener, boolean isLeft, @DimenRes int nameMarginLeft) { + void setItems(DialogListItem[] items, @NonNull OnItemClickListener itemClickListener, boolean isLeft, @DimenRes int nameMarginLeft) { if (items == null) return; DialogListAdapter adapter = new DialogListAdapter(items, itemClickListener, isLeft); adapter.setNameMarginLeft(nameMarginLeft); diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/MemberListFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/MemberListFragment.java index aeb866fa..9783aaa4 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/MemberListFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/MemberListFragment.java @@ -84,7 +84,8 @@ protected void onActionItemClicked(View view, int position, Member member) { new DialogListItem[]{promoteOperator, banMember}; DialogUtils.buildItems(member.getNickname(), (int) getResources().getDimension(R.dimen.sb_dialog_width_280), - items, (v, p, key) -> { + items, (v, p, item) -> { + final int key = item.getKey(); if (key == R.string.sb_text_promote_operator) { addOperator(member.getUserId()); } else if (key == R.string.sb_text_dismiss_operator) { diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/MemberTypeListFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/MemberTypeListFragment.java index 8106fa48..afa8abd2 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/MemberTypeListFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/MemberTypeListFragment.java @@ -171,7 +171,7 @@ private void initHeaderOnReady() { } private void initMemberList(@NonNull GroupChannel channel) { - UserTypeListViewModel viewModel = new ViewModelProvider(getActivity(), new ViewModelFactory(channel, customQueryHandler)).get(channel.getUrl(), UserTypeListViewModel.class); + UserTypeListViewModel viewModel = new ViewModelProvider(getViewModelStore(), new ViewModelFactory(channel, customQueryHandler)).get(channel.getUrl(), UserTypeListViewModel.class); getLifecycle().addObserver(viewModel); if (adapter == null) { adapter = new MemberListAdapter(); diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/MessageAnchorDialog.java b/uikit/src/main/java/com/sendbird/uikit/fragments/MessageAnchorDialog.java index f9dbe830..a1372d0f 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/MessageAnchorDialog.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/MessageAnchorDialog.java @@ -21,7 +21,7 @@ class MessageAnchorDialog { private final View anchorView; private final DialogView contentView; private final View parent; - private OnItemClickListener itemClickListener; + private OnItemClickListener itemClickListener; private final PopupWindow window; private final Context context; private final View.OnLayoutChangeListener layoutChangeListener; @@ -36,10 +36,10 @@ private MessageAnchorDialog(@NonNull View anchorView, @NonNull View parent, @Non this.window.setSoftInputMode(WindowManager.LayoutParams.SOFT_INPUT_STATE_ALWAYS_HIDDEN); contentView = new DialogView(context); - contentView.setItems(items, (view, position, key) -> { + contentView.setItems(items, (view, position, item) -> { window.dismiss(); if (itemClickListener != null) { - itemClickListener.onItemClick(view, position, key); + itemClickListener.onItemClick(view, position, item); } }, false, R.dimen.sb_size_16); contentView.setBackgroundAnchor(); @@ -121,7 +121,7 @@ private static boolean isDropDown(View parent, View anchorView) { return (parentHeight / 2 > loc[1]-parentLoc[1]); } - void setOnItemClickListener(OnItemClickListener itemClickListener) { + void setOnItemClickListener(OnItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; } @@ -133,7 +133,7 @@ public static class Builder { private final View anchorView; private final View parent; private final DialogListItem[] items; - private OnItemClickListener itemClickListener; + private OnItemClickListener itemClickListener; private PopupWindow.OnDismissListener dismissListener; public Builder(View anchorView, View parent, DialogListItem[] items) { @@ -142,7 +142,7 @@ public Builder(View anchorView, View parent, DialogListItem[] items) { this.items = items; } - public Builder setOnItemClickListener(OnItemClickListener itemClickListener) { + public Builder setOnItemClickListener(OnItemClickListener itemClickListener) { this.itemClickListener = itemClickListener; return this; } diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/MessageSearchFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/MessageSearchFragment.java index 7ee2150a..6a8465cf 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/MessageSearchFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/MessageSearchFragment.java @@ -143,7 +143,7 @@ private void initSearchBar() { } private void initSearchResultList(@NonNull GroupChannel channel) { - viewModel = new ViewModelProvider(getActivity(), new ViewModelFactory(channel, query)).get(channel.getUrl(), SearchViewModel.class); + viewModel = new ViewModelProvider(getViewModelStore(), new ViewModelFactory(channel, query)).get(channel.getUrl(), SearchViewModel.class); getLifecycle().addObserver(viewModel); Bundle args = getArguments(); diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/MutedMemberListFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/MutedMemberListFragment.java index 79c7f759..d2b03005 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/MutedMemberListFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/MutedMemberListFragment.java @@ -48,7 +48,7 @@ protected void onActionItemClicked(View view, int position, Member member) { DialogListItem unMute = new DialogListItem(R.string.sb_text_unmute_member); items = new DialogListItem[]{unMute}; DialogUtils.buildItems(member.getNickname(), (int) getResources().getDimension(R.dimen.sb_dialog_width_280), - items, (v, p, key) -> unmuteUser(member.getUserId())).showSingle(getFragmentManager()); + items, (v, p, item) -> unmuteUser(member.getUserId())).showSingle(getFragmentManager()); } @Override diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/OpenChannelFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/OpenChannelFragment.java index e7850f4f..6237e0de 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/OpenChannelFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/OpenChannelFragment.java @@ -78,6 +78,9 @@ import com.sendbird.uikit.widgets.PagerRecyclerView; import java.io.File; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.List; import java.util.concurrent.atomic.AtomicBoolean; /** @@ -205,7 +208,7 @@ protected void onDrawPage() { } private OpenChannelViewModel createViewModel(OpenChannel channel) { - return new ViewModelProvider(getActivity(), new ViewModelFactory(channel, params)).get(channel.getUrl(), OpenChannelViewModel.class); + return new ViewModelProvider(getViewModelStore(), new ViewModelFactory(channel, params)).get(channel.getUrl(), OpenChannelViewModel.class); } private void drawChannel(OpenChannel channel) { @@ -543,7 +546,8 @@ protected void showMediaSelectDialog() { new DialogListItem(R.string.sb_text_channel_input_document, R.drawable.icon_document) }; hideKeyboard(); - DialogUtils.buildItemsBottom(items, (view, position, key) -> { + DialogUtils.buildItemsBottom(items, (view, position, item) -> { + final int key = item.getKey(); try { if (key == R.string.sb_text_channel_input_camera) { takeCamera(); @@ -905,61 +909,159 @@ public void onIdentifiableItemLongClick(View itemView, String clickableType, int final BaseMessage.SendingStatus status = message.getSendingStatus(); if (status == BaseMessage.SendingStatus.PENDING) return; - MessageType type = MessageViewHolderFactory.getMessageType(message); - DialogListItem copy = new DialogListItem(R.string.sb_text_channel_anchor_copy, R.drawable.icon_copy); - DialogListItem edit = new DialogListItem(R.string.sb_text_channel_anchor_edit, R.drawable.icon_edit); - DialogListItem save = new DialogListItem(R.string.sb_text_channel_anchor_save, R.drawable.icon_download); - DialogListItem delete = new DialogListItem(R.string.sb_text_channel_anchor_delete, R.drawable.icon_delete); - DialogListItem retry = new DialogListItem(R.string.sb_text_channel_anchor_retry, 0); - DialogListItem deleteFailed = new DialogListItem(R.string.sb_text_channel_anchor_delete, 0); - - DialogListItem[] actions = null; - switch (type) { - case VIEW_TYPE_USER_MESSAGE_ME: - if (status == BaseMessage.SendingStatus.SUCCEEDED) { - actions = new DialogListItem[]{copy, edit, delete}; - } else if (MessageUtils.isFailed(message)) { - actions = new DialogListItem[]{retry, deleteFailed}; - } - break; - case VIEW_TYPE_USER_MESSAGE_OTHER: - actions = new DialogListItem[]{copy}; - break; - case VIEW_TYPE_FILE_MESSAGE_VIDEO_ME: - case VIEW_TYPE_FILE_MESSAGE_IMAGE_ME: - case VIEW_TYPE_FILE_MESSAGE_ME: - if (MessageUtils.isFailed(message)) { - actions = new DialogListItem[]{retry, deleteFailed}; - } else { - actions = new DialogListItem[]{delete, save}; - } - break; - case VIEW_TYPE_FILE_MESSAGE_VIDEO_OTHER: - case VIEW_TYPE_FILE_MESSAGE_IMAGE_OTHER: - case VIEW_TYPE_FILE_MESSAGE_OTHER: - actions = new DialogListItem[]{save}; - break; - case VIEW_TYPE_UNKNOWN_MESSAGE_ME: - actions = new DialogListItem[]{delete}; - default: - break; - } + final List items = makeMessageContextMenu(message); + showMessageContextMenu(itemView, message, items); + } + } + + private void showMessageContextMenu(@NonNull View anchorView, @NonNull BaseMessage message, @NonNull List items) { + int size = items.size(); + final DialogListItem[] actions = items.toArray(new DialogListItem[size]); + + if (MessageUtils.isUnknownType(message)) { + if (getContext() == null || getFragmentManager() == null) return; + DialogUtils + .buildItemsBottom(actions, createMessageActionListener(message)) + .showSingle(getFragmentManager()); + } else { + if (getContext() == null) return; + messageAnchorDialog = new MessageAnchorDialog.Builder(anchorView, binding.mrvMessageList, actions) + .setOnItemClickListener(createMessageActionListener(message)) + .build(); + messageAnchorDialog.show(); + } + } - if (actions != null) { - if (MessageUtils.isUnknownType(message)) { - if (getContext() == null || getFragmentManager() == null) return; - DialogUtils - .buildItemsBottom(actions, createMessageActionListener(message)) - .showSingle(getFragmentManager()); + /** + * Make context menu items that are shown when the message is long clicked. + * + * @param message A clicked message. + * @return Collection of {@link DialogListItem} + * @since 2.2.3 + */ + @NonNull + protected List makeMessageContextMenu(@NonNull BaseMessage message) { + final List items = new ArrayList<>(); + final BaseMessage.SendingStatus status = message.getSendingStatus(); + if (status == BaseMessage.SendingStatus.PENDING) return items; + + MessageType type = MessageViewHolderFactory.getMessageType(message); + DialogListItem copy = new DialogListItem(R.string.sb_text_channel_anchor_copy, R.drawable.icon_copy); + DialogListItem edit = new DialogListItem(R.string.sb_text_channel_anchor_edit, R.drawable.icon_edit); + DialogListItem save = new DialogListItem(R.string.sb_text_channel_anchor_save, R.drawable.icon_download); + DialogListItem delete = new DialogListItem(R.string.sb_text_channel_anchor_delete, R.drawable.icon_delete); + DialogListItem retry = new DialogListItem(R.string.sb_text_channel_anchor_retry, 0); + DialogListItem deleteFailed = new DialogListItem(R.string.sb_text_channel_anchor_delete, 0); + + DialogListItem[] actions = null; + switch (type) { + case VIEW_TYPE_USER_MESSAGE_ME: + if (status == BaseMessage.SendingStatus.SUCCEEDED) { + actions = new DialogListItem[]{copy, edit, delete}; + } else if (MessageUtils.isFailed(message)) { + actions = new DialogListItem[]{retry, deleteFailed}; + } + break; + case VIEW_TYPE_USER_MESSAGE_OTHER: + actions = new DialogListItem[]{copy}; + break; + case VIEW_TYPE_FILE_MESSAGE_VIDEO_ME: + case VIEW_TYPE_FILE_MESSAGE_IMAGE_ME: + case VIEW_TYPE_FILE_MESSAGE_ME: + if (MessageUtils.isFailed(message)) { + actions = new DialogListItem[]{retry, deleteFailed}; } else { - if (getContext() == null) return; - messageAnchorDialog = new MessageAnchorDialog.Builder(itemView, binding.mrvMessageList, actions) - .setOnItemClickListener(createMessageActionListener(message)) - .build(); - messageAnchorDialog.show(); + actions = new DialogListItem[]{delete, save}; } + break; + case VIEW_TYPE_FILE_MESSAGE_VIDEO_OTHER: + case VIEW_TYPE_FILE_MESSAGE_IMAGE_OTHER: + case VIEW_TYPE_FILE_MESSAGE_OTHER: + actions = new DialogListItem[]{save}; + break; + case VIEW_TYPE_UNKNOWN_MESSAGE_ME: + actions = new DialogListItem[]{delete}; + default: + break; + } + + if (actions != null) { + items.addAll(Arrays.asList(actions)); + } + return items; + } + + /** + * It will be called when the message context menu was clicked. + * + * @param message A clicked message. + * @param view The view that was clicked. + * @param position The position that was clicked. + * @param item {@link DialogListItem} that was clicked. + * @return true if long click event was handled, false otherwise. + * @since 2.2.3 + */ + protected boolean onMessageContextMenuItemClicked(@NonNull BaseMessage message, @NonNull View view, int position, @NonNull DialogListItem item) { + final int key = item.getKey(); + if (key == R.string.sb_text_channel_anchor_copy) { + copyTextToClipboard(message.getMessage()); + return true; + } else if (key == R.string.sb_text_channel_anchor_edit) { + editMessage(message); + return true; + } else if (key == R.string.sb_text_channel_anchor_delete) { + if (MessageUtils.isFailed(message)) { + Logger.dev("delete"); + deleteMessage(message); + } else { + showWarningDialog(message); + } + return true; + } else if (key == R.string.sb_text_channel_anchor_save) { + if (message instanceof FileMessage) { + saveFileMessage((FileMessage) message); } + return true; + } else if (key == R.string.sb_text_channel_anchor_retry) { + resendMessage(message); + return true; } + + return false; + } + + /** + * Download {@link FileMessage} into external storage. + * It needs to have a permission. + * If current application needs permission, the request of permission will call automatically. + * After permission is granted, the download will be also called automatically. + * + * @param message A file message to download contents. + * @since 2.2.3 + */ + protected void saveFileMessage(@NonNull FileMessage message) { + if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { + download(message); + } else { + checkPermission(PERMISSION_REQUEST_STORAGE, new PermissionFragment.IPermissionHandler() { + @Override + @NonNull + public String[] getPermissions(int requestCode) { + return new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, + Manifest.permission.READ_EXTERNAL_STORAGE}; + } + + @Override + public void onPermissionGranted(int requestCode) { + download(message); + } + }); + } + } + + @NonNull + private OnItemClickListener createMessageActionListener(@NonNull BaseMessage message) { + return (view, position, item) -> onMessageContextMenuItemClicked(message, view, position, item); } private void clearInput() { @@ -973,42 +1075,6 @@ private void hideKeyboard() { } } - private OnItemClickListener createMessageActionListener(BaseMessage message) { - return (view, position, key) -> { - if (key == R.string.sb_text_channel_anchor_copy) { - copyTextToClipboard(message.getMessage()); - } else if (key == R.string.sb_text_channel_anchor_edit) { - editMessage(message); - } else if (key == R.string.sb_text_channel_anchor_delete) { - if (MessageUtils.isFailed(message)) { - Logger.dev("delete"); - deleteMessage(message); - } else { - showWarningDialog(message); - } - } else if (key == R.string.sb_text_channel_anchor_save) { - if (Build.VERSION.SDK_INT > Build.VERSION_CODES.P) { - download((FileMessage) message); - } else { - checkPermission(PERMISSION_REQUEST_STORAGE, new IPermissionHandler() { - @Override - public String[] getPermissions(int requestCode) { - return new String[]{Manifest.permission.WRITE_EXTERNAL_STORAGE, - Manifest.permission.READ_EXTERNAL_STORAGE}; - } - - @Override - public void onPermissionGranted(int requestCode) { - download((FileMessage) message); - } - }); - } - } else if (key == R.string.sb_text_channel_anchor_retry) { - resendMessage(message); - } - }; - } - private void download(@NonNull FileMessage fileMessage) { toastSuccess(R.string.sb_text_toast_success_start_download_file); TaskQueue.addTask(new JobResultTask() { diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/OpenChannelSettingsFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/OpenChannelSettingsFragment.java index 00928603..eb51d4c7 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/OpenChannelSettingsFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/OpenChannelSettingsFragment.java @@ -254,7 +254,8 @@ private void initHeaderOnReady(OpenChannel channel) { }; if (getContext() == null || getFragmentManager() == null) return; - DialogUtils.buildItemsBottom(items, (view, p, key) -> { + DialogUtils.buildItemsBottom(items, (view, p, item) -> { + final int key = item.getKey(); if (key == R.string.sb_text_channel_settings_change_channel_name) { if (getContext() == null || getFragmentManager() == null) return; @@ -310,8 +311,9 @@ private void showMediaSelectDialog() { DialogUtils.buildItems(getString(R.string.sb_text_channel_settings_change_channel_image), (int) getResources().getDimension(R.dimen.sb_dialog_width_280), - items, (v, p, key) -> { + items, (v, p, item) -> { try { + final int key = item.getKey(); SendBird.setAutoBackgroundDetection(false); if (key == R.string.sb_text_channel_settings_change_channel_image_camera) { takeCamera(); diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/OperatorListFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/OperatorListFragment.java index 60cffb65..8d10bd10 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/OperatorListFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/OperatorListFragment.java @@ -59,7 +59,7 @@ protected void onActionItemClicked(View view, int position, User user) { items = new DialogListItem[]{removeOperator}; DialogUtils.buildItems(user.getNickname(), (int) getResources().getDimension(R.dimen.sb_dialog_width_280), - items, (v, p, key) -> removeOperator(user.getUserId())).showSingle(getFragmentManager()); + items, (v, p, item) -> removeOperator(user.getUserId())).showSingle(getFragmentManager()); } private void removeOperator(@NonNull String userId) { diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/ParticipantsListFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/ParticipantsListFragment.java index 2cc99b9a..3c6c644f 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/ParticipantsListFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/ParticipantsListFragment.java @@ -159,7 +159,7 @@ private void initHeaderOnReady() { } private void initParticipantsList(@NonNull OpenChannel channel) { - UserTypeListViewModel viewModel = new ViewModelProvider(getActivity(), new ViewModelFactory(channel, new ParticipantsListQuery(channel))).get(channel.getUrl(), UserTypeListViewModel.class); + UserTypeListViewModel viewModel = new ViewModelProvider(getViewModelStore(), new ViewModelFactory(channel, new ParticipantsListQuery(channel))).get(channel.getUrl(), UserTypeListViewModel.class); getLifecycle().addObserver(viewModel); if (adapter == null) { adapter = new UserTypeListAdapter(); diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/SelectUserFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/SelectUserFragment.java index 603f3438..3aef7551 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/SelectUserFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/SelectUserFragment.java @@ -89,7 +89,7 @@ protected void onConfigure() { * Draw page with set data. */ protected void onDrawPage() { - this.viewModel = new ViewModelProvider(this, new ViewModelFactory(customUserListQueryHandler)).get(SelectableUserInfoListViewModel.class); + this.viewModel = new ViewModelProvider(getViewModelStore(), new ViewModelFactory(customUserListQueryHandler)).get(SelectableUserInfoListViewModel.class); initHeaderOnReady(); initUserList(); } diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/SendBirdDialogFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/SendBirdDialogFragment.java index 7eeb8ddb..ab7c4240 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/SendBirdDialogFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/SendBirdDialogFragment.java @@ -205,7 +205,7 @@ public Builder setDialogGravity(DialogGravity dialogGravity) { * @param iconGravity {@link ItemIconGravity} that will locate an icon for each items. * @return This Builder object to allow for chaining of calls to set methods. */ - public Builder setItems(DialogListItem[] items, OnItemClickListener itemClickListener, ItemIconGravity iconGravity) { + public Builder setItems(DialogListItem[] items, OnItemClickListener itemClickListener, ItemIconGravity iconGravity) { this.params.items = items; this.params.itemClickListener = itemClickListener; this.params.itemIconGravity = iconGravity; @@ -333,7 +333,7 @@ private static class Params { private String title; private DialogGravity dialogGravity = DialogGravity.CENTER; private DialogListItem[] items; - private OnItemClickListener itemClickListener; + private OnItemClickListener itemClickListener; private ItemIconGravity itemIconGravity; private DialogEditTextParams editTextParams; private OnEditTextResultListener editTextResultListener; diff --git a/uikit/src/main/java/com/sendbird/uikit/fragments/UserTypeListFragment.java b/uikit/src/main/java/com/sendbird/uikit/fragments/UserTypeListFragment.java index 60f8fa47..e7716977 100644 --- a/uikit/src/main/java/com/sendbird/uikit/fragments/UserTypeListFragment.java +++ b/uikit/src/main/java/com/sendbird/uikit/fragments/UserTypeListFragment.java @@ -173,7 +173,7 @@ private void initHeaderOnReady() { } private void initChannelUserList(@NonNull GroupChannel channel) { - UserTypeListViewModel viewModel = new ViewModelProvider(getActivity(), new ViewModelFactory(channel, customQueryHandler)).get(channel.getUrl(), UserTypeListViewModel.class); + UserTypeListViewModel viewModel = new ViewModelProvider(getViewModelStore(), new ViewModelFactory(channel, customQueryHandler)).get(channel.getUrl(), UserTypeListViewModel.class); getLifecycle().addObserver(viewModel); if (adapter == null) { adapter = new UserTypeListAdapter(); diff --git a/uikit/src/main/java/com/sendbird/uikit/model/DialogListItem.java b/uikit/src/main/java/com/sendbird/uikit/model/DialogListItem.java index effff831..66fe3233 100644 --- a/uikit/src/main/java/com/sendbird/uikit/model/DialogListItem.java +++ b/uikit/src/main/java/com/sendbird/uikit/model/DialogListItem.java @@ -1,44 +1,95 @@ package com.sendbird.uikit.model; +import androidx.annotation.DrawableRes; +import androidx.annotation.StringRes; + public class DialogListItem { + @StringRes private final int key; + @DrawableRes private final int icon; private final boolean isAlert; private boolean disabled; - public DialogListItem(int key) { + /** + * A Single item of selectable dialog list. + * + * @param key the resource identifier of the string resource to be displayed. + */ + public DialogListItem(@StringRes int key) { this(key, 0); } - public DialogListItem(int key, int icon) { + /** + * A Single item of selectable dialog list. + * + * @param key The resource identifier of the string resource to be displayed. + * @param icon Resource identifier of the icon Drawable. + */ + public DialogListItem(@StringRes int key, @DrawableRes int icon) { this(key, icon, false); } - public DialogListItem(int key, int icon, boolean isAlert) { + /** + * A Single item of selectable dialog list. + * + * @param key The resource identifier of the string resource to be displayed. + * @param icon Resource identifier of the icon Drawable. + * @param isAlert Determine whether the item text uses an error color. If it sets true, the text color will be shown as an error color. + */ + public DialogListItem(@StringRes int key, @DrawableRes int icon, boolean isAlert) { this.key = key; this.icon = icon; this.isAlert = isAlert; } - public DialogListItem(int key, int icon, boolean isAlert, boolean disabled) { + /** + * A Single item of selectable dialog list. + * + * @param key the resource identifier of the string resource to be displayed. + * @param icon Resource identifier of the icon Drawable. + * @param isAlert Determine whether the item text uses an error color. If it sets true, the text color will be shown as an error color. + * @param disabled Determine whether to disable the item. + */ + public DialogListItem(@StringRes int key, @DrawableRes int icon, boolean isAlert, boolean disabled) { this.key = key; this.icon = icon; this.isAlert = isAlert; this.disabled = disabled; } + /** + * Returns a key of item. + * + * @return String resource id. + */ public int getKey() { return key; } + /** + * Returns an icon of item. + * + * @return Drawable resource id. + */ public int getIcon() { return icon; } + /** + * Returns the item text uses error color. + * + * @return true if the text color uses error color, false otherwise. + */ public boolean isAlert() { return isAlert; } + /** + * Returns the item is disabled. + * + * @return true if the item is disabled, false otherwise. + */ public boolean isDisabled() { return disabled; } diff --git a/uikit/src/main/java/com/sendbird/uikit/utils/DialogUtils.java b/uikit/src/main/java/com/sendbird/uikit/utils/DialogUtils.java index 2fe5d6d9..95f4010f 100644 --- a/uikit/src/main/java/com/sendbird/uikit/utils/DialogUtils.java +++ b/uikit/src/main/java/com/sendbird/uikit/utils/DialogUtils.java @@ -35,7 +35,7 @@ private DialogUtils(){ public static SendBirdDialogFragment buildItems(String title, int dialogWidth, DialogListItem[] items, - OnItemClickListener itemClickListener) { + OnItemClickListener itemClickListener) { return new SendBirdDialogFragment.Builder() .setDialogWidth(dialogWidth) .setTitle(title) @@ -44,7 +44,7 @@ public static SendBirdDialogFragment buildItems(String title, } public static SendBirdDialogFragment buildItemsBottom(DialogListItem[] items, - OnItemClickListener itemClickListener) { + OnItemClickListener itemClickListener) { return new SendBirdDialogFragment.Builder() .setDialogGravity(SendBirdDialogFragment.DialogGravity.BOTTOM) .setItems(items, itemClickListener, SendBirdDialogFragment.ItemIconGravity.START) @@ -105,7 +105,7 @@ public static SendBirdDialogFragment buildContentViewTop(View contentView) { public static SendBirdDialogFragment buildContentViewAndItems(View contentView, DialogListItem[] items, - OnItemClickListener itemClickListener) { + OnItemClickListener itemClickListener) { return new SendBirdDialogFragment.Builder() .setDialogGravity(SendBirdDialogFragment.DialogGravity.BOTTOM) .setContentView(contentView) diff --git a/uikit/src/main/java/com/sendbird/uikit/vm/ChannelListViewModel.java b/uikit/src/main/java/com/sendbird/uikit/vm/ChannelListViewModel.java index 61255356..a6e42505 100644 --- a/uikit/src/main/java/com/sendbird/uikit/vm/ChannelListViewModel.java +++ b/uikit/src/main/java/com/sendbird/uikit/vm/ChannelListViewModel.java @@ -1,6 +1,7 @@ package com.sendbird.uikit.vm; import androidx.annotation.NonNull; +import androidx.annotation.Nullable; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; @@ -25,6 +26,7 @@ public class ChannelListViewModel extends BaseViewModel implements PagerRecyclerView.Pageable>, GroupChannelCollectionHandler { + @Nullable private GroupChannelCollection collection; @NonNull private final MutableLiveData> channelList = new MutableLiveData<>(); @@ -48,6 +50,7 @@ private synchronized void disposeChannelCollection() { } private void notifyChannelChanged() { + if (collection == null) return; List newList = collection.getChannelList(); changeAlertStatusIfEmpty(newList.size() == 0 ? StatusFrameView.Status.EMPTY : StatusFrameView.Status.NONE); channelList.postValue(newList); @@ -86,6 +89,7 @@ public LiveData getStatusFrame() { @Override public boolean hasNext() { + if (collection == null) return false; return collection.hasMore(); } @@ -128,6 +132,7 @@ private List loadMore() throws Exception { } private List loadMoreBlocking() throws Exception { + if (!hasNext() || collection == null) return Collections.emptyList(); final CountDownLatch lock = new CountDownLatch(1); final AtomicReference error = new AtomicReference<>(); final AtomicReference> channelListRef = new AtomicReference<>(); @@ -143,6 +148,7 @@ private List loadMoreBlocking() throws Exception { } private boolean hasData() { + if (collection == null) return false; return collection.getChannelList().size() > 0; } diff --git a/uikit/src/main/java/com/sendbird/uikit/vm/ChannelViewModel.java b/uikit/src/main/java/com/sendbird/uikit/vm/ChannelViewModel.java index 183aea58..dcc823b1 100644 --- a/uikit/src/main/java/com/sendbird/uikit/vm/ChannelViewModel.java +++ b/uikit/src/main/java/com/sendbird/uikit/vm/ChannelViewModel.java @@ -57,6 +57,7 @@ public class ChannelViewModel extends BaseViewModel implements PagerRecyclerView private final GroupChannel channel; private final MessageList cachedMessages = new MessageList(); + @Nullable private MessageCollection collection; private MessageCollectionHandler handler; private boolean needToLoadMessageCache = true; @@ -131,7 +132,7 @@ private synchronized void initMessageCollection(final long startingPoint, @NonNu // If the collection starts with a starting point value, not MAX_VALUE, // the message should be requested the newest messages at once because there may be no new messages in the cache private void loadLatestMessagesForCache() { - if (!needToLoadMessageCache || this.collection.getStartingPoint() == Long.MAX_VALUE) return; + if (!needToLoadMessageCache || (this.collection != null && this.collection.getStartingPoint() == Long.MAX_VALUE)) return; final MessageCollection syncCollection = new MessageCollection.Builder(channel, new MessageListParams()).build(); syncCollection.loadPrevious((messages, e) -> { if (e == null) { @@ -189,15 +190,18 @@ public LiveData getStatusFrame() { @Override public boolean hasNext() { + if (collection == null) return false; return collection.hasNext(); } @Override public boolean hasPrevious() { + if (collection == null) return false; return collection.hasPrevious(); } public long getStartingPoint() { + if (collection == null) return Long.MAX_VALUE; return collection.getStartingPoint(); } @@ -211,7 +215,7 @@ private synchronized void notifyChannelDataChanged() { private synchronized void notifyDataSetChanged(@NonNull String traceName) { Logger.d(">> ChannelViewModel::notifyDataSetChanged(), size = %s, action=%s", cachedMessages.size(), traceName); final List copiedList = cachedMessages.toList(); - if (!hasNext()) { + if (!hasNext() && collection != null) { copiedList.addAll(0, collection.getPendingMessages()); copiedList.addAll(0, collection.getFailedMessages()); } @@ -449,19 +453,21 @@ public void deleteMessage(@NonNull BaseMessage message) { Logger.i("++ deleted message : %s", message); }); } else if (status == BaseMessage.SendingStatus.FAILED) { - collection.removeFailedMessages(Collections.singletonList(message), (requestIds, e) -> { - if (e != null) { - Logger.e(e); - errorToast.setValue(R.string.sb_text_error_delete_message); - return; - } - - Logger.i("++ deleted message : %s", message); - notifyDataSetChanged(StringSet.ACTION_FAILED_MESSAGE_REMOVED); - if (message instanceof FileMessage) { - PendingMessageRepository.getInstance().clearFileInfo((FileMessage) message); - } - }); + if (collection != null) { + collection.removeFailedMessages(Collections.singletonList(message), (requestIds, e) -> { + if (e != null) { + Logger.e(e); + errorToast.setValue(R.string.sb_text_error_delete_message); + return; + } + + Logger.i("++ deleted message : %s", message); + notifyDataSetChanged(StringSet.ACTION_FAILED_MESSAGE_REMOVED); + if (message instanceof FileMessage) { + PendingMessageRepository.getInstance().clearFileInfo((FileMessage) message); + } + }); + } } } @@ -491,32 +497,34 @@ public synchronized void loadInitial(final long startingPoint, @NonNull final Me messageLoadState.postValue(MessageLoadState.LOAD_STARTED); cachedMessages.clear(); - collection.initialize(MessageCollectionInitPolicy.CACHE_AND_REPLACE_BY_API, new MessageCollectionInitHandler() { - @Override - public void onCacheResult(@Nullable List cachedList, @Nullable SendBirdException e) { - if (e == null && cachedList != null && cachedList.size() > 0) { - cachedMessages.addAll(cachedList); - notifyDataSetChanged(StringSet.ACTION_INIT_FROM_CACHE); + if (collection != null) { + collection.initialize(MessageCollectionInitPolicy.CACHE_AND_REPLACE_BY_API, new MessageCollectionInitHandler() { + @Override + public void onCacheResult(@Nullable List cachedList, @Nullable SendBirdException e) { + if (e == null && cachedList != null && cachedList.size() > 0) { + cachedMessages.addAll(cachedList); + notifyDataSetChanged(StringSet.ACTION_INIT_FROM_CACHE); + } } - } - @Override - public void onApiResult(@Nullable List apiResultList, @Nullable SendBirdException e) { - if (e == null && apiResultList != null && apiResultList.size() > 0) { - cachedMessages.clear(); - cachedMessages.addAll(apiResultList); - notifyDataSetChanged(StringSet.ACTION_INIT_FROM_REMOTE); - markAsRead(); + @Override + public void onApiResult(@Nullable List apiResultList, @Nullable SendBirdException e) { + if (e == null && apiResultList != null && apiResultList.size() > 0) { + cachedMessages.clear(); + cachedMessages.addAll(apiResultList); + notifyDataSetChanged(StringSet.ACTION_INIT_FROM_REMOTE); + markAsRead(); + } + messageLoadState.postValue(MessageLoadState.LOAD_ENDED); } - messageLoadState.postValue(MessageLoadState.LOAD_ENDED); - } - }); + }); + } } @WorkerThread @Override public List loadPrevious() throws Exception { - if (!hasPrevious()) return Collections.emptyList(); + if (!hasPrevious() || collection == null) return Collections.emptyList(); Logger.i(">> ChannelViewModel::loadPrevious()"); final AtomicReference> result = new AtomicReference<>(); @@ -525,10 +533,12 @@ public List loadPrevious() throws Exception { messageLoadState.postValue(MessageLoadState.LOAD_STARTED); collection.loadPrevious((messages, e) -> { - Logger.d("++ privious size = %s", messages == null ? 0: messages.size()); + Logger.d("++ privious size = %s", messages == null ? 0 : messages.size()); try { if (e == null) { - cachedMessages.addAll(messages); + if (messages != null) { + cachedMessages.addAll(messages); + } result.set(messages); notifyDataSetChanged(StringSet.ACTION_PREVIOUS); } @@ -547,7 +557,7 @@ public List loadPrevious() throws Exception { @WorkerThread @Override public List loadNext() throws Exception { - if (!hasNext()) return Collections.emptyList(); + if (!hasNext() || collection == null) return Collections.emptyList(); Logger.i(">> ChannelViewModel::loadNext()"); final AtomicReference> result = new AtomicReference<>(); diff --git a/uikit/src/main/java/com/sendbird/uikit/vm/OpenChannelViewModel.java b/uikit/src/main/java/com/sendbird/uikit/vm/OpenChannelViewModel.java index 45b651fb..6d021ef6 100644 --- a/uikit/src/main/java/com/sendbird/uikit/vm/OpenChannelViewModel.java +++ b/uikit/src/main/java/com/sendbird/uikit/vm/OpenChannelViewModel.java @@ -6,6 +6,7 @@ import androidx.lifecycle.LifecycleObserver; import androidx.lifecycle.LiveData; import androidx.lifecycle.MutableLiveData; +import androidx.lifecycle.Observer; import androidx.lifecycle.OnLifecycleEvent; import com.sendbird.android.BaseChannel; @@ -40,8 +41,8 @@ public class OpenChannelViewModel extends BaseViewModel implements LifecycleObserver, PagerRecyclerView.Pageable> { private static final int DEFAULT_MESSAGE_LOAD_SIZE = 40; - private final String CONNECTION_HANDLER_ID = "CONNECTION_HANDLER_OPEN_CHAT" + System.currentTimeMillis();; - private final String CHANNEL_HANDLER_ID = "CHANNEL_HANDLER_OPEN_CHANNEL_CHAT" + System.currentTimeMillis();; + private final String CONNECTION_HANDLER_ID = "CONNECTION_HANDLER_OPEN_CHAT" + System.currentTimeMillis(); + private final String CHANNEL_HANDLER_ID = "CHANNEL_HANDLER_OPEN_CHANNEL_CHAT" + System.currentTimeMillis(); private final ExecutorService worker = Executors.newSingleThreadExecutor(); private final MutableLiveData> messageList = new MutableLiveData<>(); private final MessageList messageCollection = new MessageList(); @@ -52,19 +53,34 @@ public class OpenChannelViewModel extends BaseViewModel implements LifecycleObse private final MutableLiveData messageLoadState = new MutableLiveData<>(); private final MutableLiveData statusFrame = new MutableLiveData<>(); private final OpenChannel channel; + private final Observer pendingStatusObserver; private boolean hasPrevious = true; OpenChannelViewModel(@NonNull OpenChannel openChannel, @Nullable MessageListParams params) { super(); this.channel = openChannel; - messageListParams = params != null ? params : new MessageListParams(); - messageListParams.setReverse(true); - messageListParams.setNextResultSize(0); - messageListParams.setIncludeReactions(ReactionUtils.useReaction(openChannel)); + this.messageListParams = params != null ? params : new MessageListParams(); + this.messageListParams.setReverse(true); + this.messageListParams.setNextResultSize(0); + this.messageListParams.setIncludeReactions(ReactionUtils.useReaction(openChannel)); if (messageListParams.getPreviousResultSize() <= 0) { messageListParams.setPreviousResultSize(DEFAULT_MESSAGE_LOAD_SIZE); } + + this.pendingStatusObserver = message -> { + Logger.d("__ pending message events, message = %s", message.getMessage()); + if (message.getChannelUrl().equals(channel.getUrl())) { + final BaseMessage.SendingStatus sendingStatus = message.getSendingStatus(); + Logger.i("__ pending status of message is changed, pending status = %s ", sendingStatus); + if (sendingStatus == BaseMessage.SendingStatus.SUCCEEDED) { + messageCollection.add(message); + } + notifyDataSetChanged(); + } + }; + PendingMessageRepository.getInstance().addPendingMessageStatusChanged(pendingStatusObserver); + registerChannelHandler(); } private boolean isCurrentChannel(@NonNull String channelUrl) { @@ -99,8 +115,7 @@ public LiveData getMessageLoadState() { return messageLoadState; } - @OnLifecycleEvent(Lifecycle.Event.ON_CREATE) - private void onCreate() { + private void registerChannelHandler() { SendBird.addConnectionHandler(CONNECTION_HANDLER_ID, new SendBird.ConnectionHandler() { @Override public void onReconnectStarted() { @@ -295,12 +310,6 @@ private void onResume() { requestChangeLogs(channel); } - @OnLifecycleEvent(Lifecycle.Event.ON_DESTROY) - private void onDestroy() { - SendBird.removeConnectionHandler(CONNECTION_HANDLER_ID); - SendBird.removeChannelHandler(CHANNEL_HANDLER_ID); - } - public void load() { worker.execute(() -> { try { @@ -340,8 +349,11 @@ private List loadPrevious(long ts) throws Exception { @Override protected void onCleared() { super.onCleared(); - Logger.dev("-- onCleared ChannelViewModel"); + Logger.i("-- onCleared ChannelViewModel"); + SendBird.removeConnectionHandler(CONNECTION_HANDLER_ID); + SendBird.removeChannelHandler(CHANNEL_HANDLER_ID); worker.shutdownNow(); + PendingMessageRepository.getInstance().removePendingMessageStatusObserver(pendingStatusObserver); } private void notifyDataSetChanged() { @@ -415,21 +427,17 @@ public void sendUserMessage(@NonNull UserMessageParams params) { if (e != null) { Logger.e(e); PendingMessageRepository.getInstance().updatePendingMessage(channelUrl, message); - notifyDataSetChanged(); return; } if (messageListParams.belongsTo(message)) { Logger.i("++ sent message : %s", message); - messageCollection.add(message); PendingMessageRepository.getInstance().removePendingMessage(channelUrl, message); - notifyDataSetChanged(); } }); if (pendingUserMessage != null) { if (messageListParams.belongsTo(pendingUserMessage)) { PendingMessageRepository.getInstance().addPendingMessage(channelUrl, pendingUserMessage); - notifyDataSetChanged(); } else { errorToast.postValue(R.string.sb_text_error_message_filtered); } @@ -444,7 +452,6 @@ public void sendFileMessage(@NonNull FileMessageParams params, @NonNull FileInfo Logger.e(ee); if (message != null) { PendingMessageRepository.getInstance().updatePendingMessage(channelUrl, message); - notifyDataSetChanged(); } if (ee.getMessage() != null) { errorToast.postValue(R.string.sb_text_error_send_message); @@ -455,9 +462,7 @@ public void sendFileMessage(@NonNull FileMessageParams params, @NonNull FileInfo if (messageListParams.belongsTo(message)) { Logger.i("++ sent message : %s", message); //if (file.exists()) file.deleteOnExit(); - messageCollection.add(message); PendingMessageRepository.getInstance().removePendingMessage(channelUrl, message); - notifyDataSetChanged(); } }); @@ -465,7 +470,6 @@ public void sendFileMessage(@NonNull FileMessageParams params, @NonNull FileInfo if (messageListParams.belongsTo(pendingFileMessage)) { PendingMessageRepository.getInstance().addPendingMessage(channelUrl, pendingFileMessage); PendingMessageRepository.getInstance().addFileInfo(pendingFileMessage, fileInfo); - notifyDataSetChanged(); } else { errorToast.postValue(R.string.sb_text_error_message_filtered); } @@ -480,17 +484,13 @@ public void resendMessage(@NonNull BaseMessage message) { Logger.e(e); errorToast.postValue(R.string.sb_text_error_resend_message); PendingMessageRepository.getInstance().updatePendingMessage(channelUrl, message12); - notifyDataSetChanged(); return; } Logger.i("__ resent message : %s", message12); - messageCollection.add(message12); PendingMessageRepository.getInstance().removePendingMessage(channelUrl, message12); - notifyDataSetChanged(); }); PendingMessageRepository.getInstance().updatePendingMessage(channelUrl, pendingMessage); - notifyDataSetChanged(); } else if (message instanceof FileMessage) { FileInfo info = PendingMessageRepository.getInstance().getFileInfo(message); Logger.d("++ file info=%s", info); @@ -500,18 +500,14 @@ public void resendMessage(@NonNull BaseMessage message) { Logger.e(e1); errorToast.postValue(R.string.sb_text_error_resend_message); PendingMessageRepository.getInstance().updatePendingMessage(channelUrl, message1); - notifyDataSetChanged(); return; } Logger.i("__ resent file message : %s", message1); //if (file.exists()) file.deleteOnExit(); - messageCollection.add(message1); PendingMessageRepository.getInstance().removePendingMessage(channelUrl, message1); - notifyDataSetChanged(); }); PendingMessageRepository.getInstance().updatePendingMessage(channelUrl, pendingMessage); - notifyDataSetChanged(); } } @@ -543,7 +539,6 @@ public void deleteMessage(@NonNull BaseMessage message) { }); } else { PendingMessageRepository.getInstance().removePendingMessage(message.getChannelUrl(), message); - notifyDataSetChanged(); } } diff --git a/uikit/src/main/java/com/sendbird/uikit/vm/PendingMessageRepository.java b/uikit/src/main/java/com/sendbird/uikit/vm/PendingMessageRepository.java index 6a700309..19e9ea2f 100644 --- a/uikit/src/main/java/com/sendbird/uikit/vm/PendingMessageRepository.java +++ b/uikit/src/main/java/com/sendbird/uikit/vm/PendingMessageRepository.java @@ -2,6 +2,7 @@ import androidx.annotation.NonNull; import androidx.annotation.Nullable; +import androidx.lifecycle.Observer; import com.sendbird.android.BaseMessage; import com.sendbird.android.FileMessage; @@ -14,6 +15,23 @@ public class PendingMessageRepository { + @NonNull + private final List> pendingMessageStatusChanged = new ArrayList<>(); + + boolean addPendingMessageStatusChanged(@NonNull Observer subscriber) { + return pendingMessageStatusChanged.add(subscriber); + } + + boolean removePendingMessageStatusObserver(@NonNull Observer subscriber) { + return pendingMessageStatusChanged.remove(subscriber); + } + + private synchronized void notifyPendingMessageStatusChanged(@NonNull BaseMessage message) { + for (Observer subscriber : pendingMessageStatusChanged) { + subscriber.onChanged(message); + } + } + private PendingMessageRepository() {} private static class PendingMessageManagerHolder { @@ -62,6 +80,7 @@ void addPendingMessage(@NonNull String channelUrl, @NonNull BaseMessage message) } pendingMessages.add(0, message); pendingMessageMap.put(channelUrl, pendingMessages); + notifyPendingMessageStatusChanged(message); } void updatePendingMessage(@NonNull String channelUrl, @Nullable BaseMessage message) { @@ -79,6 +98,7 @@ void updatePendingMessage(@NonNull String channelUrl, @Nullable BaseMessage mess } pendingMessageMap.put(channelUrl, pendingMessages); } + notifyPendingMessageStatusChanged(message); } boolean removePendingMessage(@NonNull String channelUrl, @NonNull BaseMessage message) { @@ -104,6 +124,10 @@ boolean removePendingMessage(@NonNull String channelUrl, @NonNull BaseMessage me } pendingMessageMap.put(channelUrl, pendingMessages); } + + if (isRemoved) { + notifyPendingMessageStatusChanged(message); + } return isRemoved; } diff --git a/uikit/src/main/java/com/sendbird/uikit/widgets/MessageInputView.java b/uikit/src/main/java/com/sendbird/uikit/widgets/MessageInputView.java index a124e2a1..995942c0 100644 --- a/uikit/src/main/java/com/sendbird/uikit/widgets/MessageInputView.java +++ b/uikit/src/main/java/com/sendbird/uikit/widgets/MessageInputView.java @@ -55,7 +55,7 @@ public class MessageInputView extends FrameLayout { private OnInputTextChangedListener editModeTextChangedListener; private OnInputModeChangedListener inputModeChangedListener; private Mode mode; - private int addButtonVisibilityBeforeEditMode = VISIBLE; + private int addButtonVisibility = VISIBLE; private boolean showSendButtonAlways; public enum Mode { @@ -185,16 +185,15 @@ public void setInputMode(@NonNull final Mode mode) { if (Mode.EDIT == mode) { setQuoteReplyPanelVisibility(GONE); setEditPanelVisibility(VISIBLE); - addButtonVisibilityBeforeEditMode = binding.ibtnAdd.getVisibility(); - setAddButtonVisibility(GONE); + binding.ibtnAdd.setVisibility(GONE); } else if (Mode.QUOTE_REPLY == mode) { setQuoteReplyPanelVisibility(VISIBLE); setEditPanelVisibility(GONE); - setAddButtonVisibility(addButtonVisibilityBeforeEditMode); + setAddButtonVisibility(addButtonVisibility); } else { setQuoteReplyPanelVisibility(GONE); setEditPanelVisibility(GONE); - setAddButtonVisibility(addButtonVisibilityBeforeEditMode); + setAddButtonVisibility(addButtonVisibility); } if (inputModeChangedListener != null) { @@ -282,6 +281,7 @@ public void showSendButtonAlways(boolean always) { } public void setAddButtonVisibility(int visibility) { + addButtonVisibility = visibility; binding.ibtnAdd.setVisibility(visibility); }