com.qiscus.sdk.ui.fragment.QiscusBaseChatFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.qiscus.sdk.ui.fragment.QiscusBaseChatFragment.java

Source

/*
 * Copyright (c) 2016 Qiscus.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.qiscus.sdk.ui.fragment;

import android.app.Activity;
import android.content.ActivityNotFoundException;
import android.content.Context;
import android.content.Intent;
import android.graphics.PorterDuff;
import android.graphics.drawable.Drawable;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.provider.MediaStore;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v4.app.NotificationManagerCompat;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.FileProvider;
import android.support.v4.widget.SwipeRefreshLayout;
import android.support.v7.app.AlertDialog;
import android.support.v7.widget.LinearLayoutManager;
import android.text.Editable;
import android.text.TextWatcher;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.view.animation.Animation;
import android.view.animation.AnimationUtils;
import android.widget.EditText;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.qiscus.sdk.Qiscus;
import com.qiscus.sdk.R;
import com.qiscus.sdk.data.local.QiscusCacheManager;
import com.qiscus.sdk.data.model.QiscusAccount;
import com.qiscus.sdk.data.model.QiscusChatConfig;
import com.qiscus.sdk.data.model.QiscusChatRoom;
import com.qiscus.sdk.data.model.QiscusComment;
import com.qiscus.sdk.data.remote.QiscusPusherApi;
import com.qiscus.sdk.filepicker.FilePickerBuilder;
import com.qiscus.sdk.filepicker.FilePickerConst;
import com.qiscus.sdk.presenter.QiscusChatPresenter;
import com.qiscus.sdk.ui.QiscusAccountLinkingActivity;
import com.qiscus.sdk.ui.QiscusPhotoViewerActivity;
import com.qiscus.sdk.ui.adapter.QiscusBaseChatAdapter;
import com.qiscus.sdk.ui.view.QiscusAudioRecorderView;
import com.qiscus.sdk.ui.view.QiscusChatButtonView;
import com.qiscus.sdk.ui.view.QiscusChatScrollListener;
import com.qiscus.sdk.ui.view.QiscusRecyclerView;
import com.qiscus.sdk.util.QiscusAndroidUtil;
import com.qiscus.sdk.util.QiscusFileUtil;
import com.qiscus.sdk.util.QiscusImageUtil;
import com.qiscus.sdk.util.QiscusPermissionsUtil;
import com.qiscus.sdk.util.QiscusRawDataExtractor;
import com.trello.rxlifecycle.components.support.RxFragment;
import com.vanniktech.emoji.EmojiEditText;
import com.vanniktech.emoji.EmojiPopup;

import org.json.JSONException;
import org.json.JSONObject;

import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;

/**
 * Created on : September 28, 2016
 * Author     : zetbaitsu
 * Name       : Zetra
 * Email      : zetra@mail.ugm.ac.id
 * GitHub     : https://github.com/zetbaitsu
 * LinkedIn   : https://id.linkedin.com/in/zetbaitsu
 */
