org.tigase.mobile.chat.ChatHistoryFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.tigase.mobile.chat.ChatHistoryFragment.java

Source

/*
 * Tigase Mobile Messenger for Android
 * Copyright (C) 2011-2013 "Artur Hefczyc" <artur.hefczyc@tigase.org>
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Affero General Public License as published by
 * the Free Software Foundation, version 3 of the License.
 *
 * 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 Affero General Public License for more details.
 *
 * You should have received a copy of the GNU Affero General Public License
 * along with this program. Look for COPYING file in the top folder.
 * If not, see http://www.gnu.org/licenses/.
 */
package org.tigase.mobile.chat;

import java.util.Date;
import java.util.List;

import org.tigase.mobile.FragmentWithUID;
import org.tigase.mobile.MessengerApplication;
import org.tigase.mobile.MultiJaxmpp;
import org.tigase.mobile.MultiJaxmpp.ChatWrapper;
import org.tigase.mobile.R;
import org.tigase.mobile.RosterDisplayTools;
import org.tigase.mobile.TigaseMobileMessengerActivity;
import org.tigase.mobile.chatlist.ChatListActivity;
import org.tigase.mobile.db.ChatTableMetaData;
import org.tigase.mobile.db.providers.ChatHistoryProvider;
import org.tigase.mobile.filetransfer.FileTransferUtility;
import org.tigase.mobile.roster.CPresence;

import tigase.jaxmpp.core.client.BareJID;
import tigase.jaxmpp.core.client.JID;
import tigase.jaxmpp.core.client.JaxmppCore;
import tigase.jaxmpp.core.client.exceptions.JaxmppException;
import tigase.jaxmpp.core.client.observer.Listener;
import tigase.jaxmpp.core.client.xml.XMLException;
import tigase.jaxmpp.core.client.xmpp.modules.chat.AbstractChatManager;
import tigase.jaxmpp.core.client.xmpp.modules.chat.Chat;
import tigase.jaxmpp.core.client.xmpp.modules.chat.ChatState;
import tigase.jaxmpp.core.client.xmpp.modules.chat.MessageModule;
import tigase.jaxmpp.core.client.xmpp.modules.chat.MessageModule.MessageEvent;
import tigase.jaxmpp.core.client.xmpp.modules.presence.PresenceModule;
import tigase.jaxmpp.core.client.xmpp.modules.presence.PresenceModule.PresenceEvent;
import tigase.jaxmpp.core.client.xmpp.modules.roster.RosterItem;
import tigase.jaxmpp.j2se.Jaxmpp;
import android.app.Activity;
import android.app.Dialog;
import android.content.Context;
import android.content.Intent;
import android.database.Cursor;
import android.database.DataSetObserver;
import android.net.Uri;
import android.os.Build;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader;
import android.support.v4.view.ViewPager;
import android.text.ClipboardManager;
import android.text.format.DateFormat;
import android.util.Log;
import android.view.ContextMenu;
import android.view.ContextMenu.ContextMenuInfo;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView.AdapterContextMenuInfo;
import android.widget.ListView;
import android.widget.TextView;

public class ChatHistoryFragment extends FragmentWithUID implements LoaderCallbacks<Cursor> {

    private static final boolean DEBUG = true;

    private static final String TAG = "tigase-chat";

    public static Fragment newInstance(String account, long chatId) {
        ChatHistoryFragment f = new ChatHistoryFragment();

        Bundle args = new Bundle();
        args.putLong("chatId", chatId);
        args.putString("account", account);
        f.setArguments(args);

        if (DEBUG)
            Log.d(TAG, "Creating ChatFragment id=" + chatId);

        return f;
    }

    public static String prepareAdditionalDebug(final MultiJaxmpp multi) {
        try {
            String s = "Known wrappers: [";

            for (ChatWrapper w : multi.getChats()) {
                if (w.isChat()) {
                    s += "CHAT=" + w.getChat().getId() + " ";
                } else if (w.isRoom()) {
                    s += "ROOM=" + w.getRoom().getId() + " ";
                } else {
                    s += "SOMETHINGELSE ";
                }
            }
            s += "] ";

            s += "Known JAXMPP: [";
            for (JaxmppCore j : multi.get()) {
                s += j.getSessionObject().getUserBareJid().toString() + " ";
            }
            s += "] ";

            return s;
        } catch (Exception e) {
            Log.e(TAG, "WTF?", e);
            return "Exception: " + e.getMessage();
        }

    }

