com.raspi.chatapp.ui.chatting.ChatFragment.java Source code

Java tutorial

Introduction

Here is the source code for com.raspi.chatapp.ui.chatting.ChatFragment.java

Source

/*
 * Copyright 2016 Niklas Schelten
 *
 * 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.raspi.chatapp.ui.chatting;

import android.content.BroadcastReceiver;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.IntentFilter;
import android.graphics.Bitmap;
import android.graphics.BitmapFactory;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Build;
import android.os.Bundle;
import android.os.Handler;
import android.os.Parcelable;
import android.os.ResultReceiver;
import android.support.v4.app.Fragment;
import android.support.v4.content.LocalBroadcastManager;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AlertDialog;
import android.support.v7.app.AppCompatActivity;
import android.text.Html;
import android.util.Log;
import android.view.ActionMode;
import android.view.KeyEvent;
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.view.ViewTreeObserver;
import android.view.inputmethod.InputMethodManager;
import android.widget.AbsListView;
import android.widget.AdapterView;
import android.widget.Button;
import android.widget.EditText;
import android.widget.ImageButton;
import android.widget.ImageView;
import android.widget.ListView;

import com.alexbbb.uploadservice.UploadServiceBroadcastReceiver;
import com.raspi.chatapp.R;
import com.raspi.chatapp.ui.image.ImageViewActivity;
import com.raspi.chatapp.ui.util.emojicon.EmojiconEditText;
import com.raspi.chatapp.ui.util.emojicon.EmojiconGridView;
import com.raspi.chatapp.ui.util.emojicon.EmojiconPopup;
import com.raspi.chatapp.ui.util.emojicon.emoji.Emojicon;
import com.raspi.chatapp.ui.util.image.WallpaperImageView;
import com.raspi.chatapp.ui.util.message_array.Date;
import com.raspi.chatapp.ui.util.message_array.ImageMessage;
import com.raspi.chatapp.ui.util.message_array.LoadMoreMessages;
import com.raspi.chatapp.ui.util.message_array.MessageArrayAdapter;
import com.raspi.chatapp.ui.util.message_array.MessageArrayContent;
import com.raspi.chatapp.ui.util.message_array.NewMessage;
import com.raspi.chatapp.ui.util.message_array.TextMessage;
import com.raspi.chatapp.util.Constants;
import com.raspi.chatapp.util.internet.XmppManager;
import com.raspi.chatapp.util.internet.http.MessageDownloadService;
import com.raspi.chatapp.util.internet.http.Upload;
import com.raspi.chatapp.util.storage.MessageHistory;
import com.raspi.chatapp.util.storage.file.MyFileUtils;

import java.io.File;
import java.lang.ref.WeakReference;
import java.util.Calendar;
import java.util.GregorianCalendar;
import java.util.HashSet;
import java.util.Set;
import java.util.TreeSet;

/**
 * A {@link Fragment} subclass.
 * Activities that contain this fragment must implement the
 * {@link OnChatFragmentInteractionListener} interface
 * to handle interaction events.<br/>
 * This fragment contains the basic chat. It manages everything that has to
 * do with the ui of one single chat.
 */
public class ChatFragment extends Fragment {
    /**
     * the messageLimit describes how many messages are loaded at creating and
     * also how many more messages are loaded when clicking "loadMoreMessages".
     */
    private static final int MESSAGE_LIMIT = 30;

    /**
     * the current amount of loaded messages.
     */
    private int messageAmount = MESSAGE_LIMIT;

    /**
     * the buddyId of this instance of ChatFragment.
     */
    private String buddyId;
    /**
     * the chatName of this instance of ChatFragment.
     */
    private String chatName;