public abstract class QiscusBaseChatFragment<T extends QiscusBaseChatAdapter> extends RxFragment
        implements SwipeRefreshLayout.OnRefreshListener, QiscusChatScrollListener.Listener,
        QiscusChatPresenter.View, QiscusAudioRecorderView.RecordListener, QiscusPermissionsUtil.PermissionCallbacks,
        QiscusChatButtonView.ChatButtonClickListener {

    protected static final int RC_PERMISSIONS = 127;

    private static final String[] PERMISSIONS = { "android.permission.WRITE_EXTERNAL_STORAGE",
            "android.permission.READ_EXTERNAL_STORAGE", "android.permission.RECORD_AUDIO" };

    private static final String AUDIO_PERMISSION = "android.permission.RECORD_AUDIO";
    private static final String[] FILE_PERMISSION = { "android.permission.WRITE_EXTERNAL_STORAGE",
            "android.permission.READ_EXTERNAL_STORAGE", };

    protected static final String CHAT_ROOM_DATA = "chat_room_data";
    protected static final String EXTRA_STARTING_MESSAGE = "extra_starting_message";
    protected static final String EXTRA_SHARE_FILE = "extra_share_file";
    protected static final String COMMENTS_DATA = "saved_comments_data";
    protected static final int TAKE_PICTURE_REQUEST = 1;

    @NonNull
    protected ViewGroup rootView;
    @Nullable
    protected ViewGroup emptyChatHolder;
    @NonNull
    protected SwipeRefreshLayout swipeRefreshLayout;
    @NonNull
    protected QiscusRecyclerView messageRecyclerView;
    @Nullable
    protected ViewGroup messageInputPanel;
    @Nullable
    protected ViewGroup messageEditTextContainer;
    @NonNull
    protected EditText messageEditText;
    @NonNull
    protected ImageView sendButton;
    @Nullable
    protected View newMessageButton;
    @NonNull
    protected View loadMoreProgressBar;
    @Nullable
    protected ImageView emptyChatImageView;
    @Nullable
    protected TextView emptyChatTitleView;
    @Nullable
    protected TextView emptyChatDescView;
    @Nullable
    protected ViewGroup attachmentPanel;
    @Nullable
    protected View addImageLayout;
    @Nullable
    protected ImageView addImageButton;
    @Nullable
    protected TextView addImageTextView;
    @Nullable
    protected View takeImageLayout;
    @Nullable
    protected ImageView takeImageButton;
    @Nullable
    protected TextView takeImageTextView;
    @Nullable
    protected View addFileLayout;
    @Nullable
    protected ImageView addFileButton;
    @Nullable
    protected TextView addFileTextView;
    @Nullable
    protected View recordAudioLayout;
    @Nullable
    protected ImageView recordAudioButton;
    @Nullable
    protected TextView recordAudioTextView;
    @Nullable
    protected ImageView hideAttachmentButton;
    @Nullable
    protected ImageView toggleEmojiButton;
    @Nullable
    protected QiscusAudioRecorderView recordAudioPanel;

    protected QiscusChatConfig chatConfig;
    protected QiscusChatRoom qiscusChatRoom;
    protected String startingMessage;
    protected File shareFile;
    protected T chatAdapter;
    protected QiscusChatPresenter qiscusChatPresenter;
    protected Animation animation;
    protected LinearLayoutManager chatLayoutManager;
    private QiscusAccount qiscusAccount;
    private boolean fieldMessageEmpty = true;
    private CommentSelectedListener commentSelectedListener;
    private RoomChangedListener roomChangedListener;
    private EmojiPopup emojiPopup;

    @Nullable
    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = LayoutInflater.from(getActivity()).inflate(getResourceLayout(), container, false);
        onLoadView(view);
        return view;
    }

    protected abstract int getResourceLayout();

    protected void onLoadView(View view) {
        rootView = getRootView(view);
        emptyChatHolder = getEmptyChatHolder(view);
        swipeRefreshLayout = getSwipeRefreshLayout(view);
        messageRecyclerView = getMessageRecyclerView(view);
        messageInputPanel = getMessageInputPanel(view);
        messageEditTextContainer = getMessageEditTextContainer(view);
        messageEditText = getMessageEditText(view);
        sendButton = getSendButton(view);
        newMessageButton = getNewMessageButton(view);
        loadMoreProgressBar = getLoadMoreProgressBar(view);

        emptyChatImageView = getEmptyChatImageView(view);
        emptyChatTitleView = getEmptyChatTitleView(view);
        emptyChatDescView = getEmptyChatDescView(view);

        attachmentPanel = getAttachmentPanel(view);
        hideAttachmentButton = getHideAttachmentButton(view);

        addImageLayout = getAddImageLayout(view);
        addImageButton = getAddImageButton(view);
        addImageTextView = getAddImageTextView(view);

        takeImageLayout = getTakeImageLayout(view);
        takeImageButton = getTakeImageButton(view);
        takeImageTextView = getTakeImageTextView(view);

        addFileLayout = getAddFileLayout(view);
        addFileButton = getAddFileButton(view);
        addFileTextView = getAddFileTextView(view);

        recordAudioLayout = getRecordAudioLayout(view);
        recordAudioButton = getRecordAudioButton(view);
        recordAudioTextView = getRecordAudioTextView(view);

        toggleEmojiButton = getToggleEmojiButton(view);
        recordAudioPanel = getRecordAudioPanel(view);

        if (toggleEmojiButton != null && !(messageEditText instanceof EmojiEditText)) {
            throw new RuntimeException(
                    "Please use EmojiEditText as message text field if you want to using EmojiKeyboard.");
        }

        messageEditText.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {

            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                onMessageEditTextChanged(s);
            }

            @Override
            public void afterTextChanged(Editable s) {

            }
        });

        messageEditText.setOnClickListener(v -> {
            if (emojiPopup != null && emojiPopup.isShowing()) {
                toggleEmoji();
            }
        });

        sendButton.setOnClickListener(v -> {
            String message = messageEditText.getText().toString().trim();
            if (message.isEmpty()) {
                showAttachmentPanel();
            } else {
                sendMessage(message);
            }
        });

        if (newMessageButton != null) {
            newMessageButton.setOnClickListener(v -> scrollToBottom());
        }
        if (addImageButton != null) {
            addImageButton.setOnClickListener(v -> addImage());
        }
        if (takeImageButton != null) {
            takeImageButton.setOnClickListener(v -> takeImage());
        }
        if (addFileButton != null) {
            addFileButton.setOnClickListener(v -> addFile());
        }
        if (recordAudioButton != null) {
            recordAudioButton.setOnClickListener(v -> recordAudio());
        }
        if (toggleEmojiButton != null) {
            toggleEmojiButton.setOnClickListener(v -> toggleEmoji());
        }
        if (hideAttachmentButton != null) {
            hideAttachmentButton.setOnClickListener(v -> hideAttachmentPanel());
        }
        if (recordAudioPanel != null) {
            recordAudioPanel.setRecordListener(this);
        }
    }

    @NonNull
    protected abstract ViewGroup getRootView(View view);

    @Nullable
    protected abstract ViewGroup getEmptyChatHolder(View view);

    @NonNull
    protected abstract SwipeRefreshLayout getSwipeRefreshLayout(View view);

    @NonNull
    protected abstract QiscusRecyclerView getMessageRecyclerView(View view);

    @Nullable
    protected abstract ViewGroup getMessageInputPanel(View view);

    @Nullable
    protected abstract ViewGroup getMessageEditTextContainer(View view);

    @NonNull
    protected abstract EditText getMessageEditText(View view);

    @NonNull
    protected abstract ImageView getSendButton(View view);

    @Nullable
    protected abstract View getNewMessageButton(View view);

    @NonNull
    protected abstract View getLoadMoreProgressBar(View view);

    @Nullable
    protected abstract ImageView getEmptyChatImageView(View view);

    @Nullable
    protected abstract TextView getEmptyChatTitleView(View view);

    @Nullable
    protected abstract TextView getEmptyChatDescView(View view);

    @Nullable
    protected abstract ViewGroup getAttachmentPanel(View view);

    @Nullable
    protected abstract View getAddImageLayout(View view);

    @Nullable
    protected abstract ImageView getAddImageButton(View view);

    @Nullable
    protected TextView getAddImageTextView(View view) {
        return null;
    }

    @Nullable
    protected abstract View getTakeImageLayout(View view);

    @Nullable
    protected abstract ImageView getTakeImageButton(View view);

    @Nullable
    protected TextView getTakeImageTextView(View view) {
        return null;
    }

    @Nullable
    protected abstract View getAddFileLayout(View view);

    @Nullable
    protected abstract ImageView getAddFileButton(View view);

    @Nullable
    protected TextView getAddFileTextView(View view) {
        return null;
    }

    @Nullable
    protected abstract View getRecordAudioLayout(View view);

    @Nullable
    protected abstract ImageView getRecordAudioButton(View view);

    @Nullable
    protected TextView getRecordAudioTextView(View view) {
        return null;
    }

    @Nullable
    public abstract ImageView getHideAttachmentButton(View view);

    @Nullable
    protected abstract ImageView getToggleEmojiButton(View view);

    @Nullable
    protected abstract QiscusAudioRecorderView getRecordAudioPanel(View view);

    @Override
    public void onAttach(Context context) {
        super.onAttach(context);
        Activity activity = getActivity();
        if (activity instanceof CommentSelectedListener) {
            commentSelectedListener = (CommentSelectedListener) activity;
        }

        if (activity instanceof RoomChangedListener) {
            roomChangedListener = (RoomChangedListener) activity;
        }
    }

    @Override
    public void onActivityCreated(@Nullable Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        requestPermissions();
        onCreateChatComponents(savedInstanceState);
    }

    protected void onCreateChatComponents(Bundle savedInstanceState) {
        chatConfig = onLoadChatConfig();

        resolveChatRoom(savedInstanceState);
        resolveStartingMessage();
        resolveShareFile();

        onApplyChatConfig();

        swipeRefreshLayout.setOnRefreshListener(this);

        animation = onLoadAnimation();

        onClearNotification();

        qiscusAccount = Qiscus.getQiscusAccount();

        chatAdapter = onCreateChatAdapter();
        chatAdapter.setOnItemClickListener(
                (view, position) -> onItemCommentClick((QiscusComment) chatAdapter.getData().get(position)));
        chatAdapter.setOnLongItemClickListener(
                (view, position) -> onItemCommentLongClick((QiscusComment) chatAdapter.getData().get(position)));
        chatAdapter.setChatButtonClickListener(this);
        messageRecyclerView.setUpAsBottomList();
        chatLayoutManager = (LinearLayoutManager) messageRecyclerView.getLayoutManager();
        messageRecyclerView.setAdapter(chatAdapter);
        messageRecyclerView.addOnScrollListener(new QiscusChatScrollListener(chatLayoutManager, this));

        setupEmojiPopup();

        qiscusChatPresenter = new QiscusChatPresenter(this, qiscusChatRoom);
        if (savedInstanceState == null) {
            qiscusChatPresenter.loadComments(20);
        } else {
            ArrayList<QiscusComment> comments = savedInstanceState.getParcelableArrayList(COMMENTS_DATA);
            if (comments == null) {
                qiscusChatPresenter.loadComments(20);
            } else {
                showComments(comments);
            }
        }

        if (commentSelectedListener != null) {
            commentSelectedListener.onCommentSelected(chatAdapter.getSelectedComments());
        }

        if (startingMessage != null && !startingMessage.isEmpty()) {
            sendMessage(startingMessage);
        }

        if (shareFile != null) {
            sendFile(shareFile);
        }
    }

    protected void setupEmojiPopup() {
        if (messageEditText instanceof EmojiEditText && toggleEmojiButton != null) {
            emojiPopup = EmojiPopup.Builder.fromRootView(rootView)
                    .setOnSoftKeyboardCloseListener(this::dismissEmoji)
                    .setOnEmojiPopupShownListener(
                            () -> toggleEmojiButton.setImageResource(chatConfig.getShowKeyboardIcon()))
                    .setOnEmojiPopupDismissListener(
                            () -> toggleEmojiButton.setImageResource(chatConfig.getShowEmojiIcon()))
                    .build((EmojiEditText) messageEditText);
        }
    }

    protected void hideAttachmentPanel() {
        if (attachmentPanel != null) {
            attachmentPanel.setVisibility(View.GONE);
            if (messageEditTextContainer != null) {
                messageEditTextContainer.setVisibility(View.VISIBLE);
            }
        }
    }

    protected void showAttachmentPanel() {
        if (attachmentPanel != null && attachmentPanel.getVisibility() == View.GONE) {
            attachmentPanel.setVisibility(View.VISIBLE);
            if (messageEditTextContainer != null) {
                messageEditTextContainer.setVisibility(View.GONE);
                QiscusAndroidUtil.hideKeyboard(getActivity(), messageEditText);
            }
        }
    }

    protected QiscusChatConfig onLoadChatConfig() {
        return Qiscus.getChatConfig();
    }

    protected void resolveChatRoom(Bundle savedInstanceState) {
        qiscusChatRoom = getArguments().getParcelable(CHAT_ROOM_DATA);
        if (qiscusChatRoom == null && savedInstanceState != null) {
            qiscusChatRoom = savedInstanceState.getParcelable(CHAT_ROOM_DATA);
        }

        if (qiscusChatRoom == null) {
            getActivity().finish();
            return;
        }
    }

    protected void resolveStartingMessage() {
        startingMessage = getArguments().getString(EXTRA_STARTING_MESSAGE);
        getArguments().remove(EXTRA_STARTING_MESSAGE);
    }

    protected void resolveShareFile() {
        shareFile = (File) getArguments().getSerializable(EXTRA_SHARE_FILE);
        getArguments().remove(EXTRA_SHARE_FILE);
    }

    protected void onApplyChatConfig() {
        Drawable buttonBg;

        rootView.setBackground(chatConfig.getChatRoomBackground());
        swipeRefreshLayout.setColorSchemeResources(chatConfig.getSwipeRefreshColorScheme());
        sendButton.setImageResource(chatConfig.getShowAttachmentPanelIcon());
        messageEditText.setHint(chatConfig.getMessageFieldHint());

        if (emptyChatImageView != null) {
            emptyChatImageView.setImageResource(chatConfig.getEmptyRoomImageResource());
        }
        if (emptyChatTitleView != null) {
            emptyChatTitleView.setText(chatConfig.getEmptyRoomTitle());
        }
        if (emptyChatDescView != null) {
            emptyChatDescView.setText(chatConfig.getEmptyRoomSubtitle());
        }
        if (addImageButton != null) {
            addImageButton.setImageResource(chatConfig.getAddPictureIcon());
            buttonBg = ContextCompat.getDrawable(Qiscus.getApps(), R.drawable.qiscus_gallery_button_bg);
            buttonBg.setColorFilter(
                    ContextCompat.getColor(Qiscus.getApps(), chatConfig.getAddPictureBackgroundColor()),
                    PorterDuff.Mode.SRC_ATOP);
            addImageButton.setBackground(buttonBg);
        }
        if (addImageTextView != null) {
            addImageTextView.setText(chatConfig.getAddPictureText());
        }
        if (takeImageButton != null) {
            takeImageButton.setImageResource(chatConfig.getTakePictureIcon());
            buttonBg = ContextCompat.getDrawable(Qiscus.getApps(), R.drawable.qiscus_camera_button_bg);
            buttonBg.setColorFilter(
                    ContextCompat.getColor(Qiscus.getApps(), chatConfig.getTakePictureBackgroundColor()),
                    PorterDuff.Mode.SRC_ATOP);
            takeImageButton.setBackground(buttonBg);
        }
        if (takeImageTextView != null) {
            takeImageTextView.setText(chatConfig.getTakePictureText());
        }
        if (addFileButton != null) {
            addFileButton.setImageResource(chatConfig.getAddFileIcon());
            buttonBg = ContextCompat.getDrawable(Qiscus.getApps(), R.drawable.qiscus_file_button_bg);
            buttonBg.setColorFilter(
                    ContextCompat.getColor(Qiscus.getApps(), chatConfig.getAddFileBackgroundColor()),
                    PorterDuff.Mode.SRC_ATOP);
            addFileButton.setBackground(buttonBg);
        }
        if (addFileTextView != null) {
            addFileTextView.setText(chatConfig.getAddFileText());
        }
        if (recordAudioButton != null) {
            recordAudioButton.setImageResource(chatConfig.getRecordAudioIcon());
            buttonBg = ContextCompat.getDrawable(Qiscus.getApps(), R.drawable.qiscus_record_button_bg);
            buttonBg.setColorFilter(ContextCompat.getColor(Qiscus.getApps(), chatConfig.getRecordBackgroundColor()),
                    PorterDuff.Mode.SRC_ATOP);
            recordAudioButton.setBackground(buttonBg);
        }
        if (recordAudioTextView != null) {
            recordAudioTextView.setText(chatConfig.getRecordText());
        }
        if (hideAttachmentButton != null) {
            hideAttachmentButton.setImageResource(chatConfig.getHideAttachmentPanelIcon());
            buttonBg = ContextCompat.getDrawable(Qiscus.getApps(), R.drawable.qiscus_keyboard_button_bg);
            buttonBg.setColorFilter(
                    ContextCompat.getColor(Qiscus.getApps(), chatConfig.getHideAttachmentPanelBackgroundColor()),
                    PorterDuff.Mode.SRC_ATOP);
            hideAttachmentButton.setBackground(buttonBg);
        }
        if (recordAudioPanel != null) {
            recordAudioPanel.setButtonStopRecord(chatConfig.getStopRecordIcon());
            recordAudioPanel.setButtonCancelRecord(chatConfig.getCancelRecordIcon());
        }
        if (toggleEmojiButton != null) {
            toggleEmojiButton.setImageResource(chatConfig.getShowEmojiIcon());
        }

        if (addImageLayout != null) {
            addImageLayout.setVisibility(chatConfig.isEnableAddPicture() ? View.VISIBLE : View.GONE);
        }
        if (takeImageLayout != null) {
            takeImageLayout.setVisibility(chatConfig.isEnableTakePicture() ? View.VISIBLE : View.GONE);
        }
        if (addFileLayout != null) {
            addFileLayout.setVisibility(chatConfig.isEnableAddFile() ? View.VISIBLE : View.GONE);
        }
        if (recordAudioLayout != null) {
            recordAudioLayout.setVisibility(chatConfig.isEnableRecordAudio() ? View.VISIBLE : View.GONE);
        }
    }

    protected Animation onLoadAnimation() {
        return AnimationUtils.loadAnimation(getActivity(), R.anim.qiscus_simple_grow);
    }

    protected void onClearNotification() {
        NotificationManagerCompat.from(getActivity()).cancel(qiscusChatRoom.getId());
        QiscusCacheManager.getInstance().clearMessageNotifItems(qiscusChatRoom.getId());
    }

    protected abstract T onCreateChatAdapter();

    @Override
    public void onResume() {
        super.onResume();
        onClearNotification();
        QiscusCacheManager.getInstance().setLastChatActivity(true, qiscusChatRoom.getId());
    }

    @Override
    public void onPause() {
        super.onPause();
        QiscusCacheManager.getInstance().setLastChatActivity(false, qiscusChatRoom.getId());
    }

    protected void onItemCommentClick(QiscusComment qiscusComment) {
        if (chatAdapter.getSelectedComments().isEmpty()) {
            if (qiscusComment.getState() > QiscusComment.STATE_SENDING) {
                if (qiscusComment.getType() == QiscusComment.Type.FILE
                        || qiscusComment.getType() == QiscusComment.Type.IMAGE
                        || qiscusComment.getType() == QiscusComment.Type.AUDIO) {
                    qiscusChatPresenter.downloadFile(qiscusComment);
                } else if (qiscusComment.getType() == QiscusComment.Type.ACCOUNT_LINKING) {
                    accountLinkingClick(qiscusComment);
                }
            } else if (qiscusComment.getState() == QiscusComment.STATE_FAILED) {
                showFailedCommentDialog(qiscusComment);
            }
        } else {
            toggleSelectComment(qiscusComment);
        }
    }

    protected void accountLinkingClick(QiscusComment qiscusComment) {
        try {
            JSONObject payload = QiscusRawDataExtractor.getPayload(qiscusComment);
            JSONObject params = payload.getJSONObject("params");
            startActivity(QiscusAccountLinkingActivity.generateIntent(getActivity(), params.optString("view_title"),
                    payload.getString("url"), payload.getString("redirect_url"),
                    params.optString("success_message")));
        } catch (JSONException e) {
            Log.e("Qiscus", e.getMessage());
        }
    }

    protected void showFailedCommentDialog(QiscusComment qiscusComment) {
        new AlertDialog.Builder(getActivity()).setTitle("Message send failed")
                .setItems(new CharSequence[] { "Resend", "Delete" }, (dialog, which) -> {
                    if (which == 0) {
                        qiscusChatPresenter.resendComment(qiscusComment);
                    } else {
                        qiscusChatPresenter.deleteComment(qiscusComment);
                    }
                }).setCancelable(true).create().show();
    }

    protected void onItemCommentLongClick(QiscusComment qiscusComment) {
        if (chatAdapter.getSelectedComments().isEmpty()) {
            toggleSelectComment(qiscusComment);
        }
    }

    protected void toggleSelectComment(QiscusComment qiscusComment) {
        qiscusComment.setSelected(!qiscusComment.isSelected());
        refreshComment(qiscusComment);
        if (commentSelectedListener != null) {
            commentSelectedListener.onCommentSelected(chatAdapter.getSelectedComments());
        }
    }

    public List<QiscusComment> getSelectedComments() {
        return chatAdapter.getSelectedComments();
    }

    public void clearSelectedComments() {
        chatAdapter.clearSelectedComments();
    }

    protected void onMessageEditTextChanged(CharSequence message) {
        if (message == null || message.toString().trim().isEmpty()) {
            if (!fieldMessageEmpty) {
                fieldMessageEmpty = true;
                sendButton.startAnimation(animation);
                sendButton.setImageResource(chatConfig.getShowAttachmentPanelIcon());
                QiscusPusherApi.getInstance().setUserTyping(qiscusChatRoom.getId(), qiscusChatRoom.getLastTopicId(),
                        false);
            }
        } else {
            if (fieldMessageEmpty) {
                fieldMessageEmpty = false;
                sendButton.startAnimation(animation);
                sendButton.setImageResource(chatConfig.getSendButtonIcon());
                QiscusPusherApi.getInstance().setUserTyping(qiscusChatRoom.getId(), qiscusChatRoom.getLastTopicId(),
                        true);
            }
        }
    }

    public void sendMessage(String message) {
        message = message.trim();
        if (!message.isEmpty()) {
            qiscusChatPresenter.sendComment(message);
            messageEditText.setText("");
        }
    }

    public void sendFile(File file) {
        qiscusChatPresenter.sendFile(file);
    }

    protected void addImage() {
        if (QiscusPermissionsUtil.hasPermissions(getActivity(), FILE_PERMISSION)) {
            FilePickerBuilder.getInstance(getActivity()).setMaxCount(1).addVideoPicker().pickPhoto(this);
            hideAttachmentPanel();
        } else {
            requestPermissions();
        }
    }

    protected void takeImage() {
        if (QiscusPermissionsUtil.hasPermissions(getActivity(), FILE_PERMISSION)) {
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            if (intent.resolveActivity(getActivity().getPackageManager()) != null) {
                File photoFile = null;
                try {
                    photoFile = QiscusImageUtil.createImageFile();
                } catch (IOException ex) {
                    showError(getString(R.string.chat_error_failed_write));
                }

                if (photoFile != null) {
                    if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
                        intent.putExtra(MediaStore.EXTRA_OUTPUT, Uri.fromFile(photoFile));
                    } else {
                        intent.putExtra(MediaStore.EXTRA_OUTPUT, FileProvider.getUriForFile(getActivity(),
                                Qiscus.getProviderAuthorities(), photoFile));
                    }
                    startActivityForResult(intent, TAKE_PICTURE_REQUEST);
                }
                hideAttachmentPanel();
            }
        } else {
            requestPermissions();
        }
    }

    protected void addFile() {
        if (QiscusPermissionsUtil.hasPermissions(getActivity(), FILE_PERMISSION)) {
            FilePickerBuilder.getInstance(getActivity()).setMaxCount(1).pickFile(this);
            hideAttachmentPanel();
        } else {
            requestPermissions();
        }
    }

    protected void recordAudio() {
        if (QiscusPermissionsUtil.hasPermissions(getActivity(), AUDIO_PERMISSION)) {
            if (recordAudioPanel != null) {
                recordAudioPanel.setVisibility(View.VISIBLE);
                if (messageInputPanel != null) {
                    messageInputPanel.setVisibility(View.GONE);
                }
                try {
                    recordAudioPanel.startRecord();
                } catch (IOException e) {
                    e.printStackTrace();
                    showError("Failed to record audio!");
                    recordAudioPanel.cancelRecord();
                } catch (IllegalStateException e) {
                    showError("Can not record audio, microphone may be in use!");
                    recordAudioPanel.cancelRecord();
                }
            }
        } else {
            requestPermissions();
        }
    }

    protected void toggleEmoji() {
        boolean lastShowing = emojiPopup.isShowing();
        emojiPopup.toggle();
        if (!lastShowing && !emojiPopup.isShowing()) {
            emojiPopup.toggle();
        }
    }

    protected void dismissEmoji() {
        if (emojiPopup != null && emojiPopup.isShowing()) {
            emojiPopup.dismiss();
        }
    }

    @Override
    public void initRoomData(QiscusChatRoom qiscusChatRoom, List<QiscusComment> comments) {
        this.qiscusChatRoom = qiscusChatRoom;
        if (roomChangedListener != null) {
            roomChangedListener.onRoomUpdated(qiscusChatRoom);
        }
        showComments(comments);
    }

    @Override
    public void showComments(List<QiscusComment> qiscusComments) {
        if (!qiscusComments.isEmpty()) {
            chatAdapter.addOrUpdate(qiscusComments);
        }
        if (chatAdapter.isEmpty() && qiscusComments.isEmpty()) {
            if (emptyChatHolder != null) {
                emptyChatHolder.setVisibility(View.VISIBLE);
            }
        } else {
            if (emptyChatHolder != null) {
                emptyChatHolder.setVisibility(View.GONE);
            }
        }
    }

    @Override
    public void onLoadMore(List<QiscusComment> qiscusComments) {
        chatAdapter.addOrUpdate(qiscusComments);
        if (chatAdapter.isEmpty() && qiscusComments.isEmpty()) {
            if (emptyChatHolder != null) {
                emptyChatHolder.setVisibility(View.VISIBLE);
            }
        } else {
            if (emptyChatHolder != null) {
                emptyChatHolder.setVisibility(View.GONE);
            }
        }
    }

    @Override
    public void onSendingComment(QiscusComment qiscusComment) {
        chatAdapter.addOrUpdate(qiscusComment);
        scrollToBottom();
        if (emptyChatHolder != null) {
            emptyChatHolder.setVisibility(View.GONE);
        }
    }

    @Override
    public void onSuccessSendComment(QiscusComment qiscusComment) {
        chatAdapter.addOrUpdate(qiscusComment);
    }

    @Override
    public void onFailedSendComment(QiscusComment qiscusComment) {
        chatAdapter.addOrUpdate(qiscusComment);
    }

    @Override
    public void onNewComment(QiscusComment qiscusComment) {
        chatAdapter.addOrUpdate(qiscusComment);
        if (!qiscusComment.getSenderEmail().equalsIgnoreCase(qiscusAccount.getEmail())
                && shouldShowNewMessageButton()) {
            if (newMessageButton != null && newMessageButton.getVisibility() == View.GONE) {
                newMessageButton.setVisibility(View.VISIBLE);
                newMessageButton.startAnimation(animation);
            }
        } else {
            scrollToBottom();
        }
        if (emptyChatHolder != null) {
            emptyChatHolder.setVisibility(View.GONE);
        }
    }

    @Override
    public void onCommentDeleted(QiscusComment qiscusComment) {
        chatAdapter.remove(qiscusComment);
    }

    @Override
    public void refreshComment(QiscusComment qiscusComment) {
        chatAdapter.addOrUpdate(qiscusComment);
    }

    @Override
    public void updateLastDeliveredComment(int lastDeliveredCommentId) {
        chatAdapter.updateLastDeliveredComment(lastDeliveredCommentId);
    }

    @Override
    public void updateLastReadComment(int lastReadCommentId) {
        chatAdapter.updateLastReadComment(lastReadCommentId);
    }

    private boolean shouldShowNewMessageButton() {
        return chatLayoutManager.findFirstVisibleItemPosition() > 2;
    }

    private void loadMoreComments() {
        if (loadMoreProgressBar.getVisibility() == View.GONE && chatAdapter.getItemCount() > 0) {
            QiscusComment qiscusComment = (QiscusComment) chatAdapter.getData().get(chatAdapter.getItemCount() - 1);
            if (qiscusComment.getId() == -1 || qiscusComment.getCommentBeforeId() > 0) {
                qiscusChatPresenter.loadOlderCommentThan(qiscusComment);
            }
        }
    }

    protected void scrollToBottom() {
        messageRecyclerView.smoothScrollToPosition(0);
        if (newMessageButton != null) {
            newMessageButton.setVisibility(View.GONE);
        }
    }

    @Override
    public void onFileDownloaded(File file, String mimeType) {
        Intent intent = new Intent(Intent.ACTION_VIEW);
        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            intent.setDataAndType(Uri.fromFile(file), mimeType);
            intent.setFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
        } else {
            intent.setDataAndType(FileProvider.getUriForFile(getActivity(), Qiscus.getProviderAuthorities(), file),
                    mimeType);
            intent.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK);
            intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        }
        try {
            startActivity(intent);
        } catch (ActivityNotFoundException e) {
            showError(getString(R.string.chat_error_no_handler));
        }
    }

    @Override
    public void startPhotoViewer(QiscusComment qiscusComment) {
        startActivity(QiscusPhotoViewerActivity.generateIntent(getActivity(), qiscusComment));
    }

    @Override
    public void onTopOffListMessage() {
        loadMoreComments();
    }

    @Override
    public void onMiddleOffListMessage() {
    }

    @Override
    public void onBottomOffListMessage() {
        if (newMessageButton != null) {
            newMessageButton.setVisibility(View.GONE);
        }
    }

    @Override
    public void showError(String errorMessage) {
        Toast.makeText(getActivity(), errorMessage, Toast.LENGTH_SHORT).show();
    }

    @Override
    public void showLoading() {
        try {
            swipeRefreshLayout.setRefreshing(true);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void showLoadMoreLoading() {
        try {
            swipeRefreshLayout.setRefreshing(false);
            loadMoreProgressBar.setVisibility(View.VISIBLE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void dismissLoading() {
        try {
            swipeRefreshLayout.setRefreshing(false);
            loadMoreProgressBar.setVisibility(View.GONE);
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    @Override
    public void onRefresh() {
        if (chatAdapter.getData().size() > 0) {
            loadMoreComments();
            swipeRefreshLayout.setRefreshing(false);
        } else {
            qiscusChatPresenter.loadComments(20);
        }
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        super.onActivityResult(requestCode, resultCode, data);
        if (requestCode == FilePickerConst.REQUEST_CODE_PHOTO && resultCode == Activity.RESULT_OK) {
            if (data == null) {
                showError(getString(R.string.chat_error_failed_open_picture));
                return;
            }
            ArrayList<String> paths = data.getStringArrayListExtra(FilePickerConst.KEY_SELECTED_MEDIA);
            if (paths.size() > 0) {
                sendFile(new File(paths.get(0)));
            }
        } else if (requestCode == FilePickerConst.REQUEST_CODE_DOC && resultCode == Activity.RESULT_OK) {
            if (data == null) {
                showError(getString(R.string.chat_error_failed_open_file));
                return;
            }
            ArrayList<String> paths = data.getStringArrayListExtra(FilePickerConst.KEY_SELECTED_DOCS);
            if (paths.size() > 0) {
                sendFile(new File(paths.get(0)));
            }
        } else if (requestCode == TAKE_PICTURE_REQUEST && resultCode == Activity.RESULT_OK) {
            try {
                sendFile(QiscusFileUtil.from(Uri.parse(QiscusCacheManager.getInstance().getLastImagePath())));
            } catch (Exception e) {
                showError(getString(R.string.chat_error_failed_read_picture));
                e.printStackTrace();
            }
        }
    }

    @Override
    public void onStartRecord() {
        hideAttachmentPanel();
    }

    @Override
    public void onCancelRecord() {
        if (recordAudioPanel != null) {
            recordAudioPanel.setVisibility(View.GONE);
        }
        if (messageInputPanel != null) {
            messageInputPanel.setVisibility(View.VISIBLE);
        }
    }

    @Override
    public void onStopRecord(File audioFile) {
        sendFile(audioFile);
        onCancelRecord();
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        super.onSaveInstanceState(outState);
        outState.putParcelable(CHAT_ROOM_DATA, qiscusChatRoom);
        ArrayList<QiscusComment> comments = new ArrayList<>();
        int size = chatAdapter.getData().size();
        for (int i = 0; i < size; i++) {
            comments.add((QiscusComment) chatAdapter.getData().get(i));
        }
        outState.putParcelableArrayList(COMMENTS_DATA, comments);
    }

    @Override
    public void onStop() {
        if (emojiPopup != null) {
            emojiPopup.dismiss();
        }

        super.onStop();
    }

    @Override
    public void onDestroyView() {
        super.onDestroyView();
        QiscusPusherApi.getInstance().setUserTyping(qiscusChatRoom.getId(), qiscusChatRoom.getLastTopicId(), false);
        QiscusCacheManager.getInstance().setLastChatActivity(false, qiscusChatRoom.getId());
        chatAdapter.detachView();
        if (recordAudioPanel != null) {
            recordAudioPanel.cancelRecord();
        }
        qiscusChatPresenter.detachView();
    }

    protected void requestPermissions() {
        if (!QiscusPermissionsUtil.hasPermissions(getActivity(), PERMISSIONS)) {
            QiscusPermissionsUtil.requestPermissions(this,
                    "Please grant permissions to make apps working properly!", RC_PERMISSIONS, PERMISSIONS);
        }
    }

    @Override
    public void onRequestPermissionsResult(int requestCode, @NonNull String[] permissions,
            @NonNull int[] grantResults) {
        super.onRequestPermissionsResult(requestCode, permissions, grantResults);
        QiscusPermissionsUtil.onRequestPermissionsResult(requestCode, permissions, grantResults, this);
    }

    @Override
    public void onPermissionsGranted(int requestCode, List<String> perms) {

    }

    @Override
    public void onPermissionsDenied(int requestCode, List<String> perms) {
        QiscusPermissionsUtil.checkDeniedPermissionsNeverAskAgain(this,
                "If you not grant permission, the apps may not be working properly. "
                        + "So please grant the permission for better user experienced.",
                R.string.qiscus_grant, R.string.qiscus_denny, perms);
    }

    @Override
    public void onChatButtonClick(JSONObject jsonButton) {
        qiscusChatPresenter.clickChatButton(jsonButton);
    }

    public interface CommentSelectedListener {
        void onCommentSelected(List<QiscusComment> selectedComments);
    }

    public interface RoomChangedListener {
        void onRoomUpdated(QiscusChatRoom qiscusChatRoom);
    }
}