    // private Cursor c;

    private Chat chat;

    private ChatAdapter chatAdapter;

    private final Listener<MessageEvent> chatUpdateListener;

    private ChatView layout;

    private ListView lv;

    private final Listener<PresenceEvent> presenceListener;

    public ChatHistoryFragment() {
        super();
        this.presenceListener = new Listener<PresenceModule.PresenceEvent>() {

            @Override
            public void handleEvent(PresenceEvent be) throws JaxmppException {
                if (DEBUG)
                    Log.d(TAG, "Received presence " + be.getJid() + " :: " + be.getPresence());
                if (ChatHistoryFragment.this.chat != null
                        && ChatHistoryFragment.this.chat.getJid().getBareJid().equals(be.getJid().getBareJid()))
                    updatePresence();
            }
        };
        this.chatUpdateListener = new Listener<MessageModule.MessageEvent>() {

            @Override
            public void handleEvent(MessageEvent be) throws JaxmppException {
                layout.updateClientIndicator();
            }
        };
    }

    private void clearMessageHistory() {
        getActivity().getApplicationContext().getContentResolver().delete(
                Uri.parse(ChatHistoryProvider.CHAT_URI + "/" + Uri.encode(chat.getJid().getBareJid().toString())),
                null, null);
    }

    private void copyMessageBody(final long id) {
        ClipboardManager clipMan = (ClipboardManager) getActivity().getSystemService(Context.CLIPBOARD_SERVICE);
        Cursor cc = null;
        try {
            cc = getChatEntry(id);
            String t = cc.getString(cc.getColumnIndex(ChatTableMetaData.FIELD_BODY));
            clipMan.setText(t);
        } finally {
            if (cc != null && !cc.isClosed())
                cc.close();
        }

    }

    public Chat getChat() {
        return chat;
    }

    private Cursor getChatEntry(long id) {
        Cursor cursor = getActivity().getApplicationContext().getContentResolver().query(Uri.parse(
                ChatHistoryProvider.CHAT_URI + "/" + Uri.encode(chat.getJid().getBareJid().toString()) + "/" + id),
                null, null, null, null);
        cursor.moveToNext();
        return cursor;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);

