/**
* Wire
* Copyright (C) 2016 Wire Swiss GmbH
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
package com.waz.zclient.pages.main.conversation.views;
import android.annotation.SuppressLint;
import android.content.Context;
import android.support.annotation.NonNull;
import android.support.design.widget.BottomSheetDialog;
import android.view.View;
import android.view.ViewGroup;
import android.widget.LinearLayout;
import android.widget.TextView;
import com.waz.api.Asset;
import com.waz.api.AssetStatus;
import com.waz.api.Message;
import com.waz.zclient.R;
import com.waz.zclient.core.api.scala.ModelObserver;
import com.waz.zclient.utils.ViewUtils;
import java.util.Set;
public class MessageBottomSheetDialog extends BottomSheetDialog {
private final Message message;
private final Callback callback;
private Set<MessageAction> chosenOperations = null;
public enum MessageAction {
FORWARD(R.id.message_bottom_menu_item_forward, R.string.glyph__share, R.string.message_bottom_menu_action_forward),
COPY(R.id.message_bottom_menu_item_copy, R.string.glyph__copy, R.string.message_bottom_menu_action_copy),
DELETE(R.id.message_bottom_menu_item_delete, R.string.glyph__trash, R.string.message_bottom_menu_action_delete),
DELETE_LOCAL(R.id.message_bottom_menu_item_delete_local, R.string.glyph__delete_me, R.string.message_bottom_menu_action_delete_local),
DELETE_GLOBAL(R.id.message_bottom_menu_item_delete_global, R.string.glyph__delete_everywhere, R.string.message_bottom_menu_action_delete_global),
LIKE(R.id.message_bottom_menu_item_like, R.string.glyph__like, R.string.message_bottom_menu_action_like),
UNLIKE(R.id.message_bottom_menu_item_unlike, R.string.glyph__liked, R.string.message_bottom_menu_action_unlike),
SAVE(R.id.message_bottom_menu_item_save, R.string.glyph__download, R.string.message_bottom_menu_action_save),
OPEN_FILE(R.id.message_bottom_menu_item_open_file, R.string.glyph__file, R.string.message_bottom_menu_action_open),
REVEAL(R.id.message_bottom_menu_item_reveal, R.string.glyph__view, R.string.message_bottom_menu_action_reveal),
EDIT(R.id.message_bottom_menu_item_edit, R.string.glyph__edit, R.string.message_bottom_menu_action_edit);
public int resId;
public int glyphResId;
public int stringId;
MessageAction(int resId, int glyphResId, int stringId) {
this.resId = resId;
this.glyphResId = glyphResId;
this.stringId = stringId;
}
}
public MessageBottomSheetDialog(@NonNull Context context, int theme, @NonNull Message message, boolean isMemberOfConversation, boolean isCollection, Callback callback) {
super(context, theme);
this.message = message;
this.callback = callback;
init(isMemberOfConversation, isCollection, true);
}
public MessageBottomSheetDialog(@NonNull Context context, int theme, @NonNull Message message, boolean isMemberOfConversation, boolean isCollection, Callback callback, Set<MessageAction> operations) {
super(context, theme);
this.message = message;
this.callback = callback;
chosenOperations = operations;
init(isMemberOfConversation, isCollection, false);
}
private void updateOptions(LinearLayout view, boolean isMemberOfConversation, boolean isCollection, boolean deleteCollapsed) {
view.removeAllViews();
if (isCopyAllowed()) {
addAction(view, MessageAction.COPY);
}
if (isOpenFileAllowed(isCollection)) {
addAction(view, MessageAction.OPEN_FILE);
}
if (isEditAllowed(isMemberOfConversation, isCollection)) {
addAction(view, MessageAction.EDIT);
}
if (isMemberOfConversation && isLikeAllowed()) {
if (message.isLikedByThisUser()) {
addAction(view, MessageAction.UNLIKE);
} else {
addAction(view, MessageAction.LIKE);
}
}
if (isSaveAllowed()) {
addAction(view, MessageAction.SAVE);
}
if (isForwardAllowed()) {
addAction(view, MessageAction.FORWARD);
}
if (deleteCollapsed && (isDeleteLocalAllowed() || isDeleteForEveryoneAllowed(isMemberOfConversation))) {
addAction(view, MessageAction.DELETE);
} else {
if (isDeleteLocalAllowed()) {
addAction(view, MessageAction.DELETE_LOCAL);
}
if (isDeleteForEveryoneAllowed(isMemberOfConversation)) {
addAction(view, MessageAction.DELETE_GLOBAL);
}
}
if (isRevealAllowed(isCollection)) {
addAction(view, MessageAction.REVEAL);
}
}
@SuppressLint("InflateParams")
private void init(final boolean isMemberOfConversation, final boolean isCollection, final boolean deleteCollapsed) {
final LinearLayout view = (LinearLayout) getLayoutInflater().inflate(R.layout.message__bottom__menu, null);
ModelObserver<Asset> assetModelObserver = new ModelObserver<Asset>() {
@Override
public void updated(Asset model) {
if (!model.isEmpty()) {
updateOptions(view, isMemberOfConversation, isCollection, deleteCollapsed);
}
}
};
assetModelObserver.setAndUpdate(message.getAsset());
updateOptions(view, isMemberOfConversation, isCollection, deleteCollapsed);
setContentView(view);
}
private void addAction(ViewGroup root, final MessageAction action) {
LinearLayout row = (LinearLayout) getLayoutInflater().inflate(R.layout.message__bottom__menu__row, root, false);
row.setId(action.resId);
TextView icon = (TextView) row.getChildAt(0);
icon.setText(action.glyphResId);
TextView label = (TextView) row.getChildAt(1);
label.setText(action.stringId);
row.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
callback.onAction(action, message);
dismiss();
}
});
LinearLayout.LayoutParams params = new LinearLayout.LayoutParams(ViewGroup.LayoutParams.MATCH_PARENT, ViewUtils.toPx(getContext(), 48));
root.addView(row, params);
}
private boolean isLikeAllowed() {
if (chosenOperations != null && !chosenOperations.contains(MessageAction.LIKE)) {
return false;
}
if (message.isEphemeral()) {
return false;
}
switch (message.getMessageType()) {
case ANY_ASSET:
case ASSET:
case AUDIO_ASSET:
case LOCATION:
case TEXT:
case TEXT_EMOJI_ONLY:
case RICH_MEDIA:
case VIDEO_ASSET:
return true;
default:
return false;
}
}
private boolean isSaveAllowed() {
if (chosenOperations != null && !chosenOperations.contains(MessageAction.SAVE)) {
return false;
}
if (message.isEphemeral()) {
return false;
}
switch (message.getMessageType()) {
// Only image supported ATM, need to handle Audio/File/Video
case ASSET:
return true;
case AUDIO_ASSET:
case VIDEO_ASSET:
return message.getAsset().getStatus() == AssetStatus.UPLOAD_DONE ||
message.getAsset().getStatus() == AssetStatus.DOWNLOAD_DONE;
default:
return false;
}
}
private boolean isOpenFileAllowed(boolean isCollection) {
if (chosenOperations != null && !chosenOperations.contains(MessageAction.OPEN_FILE)) {
return false;
}
if (message.isEphemeral()) {
return false;
}
if (isCollection) {
return false;
}
switch (message.getMessageType()) {
case ANY_ASSET:
return message.getAsset().getStatus() == AssetStatus.UPLOAD_DONE ||
message.getAsset().getStatus() == AssetStatus.DOWNLOAD_DONE;
default:
return false;
}
}
private boolean isCopyAllowed() {
if (chosenOperations != null && !chosenOperations.contains(MessageAction.COPY)) {
return false;
}
if (message.isEphemeral()) {
return false;
}
switch (message.getMessageType()) {
case TEXT:
case TEXT_EMOJI_ONLY:
case RICH_MEDIA:
return true;
default:
return false;
}
}
private boolean isForwardAllowed() {
if (chosenOperations != null && !chosenOperations.contains(MessageAction.FORWARD)) {
return false;
}
if (message.isEphemeral()) {
return false;
}
switch (message.getMessageType()) {
case TEXT:
case TEXT_EMOJI_ONLY:
case RICH_MEDIA:
return true;
case ANY_ASSET:
case AUDIO_ASSET:
case VIDEO_ASSET:
return message.getAsset().getStatus() == AssetStatus.UPLOAD_DONE ||
message.getAsset().getStatus() == AssetStatus.DOWNLOAD_DONE;
case ASSET:
// TODO: Once https://wearezeta.atlassian.net/browse/CM-976 is resolved, we should handle image asset like any other asset
return true;
default:
return false;
}
}
private boolean isEditAllowed(boolean isMemberOfConversation, boolean isCollection) {
if (chosenOperations != null && !chosenOperations.contains(MessageAction.EDIT)) {
return false;
}
if (!isMemberOfConversation ||
message.isEphemeral() ||
!message.getUser().isMe() ||
isCollection) {
return false;
}
switch (message.getMessageType()) {
case TEXT_EMOJI_ONLY:
case TEXT:
case RICH_MEDIA:
return true;
default:
return false;
}
}
private boolean isDeleteForEveryoneAllowed(boolean isMemberOfConversation) {
if (chosenOperations != null && !chosenOperations.contains(MessageAction.DELETE_GLOBAL)) {
return false;
}
if (!isMemberOfConversation ||
!message.getUser().isMe()) {
return false;
}
switch (message.getMessageType()) {
case TEXT:
case ANY_ASSET:
case ASSET:
case AUDIO_ASSET:
case VIDEO_ASSET:
case KNOCK:
case LOCATION:
case RICH_MEDIA:
case TEXT_EMOJI_ONLY:
return true;
default:
return false;
}
}
private boolean isDeleteLocalAllowed() {
return !(chosenOperations != null && !chosenOperations.contains(MessageAction.DELETE_LOCAL));
}
private boolean isRevealAllowed(Boolean isCollection) {
return !(!isCollection || (chosenOperations != null && !chosenOperations.contains(MessageAction.REVEAL)));
}
public interface Callback {
void onAction(MessageAction action, Message message);
}
}