    /**
     * the array adapter for the listView containing the messages
     */
    private MessageArrayAdapter maa;
    /**
     * the listView containing the messages.
     */
    private ListView listView;
    /**
     * an instance of the messageHistory for saving/reading data to/from the db.
     */
    private MessageHistory messageHistory;
    /**
     * the uploadReceiver receives status updates from uploading. This includes
     * onCompleted and onError.
     */
    private final UploadServiceBroadcastReceiver uploadReceiver = new UploadServiceBroadcastReceiver() {
        @Override
        public void onProgress(String uploadId, int progress) {
            //received a progress update
            Log.d("UPLOAD_DEBUG", "progress: " + progress);
            int index = uploadId.indexOf('|');
            String buddyID = uploadId.substring(0, index);
            String messageId = uploadId.substring(index + 1);
            //check if this is the correct chat
            if (buddyID.equals(buddyId)) {
                int size = listView.getLastVisiblePosition();
                MessageArrayContent mac;
                //check for all visible messages
                for (int i = listView.getFirstVisiblePosition(); i <= size; i++) {
                    mac = maa.getItem(i);
                    //atm, only ImageMessages are able to receive a uploadEvent
                    if (mac instanceof ImageMessage) {
                        ImageMessage im = (ImageMessage) mac;
                        //if the id fits the messageId update the messageView
                        if (im._ID == Long.parseLong(messageId)) {
                            Log.d("UPLOAD_DEBUG", "progress: " + progress);
                            im.progress = progress;
                            updateMessage(i);
                        }
                    }
                }
            }
        }

        @Override
        public void onCompleted(String uploadId, int serverResponseCode, String serverResponseMessage) {
            //received a completed update
            int index = uploadId.indexOf('|');
            String buddyID = uploadId.substring(0, index);
            String messageId = uploadId.substring(index + 1);
            //check if this is the correct chat
            if (buddyID.equals(buddyId)) {
                int size = maa.getCount();
                MessageArrayContent mac;
                //check for all loaded messages
                for (int i = 0; i < size; i++) {
                    mac = maa.getItem(i);
                    //atm, only ImageMessages are able to receive a uploadEvent
                    if (mac instanceof ImageMessage) {
                        ImageMessage im = (ImageMessage) mac;
                        //if the id fits the messageId update the messageView
                        if (im._ID == Long.parseLong(messageId)) {
                            im.status = MessageHistory.STATUS_SENT;
                            updateMessage(i);
                        }
                    }
                }
            }
        }

        @Override
        public void onError(String uploadId, Exception exception) {
            //received an error update
            int index = uploadId.indexOf('|');
            String buddyID = uploadId.substring(0, index);
            String messageId = uploadId.substring(index + 1);
            //check if this is the correct chat
            if (buddyID.equals(buddyId)) {
                int size = maa.getCount();
                MessageArrayContent mac;
                //check for all loaded messages
                for (int i = 0; i <= size; i++) {
                    mac = maa.getItem(i);
                    //atm, only ImageMessages are able to receive a uploadEvent
                    if (mac instanceof ImageMessage) {
                        ImageMessage im = (ImageMessage) mac;
                        //if the id fits the messageId update the messageView
                        if (im._ID == Long.parseLong(messageId)) {
                            im.status = MessageHistory.STATUS_WAITING;
                            updateMessage(i);
                        }
                    }
                }
            }
        }
    };
    /**
     * the editText where the message is typed. You are able to use emojicon.
     */
    private EmojiconEditText textIn;
    /**
     * this is the actionBar visible at the top.
     */
    private ActionBar actionBar;
    /**
     * the listener that is implemented by the ChatActivity.
     */
    private OnChatFragmentInteractionListener mListener;
    /**
     * this receiver should be called if we received a new message and we need
     * to load this
     */
    private BroadcastReceiver messageReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle extras = intent.getExtras();
            String intentBuddyId = extras.getString(Constants.BUDDY_ID);
            long messageId = extras.getLong(Constants.MESSAGE_ID);
            int index = intentBuddyId.indexOf('@');
            if (index >= 0)
                intentBuddyId = intentBuddyId.substring(0, index);
            // check whether this is the correct chat
            if (buddyId.equals(intentBuddyId)) {
                int i = 0;
                // if there was the NewMessage item anywhere in the maa, delete it
                for (MessageArrayContent mac : maa) {
                    if (mac instanceof NewMessage)
                        maa.remove(i);
                    i++;
                }
                // I retrieve the lastMessage as I suppose there is only one message
                // added.
                MessageArrayContent mac = messageHistory.getMessage(buddyId, messageId);
                // if this is an image download it. the download task will take care
                // of message status and acknowledgements
                if (mac instanceof ImageMessage)
                    downloadImage((ImageMessage) mac);
                else
                    // this is a TextMessage -> directly send the read acknowledgement
                    // and update the messageStatus
                    try {
                        // careful with my id and the others id as they may differ
                        messageHistory.updateMessageStatus(buddyId, ((TextMessage) mac)._ID,
                                MessageHistory.STATUS_READ);
                        long othersId = extras.getLong(Constants.MESSAGE_OTHERS_ID);
                        XmppManager.getInstance().sendAcknowledgement(buddyId, othersId,
                                MessageHistory.STATUS_READ);
                    } catch (Exception e) {
                        e.printStackTrace();
                    }
                // finally add the message to the listView and select it in order to
                // scroll down
                maa.add(mac);
                listView.setSelection(maa.getCount() - 1);
                // in case this was an orderedBroadcast abort it.
                abortBroadcast();
            }
        }
    };

    /**
     * this receiver receives an event if the presence of anyone in my roster
     * changed.
     */
    private BroadcastReceiver presenceChangeReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle extras = intent.getExtras();
            // if the intent is correct
            if (extras != null && extras.containsKey(Constants.BUDDY_ID)
                    && extras.containsKey(Constants.PRESENCE_STATUS)) {
                // if this is the correct chat
                if (buddyId.equals(extras.getString(Constants.BUDDY_ID))) {
                    //update the status to the new one.
                    updateStatus(extras.getString(Constants.PRESENCE_STATUS));
                }
            }
        }
    };

    /**
     * this receiver receives an event if a messageStatus changed (e.g.
     * received an acknowledgement.
     */
    private BroadcastReceiver messageStatusChangedReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            Bundle extras = intent.getExtras();
            // if the intent is correct
            if (extras != null && extras.containsKey(Constants.BUDDY_ID) && extras.containsKey("id")
                    && extras.containsKey("status")) {
                String bId = extras.getString(Constants.BUDDY_ID);
                int index = bId.indexOf('@');
                if (index >= 0) {
                    bId = bId.substring(0, index);
                }
                // if this is the correct chat
                if (buddyId.equals(bId)) {
                    long id = extras.getLong("id");
                    int i = 0;
                    // loop through all messages
                    for (MessageArrayContent mac : maa) {
                        // need to separate into imageMessage and textMessage because of
                        // casting
                        if (mac instanceof ImageMessage) {
                            ImageMessage msg = (ImageMessage) mac;
                            // if the id is correct update the status and get the view in
                            // order to update the view.. makes sense so far...
                            if (msg._ID == id) {
                                msg.status = extras.getString("status");
                                maa.getView(i, listView.getChildAt(i - listView.getFirstVisiblePosition()),
                                        listView);
                            }
                        } else if (mac instanceof TextMessage) {
                            TextMessage msg = (TextMessage) mac;
                            // do the same with TextMessage
                            if (msg._ID == id) {
                                msg.status = extras.getString("status");
                                maa.getView(i, listView.getChildAt(i - listView.getFirstVisiblePosition()),
                                        listView);
                            }
                        }
                        i++;
                    }
                }
            }
        }
    };

    /**
     * this receiver receives an event if the xmppManager reconnected to the
     * server.
     */
    private BroadcastReceiver reconnectedReceiver = new BroadcastReceiver() {
        @Override
        public void onReceive(Context context, Intent intent) {
            // make the status visible
            updateStatus(messageHistory.getOnline(buddyId));

            // resend all messages that have not been sent.
            int size = listView.getCount();
            for (int i = 0; i < size; i++) {
                MessageArrayContent mac = maa.getItem(i);
                if (mac instanceof TextMessage) {
                    TextMessage msg = (TextMessage) mac;
                    if (MessageHistory.STATUS_WAITING.equals(msg.status))
                        sendTextMessage(msg);
                }
            }
        }
    };

    /**
     * this listener manages the selection of messages
     */
    private AbsListView.MultiChoiceModeListener multiChoiceModeListener = new AbsListView.MultiChoiceModeListener() {
        Menu menu;
        // this is the set over all selected messages. This will also include
        // "unselectable" items like DateMessage as I am filtering these when
        // executing an action.
        Set<MessageArrayContent> selected;
        // This Set saves all positions in the maa of the selected items.
        // Due to the way removing multiple elements of an ArrayAdapter works I
        // need to remove the elements in reversed order (from bottom to top).
        // Also, as the treeSet sorts integers from low to high, I want to
        // reverse the indices.
        Set<Integer> selectedPositions;

        @Override
        public void onItemCheckedStateChanged(ActionMode mode, int position, long id, boolean checked) {
            // TODO update number in CAB
            MessageArrayContent mac = maa.getItem(position);
            // if the selection is invalid try to select the item below it.
            if (mac instanceof Date || mac instanceof NewMessage || mac instanceof LoadMoreMessages) {
                try {
                    listView.setItemChecked(position + 1, true);
                } catch (Exception e) {
                    e.printStackTrace();
                }
            }
            // if I checked it add this mac to the selected Set and if it really
            // was added, also add it the selectedPositions Set (with a negative
            // sign, see above)
            if (checked) {
                if (selected.add(mac))
                    selectedPositions.add(-position);
                // otherwise remove it
            } else {
                if (selected.remove(mac)) {
                    // yep casting an int into an Integer is necessary because the
                    // remove function is overloaded: it may take the object to remove
                    // OR the index (int) of the item to remove.
                    Integer x = -position;
                    selectedPositions.remove(x);
                }
            }

            // if there is exactly one item selected show the copy actionItem.
            MenuItem itemCopy = menu.findItem(R.id.action_copy);
            // be careful, use the count function and not the count of one of the
            // Sets because the sets may also include invalid selections.
            int count = count();
            itemCopy.setVisible((count) == 1);

            // if I deselected the last valid item finish the actionMode
            if (count == 0)
                mode.finish();
            else
                mode.setTitle(String.valueOf(count));
        }

        /**
         * calculates the amount of valid selections. That means every selected
         * TextMessage or ImageMessage
         * @return the count of valid selections made
         */
        private int count() {
            int result = 0;
            // loop through the selected Set and increment the result if it is a
            // Text- or ImageMessage
            for (MessageArrayContent mac : selected)
                if (mac instanceof TextMessage || mac instanceof ImageMessage)
                    result++;
            return result;
        }

        @Override
        public boolean onCreateActionMode(ActionMode mode, Menu menu) {
            // initialize the Sets
            selected = new HashSet<>();
            // this is a tree set because a treeSet sorts the element (binary
            // search tree implementation I suppose)
            selectedPositions = new TreeSet<>();
            // inflate the menu
            MenuInflater inflater = mode.getMenuInflater();
            inflater.inflate(R.menu.menu_message_select, menu);
            this.menu = menu;
            // if I am over LOLLIPOP also set the color of the statusBar to the
            // primary color as it would, otherwise, be black or something like that.
            if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
                // yeah getColor is deprecated but I am actually targeting API 16 not
                // 23...
                getActivity().getWindow().setStatusBarColor(getResources().getColor(R.color.colorPrimaryDark));
            }
            return true;
        }

        @Override
        public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
            return true;
        }

        @Override
        public boolean onActionItemClicked(final ActionMode mode, MenuItem item) {
            //the result is only true if I really caught the event ( I should always)
            boolean result = false;
            switch (item.getItemId()) {
            // if the user want to copy the content
            case R.id.action_copy:
                // the selected Set should only contain one item, so just get it
                // via array conversion
                MessageArrayContent mac = selected.toArray(new MessageArrayContent[1])[0];
                String text = null;
                // if it is a textMessage, obviously, select the text.
                if (mac instanceof TextMessage)
                    text = ((TextMessage) mac).message;
                // if it is an imageMessage, however, select the description.
                else if (mac instanceof ImageMessage)
                    text = ((ImageMessage) mac).description;

                if (text != null) {
                    // if I selected something (should always happen) copy it as
                    // simple text to the clipboard
                    ClipboardManager clipboard = (ClipboardManager) getContext()
                            .getSystemService(Context.CLIPBOARD_SERVICE);
                    ClipData clipData = ClipData.newPlainText("simple text", text);
                    clipboard.setPrimaryClip(clipData);
                }
                // finish the action mode
                mode.finish();
                result = true;
                break;
            case R.id.action_delete:
                // the delete action is not that straight forward. I will ask the
                // user if he is sure to delete the items.
                new AlertDialog.Builder(getActivity())
                        // delete message vs. delete messages...
                        .setMessage(listView.getCheckedItemCount() > 1 ? R.string.delete_messages
                                : R.string.delete_message)
                        .setPositiveButton(R.string.delete, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // if he agreed loop through all selected messages and
                                // delete them
                                for (MessageArrayContent mac : selected) {
                                    // remember to only use the text and imageMessages
                                    if (mac instanceof TextMessage || mac instanceof ImageMessage) {
                                        // typecasting ftw
                                        long _ID = (mac instanceof TextMessage) ? ((TextMessage) mac)._ID
                                                : ((ImageMessage) mac)._ID;
                                        // remove them from the db
                                        messageHistory.removeMessages(buddyId, _ID);
                                    }
                                }
                                // remove them from the ui
                                for (int i : selectedPositions) {
                                    // remember the minus I added for the treeSet to
                                    // order in the correct order.
                                    maa.remove(-i);
                                }
                                // also finish the action mode and dismiss the dialog
                                mode.finish();
                                dialog.dismiss();
                            }
                        }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                            @Override
                            public void onClick(DialogInterface dialog, int which) {
                                // only dismiss the dialog but don't finish the actionMode
                                dialog.dismiss();
                            }
                        }).create().show();
                result = true;
                break;
            }
            return result;
        }

        @Override
        public void onDestroyActionMode(ActionMode mode) {
            // if the actionMode gets destroyed (back click or similar) set the
            // Sets to null to be sure that there are no selections from the last time
            selected = null;
            selectedPositions = null;
        }
    };

    /**
     * if init is false do not initialize the fragment this time
     */
    private boolean init = true;

    public ChatFragment() {
        // Required empty public constructor
    }

    /**
     * Use this factory method to create a new instance of
     * this fragment using the provided parameters.
     *
     * @param buddyId  TRhe buddyId of the chat partner
     * @param chatName The name of the chat
     * @return A new instance of fragment ChatFragment.
     */
    public static ChatFragment newInstance(String buddyId, String chatName, Parcelable imageUri) {
        ChatFragment fragment = new ChatFragment();
        Bundle args = new Bundle();
        args.putString(Constants.BUDDY_ID, buddyId);
        args.putString(Constants.CHAT_NAME, chatName);
        if (imageUri != null)
            args.putParcelable(Constants.IMAGE_URI, imageUri);
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        // get the actionBar
        actionBar = ((AppCompatActivity) getActivity()).getSupportActionBar();
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        // just making sure
        messageAmount = MESSAGE_LIMIT;
        // if the arguments are correct set the buddyId and chatName, otherwise,
        // throw an error
        Bundle arguments = getArguments();
        try {
            buddyId = arguments.getString(Constants.BUDDY_ID);
            chatName = arguments.getString(Constants.CHAT_NAME);
        } catch (Exception e) {
            throw new IllegalArgumentException(
                    "There must be a buddyId and " + "chatName provided in order to create this fragment.");
        }
        // if the user shares an image with this app he selects a chat and,
        // therefore, an imageUri will be attached to the intent that opens the
        // chat. Then I directly want to open the sendImageFragment.
        if (arguments.containsKey(Constants.IMAGE_URI)) {
            mListener.sendImages((Uri) arguments.getParcelable(Constants.IMAGE_URI));
            init = false;

        }
        // create the instance of messageHistory
        messageHistory = new MessageHistory(getContext());
    }

    @Override
    public void onResume() {
        super.onResume();
        // start the different receiver and init the ui
        IntentFilter filter = new IntentFilter(Constants.MESSAGE_RECEIVED);
        filter.setPriority(1);
        // messageReceiver. this is for reasons not on the localBroadcastManager...
        getContext().registerReceiver(messageReceiver, filter);
        LocalBroadcastManager LBmgr = LocalBroadcastManager.getInstance(getContext());
        // the reconnected receiver
        LBmgr.registerReceiver(reconnectedReceiver, new IntentFilter(Constants.RECONNECTED));
        // the presence changed receiver
        LBmgr.registerReceiver(presenceChangeReceiver, new IntentFilter(Constants.PRESENCE_CHANGED));
        // the messageStatus changed receiver
        LBmgr.registerReceiver(messageStatusChangedReceiver, new IntentFilter(Constants.MESSAGE_STATUS_CHANGED));
        uploadReceiver.register(getContext());
        // also init the ui
        if (init)
            initUI();
        init = false;
    }

    @Override
    public void onPause() {
        // unregister the different reciever (see onResume)
        InputMethodManager mgr = (InputMethodManager) getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
        mgr.hideSoftInputFromWindow(getView().findViewById(R.id.chat_in).getWindowToken(), 0);
        getContext().unregisterReceiver(messageReceiver);
        LocalBroadcastManager LBmgr = LocalBroadcastManager.getInstance(getContext());
        LBmgr.unregisterReceiver(presenceChangeReceiver);
        LBmgr.unregisterReceiver(reconnectedReceiver);
        LBmgr.registerReceiver(messageStatusChangedReceiver, new IntentFilter(Constants.MESSAGE_STATUS_CHANGED));
        uploadReceiver.unregister(getContext());
        super.onPause();
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        // Inflate the layout for this fragment and make sure that there is an
        // optionsMenu showing
        setHasOptionsMenu(true);
        return inflater.inflate(R.layout.fragment_chat, container, false);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater menuInflater) {
        // clear the menu to make sure the old entries are no longer contained
        menu.clear();
        // inflate the menu for this fragment
        menuInflater.inflate(R.menu.menu_chat, menu);
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        // if clicked attach perform the onAttackClicked. Easy.
        case R.id.action_attach:
            init = true;
            mListener.onAttachClicked(getActivity().findViewById(R.id.action_attach));
            return true;
        // if clicked rename open the dialog where the user can rename this chat
        case R.id.action_rename:
            // this will be the shown editText (without emojicons, I might wanna
            // change this in the future, let's see)
            final EditText newName = new EditText(getActivity());
            // prefix the EditText with the current name
            newName.setText(chatName);
            // the title comes from the resources and will include the current
            // chatName
            String title = getResources().getString(R.string.change_name_title) + " " + chatName;
            new AlertDialog.Builder(getContext()).setTitle(title).setMessage(R.string.change_name).setView(newName)
                    .setPositiveButton(R.string.rename, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            // when clicking rename retrieve the messageHistory
                            MessageHistory messageHistory = new MessageHistory(getContext());
                            String name = newName.getText().toString();
                            // update the db
                            messageHistory.renameChat(buddyId, name);
                            // set the current chatName
                            chatName = name;
                            actionBar.setTitle(chatName);
                        }
                    }).setNegativeButton(R.string.cancel, new DialogInterface.OnClickListener() {
                        @Override
                        public void onClick(DialogInterface dialog, int which) {
                            // yeah there also is a negative button...
                        }
                    }).show();
            return true;
        }
        // otherwise return false, I have not caught the event
        return false;
    }

    @Override
    public void onAttach(Context context) {
        // not to be confused with onAttackClicked. This a method overridden from
        // the Fragment to signal that this fragment is somehow active
        super.onAttach(context);
        if (context instanceof OnChatFragmentInteractionListener) {
            mListener = (OnChatFragmentInteractionListener) context;
        } else {
            throw new RuntimeException(context.toString() + " must implement OnChatFragmentInteractionListener");
        }
    }

    @Override
    public void onDetach() {
        super.onDetach();
        mListener = null;
    }

    private void initUI() {
        // load wallpaper
        loadWallPaper();
        // enable the emojicon-keyboard
        initEmoji();
        // set the actionBar title
        if (actionBar != null)
            actionBar.setTitle(chatName);
        // create the messageArrayAdapter
        maa = new MessageArrayAdapter(getContext(), R.layout.message_text);

        listView = (ListView) getView().findViewById(R.id.chat_listview);
        textIn = (EmojiconEditText) getView().findViewById(R.id.chat_in);

        //    // Change the TypFace
        //    Typeface typeface = Typeface.createFromAsset(getActivity().getAssets(),
        //            "fonts/Aileron-Bold.otf");
        //    textIn.setTypeface(typeface);

        Button sendBtn = (Button) getView().findViewById(R.id.chat_sendBtn);

        sendBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                // clicking the sendButton will send the message, uhh.
                // trim the message. Spaces are a waste of resources!
                String message = textIn.getText().toString().trim();
                // and ofc only send if the message has something in it.
                if (!message.isEmpty()) {
                    // add the message to the messageHistory
                    long id = messageHistory.addMessage(buddyId,
                            getContext().getSharedPreferences(Constants.PREFERENCES, 0)
                                    .getString(Constants.USERNAME, ""),
                            // this will be a textMessage for sure
                            MessageHistory.TYPE_TEXT, message, MessageHistory.STATUS_WAITING, -1);
                    // also add the message to the ui
                    maa.add(new TextMessage(false, message, new GregorianCalendar().getTimeInMillis(),
                            MessageHistory.STATUS_WAITING, id, -1));
                    // and probably the buddy also wants the message, so send it
                    sendTextMessage(message, id);
                    // revert the editText
                    textIn.setText("");
                    int i = 0;
                    // remove the NewMessage mac if it exists.
                    for (MessageArrayContent mac : maa) {
                        if (mac instanceof NewMessage)
                            maa.remove(i);
                        i++;
                    }
                    // select the last item to scroll down.
                    listView.setSelection(maa.getCount() - 1);
                }
            }
        });

        // this fucking bitch cost me a lot of time. Yep normal seems quite good,
        // doesn't it. Then why the actual fuck is "NORMAL" not the default?
        // Android?
        listView.setTranscriptMode(AbsListView.TRANSCRIPT_MODE_NORMAL);
        // set the corresponding adapter
        listView.setAdapter(maa);
        // also clicking on items will do sometimes something.
        listView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                MessageArrayContent mac = maa.getItem(position);
                // if it is an imageMessage show the image
                if (mac instanceof ImageMessage) {
                    ImageMessage im = (ImageMessage) mac;
                    // yeah pretty straight forward, just view the image with the
                    // default application.
                    //          Intent viewIntent = new Intent(Intent.ACTION_VIEW);
                    //          viewIntent.setDataAndType(Uri.fromFile(new File(im.file)),
                    //                  "image/*");
                    Intent viewIntent = new Intent(getContext(), ImageViewActivity.class);
                    viewIntent.putExtra(Constants.BUDDY_ID, buddyId);
                    viewIntent.putExtra(Constants.MESSAGE_ID, im._ID);
                    startActivity(viewIntent);

                    // don't ask for a pwd as I am just viewing an image
                    getContext().getSharedPreferences(Constants.PREFERENCES, 0).edit()
                            .putBoolean(Constants.PWD_REQUEST, false).apply();
                } else if (mac instanceof LoadMoreMessages) {
                    // clicking on loadMoreMessage should load more messages shouldn't it?
                    // FIXME: fix bugs xD
                    // 1. need further investigation, sometimes if there is only one more
                    // message to be loaded it is not loaded but only the dateMessage

                    // retrieve the messages from db
                    MessageArrayContent[] macs = messageHistory.getMessages(buddyId, MESSAGE_LIMIT, messageAmount,
                            true);
                    messageAmount += macs.length;
                    //save position in order not to scroll
                    int index = listView.getFirstVisiblePosition() + 2;
                    View v = listView.getChildAt(2);
                    int top = (v == null) ? 0 : v.getTop();
                    //remove the date
                    maa.remove(1);
                    // calculating whether we need a new DatMessage works with
                    // converting the time in millis in days and the compare the days.
                    // Therefore, I need this constant.
                    final long c = 24 * 60 * 60 * 1000;
                    // get the oldDate which is the date of the first message that is
                    // currently loaded, so the oldest currently loaded message
                    MessageArrayContent macT = maa.getItem(1);
                    long oldDate = (macT instanceof TextMessage) ? ((TextMessage) macT).time
                            : ((ImageMessage) macT).time;
                    // this is needed for restoring the position after adding messages
                    int count = macs.length;
                    // loop through all new messages and add them, increase the count
                    // and eventually insert a DateMessage
                    for (MessageArrayContent macTemp : macs) {
                        if (macTemp instanceof TextMessage) {
                            TextMessage msg = (TextMessage) macTemp;
                            if (msg.time / c < oldDate / c) {
                                maa.insert(new Date(msg.time), 1);
                                count++;
                            }
                            // reset the old date
                            oldDate = msg.time;
                        } else if (macTemp instanceof ImageMessage) {
                            ImageMessage msg = (ImageMessage) macTemp;
                            if (msg.time / c < oldDate / c) {
                                maa.insert(new Date(msg.time), 1);
                                count++;
                            }
                            oldDate = msg.time;
                        }
                        maa.insert(macTemp, 1);
                    }
                    // insert the date at the end
                    maa.insert(new Date(oldDate), 1);
                    // reset the selection (aka scroll height
                    listView.setSelectionFromTop(index + count, top);
                    // if there are no more messages to be loaded than I currently have
                    // loaded remove the loadMoreMessages item
                    if (messageAmount >= messageHistory.getMessageAmount(buddyId)) {
                        maa.remove(0);
                        count--;
                    }
                    // hmh reselect the new scrollHeight?! why not
                    listView.setSelectionFromTop(index + count, top);
                }
            }
        });

        // activate the multiSelectionOption
        listView.setChoiceMode(ListView.CHOICE_MODE_MULTIPLE_MODAL);
        listView.setMultiChoiceModeListener(multiChoiceModeListener);

        // yeah loading messages, seems to be a good idea
        loadAllMessages();
        // set the status in the actionBar
        String lastOnline = messageHistory.getOnline(buddyId);
        updateStatus(lastOnline);
        //scroll down. Due to "I don't know why" clicking a notification is
        // different from clicking a chat in the ChatListFragment and therefore I
        // need to select the last message.
        listView.setSelection(maa.getCount() - 1);
    }

    /**
     * initialize the emojiconKeyboard
     */
    private void initEmoji() {
        // save the views I will use
        final EmojiconEditText emojiconEditText = (EmojiconEditText) getActivity().findViewById(R.id.chat_in);
        final ImageButton emojiBtn = (ImageButton) getActivity().findViewById(R.id.emoti_switch);
        final EmojiconPopup popup = new EmojiconPopup(getActivity().findViewById(R.id.root_view), getContext(),
                new EmojiconGridView.OnEmojiconClickedListener() {
                    @Override
                    public void OnEmojiconClicked(Emojicon emojicon) {
                        if (emojiconEditText == null || emojicon == null)
                            return;
                        int start = emojiconEditText.getSelectionStart();
                        int end = emojiconEditText.getSelectionEnd();
                        if (start < 0)
                            emojiconEditText.append(emojicon.getEmoji());
                        else
                            emojiconEditText.getText().replace(Math.min(start, end), Math.max(start, end),
                                    emojicon.getEmoji(), 0, emojicon.getEmoji().length());
                    }
                });
        popup.setSoftKeyboardSize();

        popup.setOnSoftKeyboardOpenCloseListener(new EmojiconPopup.OnSoftKeyboardOpenCloseListener() {
            @Override
            public void onKeyboardOpen(int keyboardHeight) {
            }

            @Override
            public void onKeyboardClose() {
                if (popup.isShowing())
                    popup.dismiss();
            }
        });
        // open/close the emojicon keyboard when pressing the button
        emojiBtn.setOnClickListener(new View.OnClickListener() {
            @Override
            public void onClick(View v) {
                if (!popup.isShowing()) {
                    if (popup.isKeyboardOpen())
                        popup.showAtBottom();
                    else {
                        emojiconEditText.setFocusableInTouchMode(true);
                        emojiconEditText.requestFocus();
                        popup.showAtBottomPending();
                        InputMethodManager imm = (InputMethodManager) getActivity()
                                .getSystemService(Context.INPUT_METHOD_SERVICE);
                        imm.showSoftInput(emojiconEditText, InputMethodManager.SHOW_IMPLICIT);
                    }
                } else
                    popup.dismiss();
            }
        });

        popup.setOnEmojiconBackspaceClickedListener(new EmojiconPopup.OnEmojiconBackspaceClickedListener() {
            @Override
            public void onEmojiconBackspaceClicked(View view) {
                emojiconEditText.dispatchKeyEvent(
                        new KeyEvent(0, 0, 0, KeyEvent.KEYCODE_DEL, 0, 0, 0, 0, KeyEvent.KEYCODE_ENDCALL));
            }
        });
    }

    /**
     * will load the background image
     */
    private void loadWallPaper() {
        // retrieve the wallpaper file
        final File file = new File(getActivity().getFilesDir(), Constants.WALLPAPER_NAME);
        if (file.exists()) {
            // if it exists retrieve the wallpaperImageView and load the image
            final WallpaperImageView imageView = (WallpaperImageView) getView().findViewById(R.id.chat_wallpaper);
            ViewTreeObserver vto = imageView.getViewTreeObserver();
            // load the image onPreDraw because I need to measuredWidth and height
            // that would be zero if retrieving them onResume or anything else...
            vto.addOnPreDrawListener(new ViewTreeObserver.OnPreDrawListener() {
                @Override
                public boolean onPreDraw() {
                    // only call this function once after resuming
                    imageView.getViewTreeObserver().removeOnPreDrawListener(this);
                    int width = imageView.getMeasuredWidth();
                    int height = imageView.getMeasuredHeight();
                    // start the backgroundImageLoaderTask
                    new WallpaperWorkerTask(imageView, width, height).execute(file);
                    return true;
                }
            });
        }
    }

    /**
     * send the message to the current buddy. The message needs to be added to
     * the messageHistory and then will be sent with this method. If successful
     * this method will update the status of the message in the messageHistory
     *
     * @param message the string to be sent as a textMessage
     * @param id      the messageId under which it is stored in the messageHistory
     */
    private void sendTextMessage(String message, long id) {
        try {
            XmppManager xmppManager = XmppManager.getInstance(getContext());
            if (xmppManager.sendTextMessage(message, buddyId, id)) {
                // if successful update the messageStatus
                messageHistory.updateMessageStatus(buddyId, id, MessageHistory.STATUS_SENT);
                int i = 0;
                // check for all messages currently loaded and update the
                // corresponding one
                for (MessageArrayContent mac : maa) {
                    if (mac instanceof TextMessage) {
                        TextMessage msg = (TextMessage) mac;
                        if (msg._ID == id) {
                            msg.status = MessageHistory.STATUS_SENT;
                            maa.getView(i, listView.getChildAt(i - listView.getFirstVisiblePosition()), listView);
                        }
                    }
                    i++;
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * send the message to the current buddy. The message needs to be added to
     * the messageHistory and then will be sent with this method. If successful
     * this method will update the status of the message in the messageHistory
     *
     * @param msg the message to be sent
     */
    private void sendTextMessage(TextMessage msg) {
        sendTextMessage(msg.message, msg._ID);
    }

    /**
     * load the amount of messages specified by messageAmount
     */
    private void loadAllMessages() {
        // clear the list
        maa.clear();
        // retrieve the messages from the messageHistory
        MessageArrayContent[] messages;
        try {
            messages = messageHistory.getMessages(buddyId, messageAmount);
        } catch (Exception e) {
            messages = new MessageArrayContent[0];
        }
        // the date is to be saved for adding the DateMessages
        long oldDate = 0;
        // this constants is needed for calculating from ms to days
        final long c = 24 * 60 * 60 * 1000;
        NewMessage nm = null;

        // if there are more messages stored than currently visible add the
        // LoadMoreMessages entry
        if (messageAmount < messageHistory.getMessageAmount(buddyId))
            maa.add(new LoadMoreMessages());

        // now loop through every message and add it
        for (MessageArrayContent message : messages) {
            // for typecasting I need to differentiate between Text and ImageMessages
            if (message instanceof TextMessage) {
                TextMessage msg = (TextMessage) message;
                // if necessary add a DateMessage
                if (msg.time / c > oldDate / c)
                    maa.add(new Date(msg.time));
                oldDate = msg.time;
                if (msg.left) {
                    // if we received and have not opened it until now, show / update
                    // the NewMessage
                    if (MessageHistory.STATUS_RECEIVED.equals(msg.status)) {
                        // if we have none, create it and add it
                        if (nm == null) {
                            nm = new NewMessage(getResources().getString(R.string.new_message));
                            maa.add(nm);
                            // if we have one just update it to make sure it shows the
                            // correct message (plural because there are at least 2 new
                            // messages)
                        } else
                            nm.status = getResources().getString(R.string.new_messages);
                        //send the read acknowledgement and update the messageHistory
                        try {
                            messageHistory.updateMessageStatus(buddyId, msg._ID, MessageHistory.STATUS_READ);
                            XmppManager.getInstance().sendAcknowledgement(buddyId, msg.othersId,
                                    MessageHistory.STATUS_READ);
                        } catch (Exception e) {
                            e.printStackTrace();
                        }
                    }
                    // if it is not on the left side but we have not sent it, send it
                } else if (MessageHistory.STATUS_WAITING.equals(msg.status))
                    sendTextMessage(msg);
                // finally add it
                maa.add(msg);
            } else if (message instanceof ImageMessage) {
                // this basically the same as above
                ImageMessage msg = (ImageMessage) message;
                if (msg.time / c > oldDate / c)
                    maa.add(new Date(msg.time));
                oldDate = msg.time;
                if (msg.left) {
                    if (msg.status.equals(MessageHistory.STATUS_RECEIVED)) {
                        if (nm == null) {
                            nm = new NewMessage(getResources().getString(R.string.new_message));
                            maa.add(nm);
                        } else
                            nm.status = getResources().getString(R.string.new_messages);
                        // ImageMessage may also be in waiting status when they are on
                        // the left side, they need to be downloaded.
                    } else if (!msg.status.equals(MessageHistory.STATUS_READ))
                        downloadImage(msg);
                } else if (MessageHistory.STATUS_WAITING.equals(msg.status)) {
                    // don't send them but upload them
                    Upload.Task task = new Upload.Task(new File(msg.file), msg.chatId, msg._ID);
                    new Upload().uploadFile(getContext(), task);
                    msg.status = MessageHistory.STATUS_SENDING;
                }
                maa.add(msg);
            }
        }
        // select the most bottom message for scrolling
        listView.setSelection(maa.getCount() - 1);
    }

    /**
     * update the status of the buddy
     *
     * @param lastOnline the string representing the last online status
     */
    private void updateStatus(String lastOnline) {
        try {
            if (lastOnline != null) {
                long time = Long.valueOf(lastOnline);
                // 0 means currently online
                if (time > 0) {
                    // convert the long value into a human readable format
                    Calendar startOfDay = Calendar.getInstance();
                    startOfDay.set(Calendar.HOUR_OF_DAY, 0);
                    startOfDay.set(Calendar.MINUTE, 0);
                    startOfDay.set(Calendar.SECOND, 0);
                    startOfDay.set(Calendar.MILLISECOND, 0);
                    long diff = startOfDay.getTimeInMillis() - time;
                    // diff between start of the day and lastOnline means buddy was
                    // online today
                    if (diff <= 0)
                        lastOnline = String.format(getResources().getString(R.string.last_online_today), time);
                    // if the diff is greater than 'c' than it was beyond yesterday and
                    // I will show the date
                    else if (diff > 1000 * 60 * 60 * 24)
                        lastOnline = String.format(getResources().getString(R.string.date_and_time), time);
                    // if it wasn't today and also wasn't beyond yesterday it probably
                    // was yesterday.
                    else
                        lastOnline = String.format(getResources().getString(R.string.last_online_yesterday), time);
                    // and set the subtitle of the action bar
                    if (actionBar != null)
                        actionBar.setSubtitle(lastOnline);
                } else {
                    // set the subtitle but make it !blue! xD
                    if (actionBar != null)
                        actionBar.setSubtitle(Html.fromHtml("<font " + "color='#55AAFF'>"
                                + getResources().getString(R.string.online) + "</font>"));
                }
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * update the message at the given index
     *
     * @param index the index where to update the image
     */
    private void updateMessage(int index) {
        updateMessage(index, false);
    }

    /**
     * update the message at the given index
     *
     * @param index       the index where to update the image
     * @param reloadImage if true and the message contains an image it will be
     *                    reloaded. this should always be true if this is the
     *                    case because due to recycling the imageView,
     *                    otherwise, might contain a wrong image.
     */
    private void updateMessage(int index, boolean reloadImage) {
        try {
            // get the current view
            View v = listView.getChildAt(index - listView.getFirstVisiblePosition());
            if (v != null) {
                // and call the getView function for reloading the view
                maa.getView(index, v, listView, reloadImage);
            }
        } catch (Exception e) {
            e.printStackTrace();
        }
    }

    /**
     * download the image, update the ui while and after downloading, update
     * the messageHistory and send an readAcknowledgement
     *
     * @param msg the message where the image should be downloaded
     */
    private void downloadImage(ImageMessage msg) {
        // don't download messages I sent by myself
        if (msg.left)
            try {
                // I need access to the external storage to do so
                MyFileUtils mfu = new MyFileUtils();
                if (!mfu.isExternalStorageWritable())
                    throw new Exception("ext storage not writable. Cannot save " + "image");
                // signal that the message is currently downloading
                messageHistory.updateMessageStatus(buddyId, msg._ID, MessageHistory.STATUS_RECEIVING);
                msg.status = MessageHistory.STATUS_RECEIVING;
                // start the download service with all necessary extras
                Intent intent = new Intent(getContext(), MessageDownloadService.class);
                intent.setAction(MessageDownloadService.DOWNLOAD_ACTION);
                intent.putExtra(MessageDownloadService.PARAM_URL, msg.url);
                // this receiver will receive updates for the download that can be
                // posted to the ui
                intent.putExtra(MessageDownloadService.PARAM_RECEIVER, new DownloadReceiver(new Handler()));
                intent.putExtra(MessageDownloadService.PARAM_FILE, msg.file);
                intent.putExtra(MessageDownloadService.PARAM_MESSAGE_ID, msg._ID);
                intent.putExtra(MessageDownloadService.PARAM_OTHERS_MSG_ID, msg.othersId);
                intent.putExtra(MessageDownloadService.PARAM_CHAT_ID, buddyId);
                getContext().startService(intent);
            } catch (Exception e) {
                e.printStackTrace();
            }
    }

    /**
     * This interface must be implemented by ui that contain this
     * fragment to allow an interaction in this fragment to be communicated
     * to the activity and potentially other chatting contained in that
     * activity.
     * <p/>
     * See the Android Training lesson <a href=
     * "http://developer.android.com/training/basics/fragments/communicating.html"
     * >Communicating with Other Fragments</a> for more information.
     */
    public interface OnChatFragmentInteractionListener {
        void onAttachClicked(View view);

        void sendImages(Uri... imageUris);
    }

    /**
     * this will load the wallpaper from the storage
     */
    class WallpaperWorkerTask extends AsyncTask<File, Void, Bitmap> {
        private final WeakReference<ImageView> imageViewWeakReference;
        private File data;
        private int width, height;

        public WallpaperWorkerTask(ImageView imageView, int width, int height) {
            // store the weakReference and the width, height
            imageViewWeakReference = new WeakReference<>(imageView);
            this.width = width;
            this.height = height;
        }

        @Override
        protected Bitmap doInBackground(File... params) {
            // only if the file exists
            if (!params[0].isFile())
                return null;
            data = params[0];

            // First decode with inJustDecodeBounds=true to check dimensions
            final BitmapFactory.Options options = new BitmapFactory.Options();
            options.inJustDecodeBounds = true;
            BitmapFactory.decodeFile(data.getAbsolutePath(), options);

            // Calculate inSampleSize
            options.inSampleSize = calculateInSampleSize(options, width, height);

            // Decode bitmap with inSampleSize set
            options.inJustDecodeBounds = false;
            Bitmap bitmap = null;
            try {
                bitmap = BitmapFactory.decodeFile(data.getAbsolutePath(), options);
            } catch (Exception e) {
                e.printStackTrace();
            }
            return bitmap;
        }

        @Override
        protected void onPostExecute(Bitmap bitmap) {
            // if I really decoded a bitmap and if the imageView still exists set
            // the bitmap
            if (bitmap != null) {
                final ImageView imageView = imageViewWeakReference.get();
                if (imageView != null)
                    imageView.setImageBitmap(bitmap);
            }
        }
    }

    //see MAA
    private int calculateInSampleSize(BitmapFactory.Options options, int reqWidth, int reqHeight) {
        if (reqHeight <= 0 && reqWidth <= 0)
            throw new IllegalArgumentException("reqWidth and reqHeigth must be " + "positive.");
        final int height = options.outHeight;
        final int width = options.outWidth;
        int inSampleSize = 1;

        if (height > reqHeight || width > reqWidth) {

            final int halfHeight = height / 2;
            final int halfWidth = width / 2;
            while ((halfHeight / inSampleSize) > reqHeight && (halfWidth / inSampleSize) > reqWidth) {
                inSampleSize *= 2;
            }
        }
        return inSampleSize;
    }

    /**
     * this receiver will receive results from the imageDownloadTask
     */
    // I don't know what androidStudios problem with this is and as it works
    // perfectly fine I won't fix this shit
    public class DownloadReceiver extends ResultReceiver {

        public DownloadReceiver(Handler handler) {
            super(handler);
        }

        @Override
        protected void onReceiveResult(int resultCode, Bundle resultData) {
            super.onReceiveResult(resultCode, resultData);
            // if I want to receive this
            if (resultCode == MessageDownloadService.UPDATE_PROGRESS) {
                // get all necessary data
                Long messageId = resultData.getLong(MessageDownloadService.PARAM_MESSAGE_ID);
                int progress = resultData.getInt(MessageDownloadService.PARAM_PROGRESS);
                int size = listView.getLastVisiblePosition();
                MessageArrayContent mac;
                // if we are not finished loop through all visible messages and
                // update the correct
                if (progress < 100) {
                    for (int i = listView.getFirstVisiblePosition(); i <= size; i++) {
                        mac = maa.getItem(i);
                        if (mac instanceof ImageMessage) {
                            ImageMessage im = (ImageMessage) mac;
                            if (im._ID == messageId) {
                                Log.d("DEBUG DOWNLOAD", "progress: " + progress);
                                ((ImageMessage) mac).progress = progress;
                                updateMessage(i);
                            }
                        }
                    }
                } else {
                    // otherwise, loop through all messages and update the correct. I
                    // do this just to make sure the message is at least in the end
                    // correctly displayed
                    for (int i = listView.getFirstVisiblePosition(); i <= size; i++) {
                        mac = maa.getItem(i);
                        if (mac instanceof ImageMessage) {
                            ImageMessage im = (ImageMessage) mac;
                            if (im._ID == messageId) {
                                Log.d("DEBUG DOWNLOAD", "progress: " + progress);
                                im.status = MessageHistory.STATUS_READ;
                                updateMessage(i);
                                messageHistory.updateMessageStatus(buddyId, messageId, MessageHistory.STATUS_READ);
                                updateMessage(i, true);

                            }
                        }
                    }
                }
            }
        }
    }
}