        if (getArguments() != null) {
            long id = getArguments().getLong("chatId");
            MultiJaxmpp multi = ((MessengerApplication) getActivity().getApplication()).getMultiJaxmpp();
            ChatWrapper ch = multi.getChatById(id);

            if (ch == null) {
                String msg = prepareAdditionalDebug(multi);
                Log.v(TAG, "ChatWrapper is null with id = " + id + '\n' + msg);
                ((TigaseMobileMessengerActivity) getActivity()).viewPager.getAdapter().notifyDataSetChanged();
            } else {
                if (ch.getChat() == null) {
                    throw new NullPointerException("ChatWrapper.getChat() is null with id = " + id);
                }
                if (ch.getChat().getSessionObject() == null) {
                    throw new NullPointerException(
                            "ChatWrapper.getChat().getSessionObject() is null with id = " + id);
                }
                setChatId(ch.getChat().getSessionObject().getUserBareJid(), ch.getChat().getId());
            }
        }
        layout.setChat(chat);
        getLoaderManager().initLoader(fragmentUID, null, this);
    }

    @Override
    public void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (requestCode == TigaseMobileMessengerActivity.SELECT_FOR_SHARE && resultCode == Activity.RESULT_OK) {
            Uri selected = data.getData();
            String mimetype = data.getType();
            RosterItem ri = chat.getSessionObject().getRoster().get(chat.getJid().getBareJid());
            JID jid = chat.getJid();
            final Jaxmpp jaxmpp = ((MessengerApplication) getActivity().getApplicationContext()).getMultiJaxmpp()
                    .get(ri.getSessionObject());
            if (jid.getResource() == null) {
                jid = FileTransferUtility.getBestJidForFeatures(jaxmpp, jid.getBareJid(),
                        FileTransferUtility.FEATURES);
            }
            if (jid != null) {
                FileTransferUtility.startFileTransfer(getActivity(), jaxmpp, chat.getJid(), selected, mimetype);
            }
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.detailsMessage) {
            AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
            showMessageDetails(info.id);
            return true;
        } else if (item.getItemId() == R.id.copyMessage) {
            AdapterContextMenuInfo info = (AdapterContextMenuInfo) item.getMenuInfo();
            copyMessageBody(info.id);
            return true;
        } else if (item.getItemId() == R.id.clearMessageHistory) {
            clearMessageHistory();
            return true;
        } else {
            return super.onContextItemSelected(item);
        }
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        this.setHasOptionsMenu(true);
        super.onCreate(savedInstanceState);
        this.setHasOptionsMenu(true);
        this.setRetainInstance(true);

        this.chatAdapter = new ChatAdapter(getActivity(), R.layout.chat_item);
        chatAdapter.registerDataSetObserver(new DataSetObserver() {

            @Override
            public void onChanged() {
                super.onChanged();
                if (DEBUG)
                    Log.i(TAG, "Changed!");
                lv.post(new Runnable() {

                    @Override
                    public void run() {
                        lv.setSelection(Integer.MAX_VALUE);
                    }
                });
            }
        });

    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View v, ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, v, menuInfo);
        MenuInflater m = new MenuInflater(getActivity());
        m.inflate(R.menu.chat_context_menu, menu);
    }

    @Override
    public Loader<Cursor> onCreateLoader(int arg0, Bundle arg1) {
        return new CursorLoader(getActivity().getApplicationContext(),
                Uri.parse(ChatHistoryProvider.CHAT_URI + "/" + Uri.encode(chat.getJid().getBareJid().toString())),
                null, null, null, null);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        menu.clear();

        inflater.inflate(R.menu.chat_main_menu, menu);

        // Share button support
        MenuItem share = menu.findItem(R.id.shareButton);

        boolean visible = false;
        if (chat != null) {
            final Jaxmpp jaxmpp = ((MessengerApplication) getActivity().getApplicationContext()).getMultiJaxmpp()
                    .get(chat.getSessionObject());
            try {
                JID jid = chat.getJid();

                if (jid.getResource() == null) {
                    jid = FileTransferUtility.getBestJidForFeatures(jaxmpp, jid.getBareJid(),
                            FileTransferUtility.FEATURES);
                }

                if (jid != null) {
                    visible = FileTransferUtility.resourceContainsFeatures(jaxmpp, jid,
                            FileTransferUtility.FEATURES);
                }
            } catch (XMLException e) {
            }
        } else {
            Log.v(TAG, "no chat for fragment");
        }
        share.setVisible(visible);

        super.onCreateOptionsMenu(menu, inflater);
    }

    @Override
    public View onCreateView(final LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        this.layout = (ChatView) inflater.inflate(R.layout.chat, null);
        layout.init();

        if (DEBUG)
            Log.d(TAG, "onActivityCreated ChatFragment " + savedInstanceState);
        if (DEBUG)
            Log.d(TAG, "Arguments: " + getArguments());
        if (DEBUG)
            Log.d(TAG, "Activity: " + getActivity());

        // if (savedInstanceState != null) {
        // long ci = savedInstanceState.getLong("chatId", -1);
        // if (ci != -1) {
        // setChatId(ci);
        // }
        // }

        // if (chat == null) {
        // throw new RuntimeException("Chat not specified!");
        // }

        this.lv = (ListView) layout.findViewById(R.id.chat_conversation_history);
        registerForContextMenu(lv);
        // lv.setOnItemLongClickListener(new OnItemLongClickListener() {
        //
        // @Override
        // public boolean onItemLongClick(AdapterView<?> parent, View view, int
        // position, long id) {
        // Men
        //
        // return true;
        // }
        // });

        lv.setAdapter(chatAdapter);

        return layout;
    }

    @Override
    public void onDestroyView() {
        // Cursor c = chatAdapter.getCursor();
        // if (c != null) {
        // if (DEBUG)
        // Log.d(TAG, "Closing cursor");
        // c.close();
        // }
        super.onDestroyView();
    }

    @Override
    public void onLoaderReset(Loader<Cursor> arg0) {
        chatAdapter.swapCursor(null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor cursor) {
        chatAdapter.swapCursor(cursor);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (item.getItemId() == R.id.showChatsButton) {
            Intent chatListActivity = new Intent(getActivity(), ChatListActivity.class);
            this.getActivity().startActivityForResult(chatListActivity, TigaseMobileMessengerActivity.REQUEST_CHAT);
        } else if (item.getItemId() == R.id.closeChatButton) {
            layout.cancelEdit();
            final Jaxmpp jaxmpp = ((MessengerApplication) getActivity().getApplicationContext()).getMultiJaxmpp()
                    .get(chat.getSessionObject());
            final ViewPager viewPager = ((TigaseMobileMessengerActivity) this.getActivity()).viewPager;
            final AbstractChatManager cm = jaxmpp.getModule(MessageModule.class).getChatManager();
            try {
                viewPager.setCurrentItem(1);
                cm.close(chat);
                // this will be done by TigaseMessengerActiviy after receiving
                // ChatOpened event
                // viewPager.getAdapter().notifyDataSetChanged();
                viewPager.setCurrentItem(1);
                if (DEBUG)
                    Log.i(TAG, "Chat with " + chat.getJid() + " (" + chat.getId() + ") closed");
            } catch (JaxmppException e) {
                Log.w(TAG, "Chat close problem!", e);
            }
        } else if (item.getItemId() == R.id.shareImageButton) {
            Log.v(TAG, "share selected for = " + chat.getJid().toString());
            Intent pickerIntent = new Intent(Intent.ACTION_PICK);
            pickerIntent.setType("image/*");
            pickerIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
            startActivityForResult(pickerIntent, TigaseMobileMessengerActivity.SELECT_FOR_SHARE);
        } else if (item.getItemId() == R.id.shareVideoButton) {
            Log.v(TAG, "share selected for = " + chat.getJid().toString());
            Intent pickerIntent = new Intent(Intent.ACTION_PICK);
            pickerIntent.setType("video/*");
            pickerIntent.addFlags(Intent.FLAG_ACTIVITY_EXCLUDE_FROM_RECENTS);
            startActivityForResult(pickerIntent, TigaseMobileMessengerActivity.SELECT_FOR_SHARE);
        }
        return true;
    }

    @Override
    public void onPrepareOptionsMenu(Menu menu) {
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.HONEYCOMB
                && Build.VERSION.SDK_INT < Build.VERSION_CODES.ICE_CREAM_SANDWICH) {
            MenuInflater inflater = new MenuInflater(this.getActivity().getApplicationContext());
            onCreateOptionsMenu(menu, inflater);
        }

        super.onPrepareOptionsMenu(menu);
    }

    @Override
    public void onResume() {
        // if (((ChatAdapter) lv.getAdapter()).getCursor().isClosed()) {
        // ((ChatAdapter) lv.getAdapter()).swapCursor(getCursor());
        // }

        super.onResume();

        updatePresence();
        layout.updateClientIndicator();
        setLocalChatState(ChatState.active);
    }

    @Override
    public void onSaveInstanceState(Bundle outState) {
        if (DEBUG)
            Log.d(TAG, "Save state of ChatFragment");
        if (outState != null)
            outState.putLong("chatId", chat.getId());
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onStart() {
        if (DEBUG)
            Log.d(TAG, "Start ChatFragment");
        final MultiJaxmpp jaxmpp = ((MessengerApplication) getActivity().getApplicationContext()).getMultiJaxmpp();

        jaxmpp.addListener(PresenceModule.ContactAvailable, this.presenceListener);
        jaxmpp.addListener(PresenceModule.ContactUnavailable, this.presenceListener);
        jaxmpp.addListener(PresenceModule.ContactChangedPresence, this.presenceListener);

        jaxmpp.addListener(MessageModule.ChatUpdated, this.chatUpdateListener);

        super.onStart();

        updatePresence();
        layout.updateClientIndicator();
    }

    @Override
    public void onStop() {
        if (DEBUG)
            Log.d(TAG, "Stop ChatFragment");
        final MultiJaxmpp jaxmpp = ((MessengerApplication) getActivity().getApplicationContext()).getMultiJaxmpp();

        jaxmpp.removeListener(MessageModule.ChatUpdated, this.chatUpdateListener);

        jaxmpp.removeListener(PresenceModule.ContactAvailable, this.presenceListener);
        jaxmpp.removeListener(PresenceModule.ContactUnavailable, this.presenceListener);
        jaxmpp.removeListener(PresenceModule.ContactChangedPresence, this.presenceListener);
        super.onStop();
    }

    private void setChatId(final BareJID account, final long chatId) {
        MultiJaxmpp multi = ((MessengerApplication) getActivity().getApplicationContext()).getMultiJaxmpp();

        List<ChatWrapper> l = multi.getChats();
        for (int i = 0; i < l.size(); i++) {
            ChatWrapper c = l.get(i);
            if (c.isChat() && c.getChat().getId() == chatId) {
                this.chat = c.getChat();
                if (DEBUG)
                    Log.d(TAG, "Found chat with " + chat.getJid() + " (id=" + chatId + ")");

                return;
            }
        }

        String ids = "";
        for (int i = 0; i < l.size(); i++) {
            ChatWrapper c = l.get(i);
            ids += c + " ";
        }
        throw new RuntimeException(
                "Chat (id:" + chatId + ", account:" + account + ")  not found! Available ids=" + ids);
    }

    @Override
    public void setUserVisibleHint(boolean isVisibleToUser) {
        super.setUserVisibleHint(isVisibleToUser);
        if (!isVisibleToUser && layout != null)
            layout.composing = false;
        setLocalChatState(isVisibleToUser ? ChatState.active : ChatState.inactive);
    }

    private void showMessageDetails(final long id) {
        Cursor cc = null;
        final java.text.DateFormat df = DateFormat.getDateFormat(getActivity());
        final java.text.DateFormat tf = DateFormat.getTimeFormat(getActivity());

        try {
            cc = getChatEntry(id);

            Dialog alertDialog = new Dialog(getActivity());
            alertDialog.setContentView(R.layout.chat_item_details_dialog);
            alertDialog.setCancelable(true);
            alertDialog.setCanceledOnTouchOutside(true);
            alertDialog.setTitle("Message details");

            TextView msgDetSender = (TextView) alertDialog.findViewById(R.id.msgDetSender);
            msgDetSender.setText(cc.getString(cc.getColumnIndex(ChatTableMetaData.FIELD_JID)));

            Date timestamp = new Date(cc.getLong(cc.getColumnIndex(ChatTableMetaData.FIELD_TIMESTAMP)));
            TextView msgDetReceived = (TextView) alertDialog.findViewById(R.id.msgDetReceived);
            msgDetReceived.setText(df.format(timestamp) + " " + tf.format(timestamp));

            final int state = cc.getInt(cc.getColumnIndex(ChatTableMetaData.FIELD_STATE));
            TextView msgDetState = (TextView) alertDialog.findViewById(R.id.msgDetState);
            switch (state) {
            case ChatTableMetaData.STATE_INCOMING:
                msgDetState.setText("Received");
                break;
            case ChatTableMetaData.STATE_OUT_SENT:
                msgDetState.setText("Sent");
                break;
            case ChatTableMetaData.STATE_OUT_NOT_SENT:
                msgDetState.setText("Not sent");
                break;
            default:
                msgDetState.setText("?");
                break;
            }

            alertDialog.show();
        } finally {
            if (cc != null && !cc.isClosed())
                cc.close();
        }
    }

    protected void updatePresence() {
        if (chat != null) {
            CPresence cp = RosterDisplayTools.getShowOf(chat.getSessionObject(), chat.getJid().getBareJid());

            // ((MessengerApplication)getActivity().getApplication()).getMultiJaxmpp().get(chat.getSessionObject());
            layout.setImagePresence(cp);

            TigaseMobileMessengerActivity activity = ((TigaseMobileMessengerActivity) getActivity());
            if (activity != null && activity.helper != null && chat != null) {
                activity.helper.updateActionBar(chat.hashCode());
            }

        }
    }

    private void setLocalChatState(final ChatState state) {
        new Thread() {
            public void run() {
                if (chat != null) {
                    try {
                        chat.setLocalChatState(state);
                    } catch (XMLException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    } catch (JaxmppException e) {
                        // TODO Auto-generated catch block
                        e.printStackTrace();
                    }
                }
            }
        }.start();
    }
}