ca.ualberta.cmput301w14t08.geochan.adapters.ThreadViewAdapter.java Source code

Java tutorial

Introduction

Here is the source code for ca.ualberta.cmput301w14t08.geochan.adapters.ThreadViewAdapter.java

Source

/*
 * Copyright 2014 Artem Chikin
 * Copyright 2014 Artem Herasymchuk
 * Copyright 2014 Tom Krywitsky
 * Copyright 2014 Henry Pabst
 * Copyright 2014 Bradley Simons
 *
 * 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 ca.ualberta.cmput301w14t08.geochan.adapters;

import java.math.RoundingMode;
import java.text.DecimalFormat;
import java.util.ArrayList;

import android.content.Context;
import android.graphics.Color;
import android.os.Bundle;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentManager;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.BaseAdapter;
import android.widget.ImageButton;
import android.widget.LinearLayout;
import android.widget.TextView;
import android.widget.Toast;
import ca.ualberta.cmput301w14t08.geochan.R;
import ca.ualberta.cmput301w14t08.geochan.fragments.EditFragment;
import ca.ualberta.cmput301w14t08.geochan.fragments.ExpandImageFragment;
import ca.ualberta.cmput301w14t08.geochan.fragments.MapViewFragment;
import ca.ualberta.cmput301w14t08.geochan.fragments.PostFragment;
import ca.ualberta.cmput301w14t08.geochan.helpers.HashHelper;
import ca.ualberta.cmput301w14t08.geochan.managers.PreferencesManager;
import ca.ualberta.cmput301w14t08.geochan.models.Comment;
import ca.ualberta.cmput301w14t08.geochan.models.FavouritesLog;
import ca.ualberta.cmput301w14t08.geochan.models.GeoLocation;
import ca.ualberta.cmput301w14t08.geochan.models.ThreadComment;

/**
 * Adapter used for displaying a ThreadComment in the ThreadViewFragment. It
 * inflates layouts for OP, top level comments and comment replies and listens to
 * the buttons of the listView elements that are visible at all times.
 * 
 * @author Artem Chikin
 */
public class ThreadViewAdapter extends BaseAdapter {
    // Constants for all the poosible layout types.
    private static final int TYPE_COMMENT0 = 0;
    private static final int TYPE_COMMENT1 = 1;
    private static final int TYPE_COMMENT2 = 2;
    private static final int TYPE_COMMENT3 = 3;
    private static final int TYPE_COMMENT4 = 4;
    private static final int TYPE_COMMENT5 = 5;
    private static final int TYPE_COMMENT6 = 6;
    private static final int TYPE_COMMENT7 = 7;
    private static final int TYPE_COMMENTMAX = 10;
    private static final int TYPE_OP = 8;
    private static final int TYPE_SEPARATOR = 9;
    private static final int TYPE_MAX_COUNT = 11;

    private Context context;
    private int id;
    private ThreadComment thread;
    private ArrayList<Comment> comments;
    private FragmentManager manager;

    /**
     * Constructs the adapter and initializes all of its components.
     * @param context  The Context the adapter is running in.
     * @param thread  The ThreadComment to display.
     * @param manager  The FragmentManager with this adapter's fragment.
     * @param id  The index of the ThreadComment in the ThreadList.
     */
    public ThreadViewAdapter(Context context, ThreadComment thread, FragmentManager manager, int id) {
        super();
        this.context = context;
        this.thread = thread;
        this.manager = manager;
        this.id = id;
        this.comments = new ArrayList<Comment>();
        buildAList(thread.getBodyComment());
    }

    /**
     * Takes a comment and recursively builds a list of comment
     * objects from the Comment's children tree.
     * This is the list we display in our listView.
     * 
     * @param comment The Comment to recursively build a list
     * from.
     * 
     */
    private void buildAList(Comment comment) {
        ArrayList<Comment> children = comment.getChildren();
        if (children.size() == 0) {
            return;
        } else {
            for (Comment c : children) {
                comments.add(c);
                buildAList(c);
            }
        }
    }

    /**
     * Sets the ThreadComment that the adapter is displaying
     * and refreshes the adapter.
     * 
     * @param thread The new ThreadComment for the adapter to display.
     * 
     */
    public void setThread(ThreadComment thread) {
        this.thread = thread;
        buildAList(thread.getBodyComment());
        this.notifyDataSetChanged();
    }

    /**
     * Returns the number of items in this adapter,
     * includes all posts (including the bodyComment) and the separator
     * between the bodyComment and the other Comments.
     * 
     * @return The number of items in the adapter.
     */
    @Override
    public int getCount() {
        int size = getCountChildren(thread.getBodyComment());
        return size + 2; // The +2 is for OP + Separator
    }

    /**
     * This method recursively counts the total amount of children a comment
     * object has.
     * 
     * @param comment  The Comment to determine the number of children of.
     * @return The number of children.
     * 
     */
    private int getCountChildren(Comment comment) {
        if (comment.getChildren().size() == 0) {
            return 0;
        }
        int size = 0;

        for (Comment c : comment.getChildren()) {
            ++size;
            size += getCountChildren(c);
        }
        return size;
    }

    /**
     * Gets an item at the specific position in the list.
     * @param position  The position.
     * @return The item.
     */
    @Override
    public Object getItem(int position) {
        if (position == 0) {
            return thread.getBodyComment();
        }
        if (position == 1) {
            return null;
        } else {
            // -2 because of OP and SEPARATOR
            return comments.get(position - 2);
        }
    }

    /**
     * Gets the id of an item at a specific position.
     * @param position  The position.
     * @return The id.
     */
    @Override
    public long getItemId(int position) {
        return position;
    }

    /**
     * Returns the number of views that this adapter can create.
     * @return The number of views the adapter can create.
     */
    @Override
    public int getViewTypeCount() {
        return TYPE_MAX_COUNT;
    }

    /**
     * Return the layout type for a list element,
     * OP, SEPARATOR, 7 COMMENT depths and COMMENT_MAX types.
     * 
     * @param position The position.
     * @return The layout type.
     */
    @Override
    public int getItemViewType(int position) {
        int type = 0;
        if (position == 0) {
            type = TYPE_OP;
        } else if (position == 1) {
            type = TYPE_SEPARATOR;
        } else {
            int depth = ((Comment) getItem(position)).getDepth();
            if (depth <= 7) {
                type = depth;
            } else {
                type = TYPE_COMMENTMAX;
            }
        }
        return type;
    }

    /**
     * Depending on the list item type, inflate the correct
     * layout, start listening for its buttons.
     * @param position The position.
     * @param convertView A previous recycled View.
     * @param parent The parent View.
     * @return The View.
     */
    @Override
    public View getView(int position, View convertView, ViewGroup parent) {
        int type = getItemViewType(position);
        final Comment comment = (Comment) getItem(position);
        switch (type) {
        case TYPE_OP:
            convertView = setConvertView(convertView, R.layout.thread_view_op);
            setOPFields(convertView);
            listenForThreadButtons(convertView, thread);
            break;

        case TYPE_COMMENT0:
            convertView = setConvertView(convertView, R.layout.thread_view_comment_0);
            setCommentFields(convertView, comment);
            listenForThumbnail(convertView, comment);
            break;

        case TYPE_COMMENT1:
            convertView = setConvertView(convertView, R.layout.thread_view_comment_1);
            setCommentFields(convertView, comment);
            listenForThumbnail(convertView, comment);
            break;

        case TYPE_COMMENT2:
            convertView = setConvertView(convertView, R.layout.thread_view_comment_2);
            setCommentFields(convertView, comment);
            listenForThumbnail(convertView, comment);
            break;

        case TYPE_COMMENT3:
            convertView = setConvertView(convertView, R.layout.thread_view_comment_3);
            setCommentFields(convertView, comment);
            listenForThumbnail(convertView, comment);
            break;

        case TYPE_COMMENT4:
            convertView = setConvertView(convertView, R.layout.thread_view_comment_4);
            setCommentFields(convertView, comment);
            listenForThumbnail(convertView, comment);
            break;

        case TYPE_COMMENT5:
            convertView = setConvertView(convertView, R.layout.thread_view_comment_5);
            setCommentFields(convertView, comment);
            listenForThumbnail(convertView, comment);
            break;

        case TYPE_COMMENT6:
            convertView = setConvertView(convertView, R.layout.thread_view_comment_6);
            setCommentFields(convertView, comment);
            listenForThumbnail(convertView, comment);
            break;

        case TYPE_COMMENT7:
            convertView = setConvertView(convertView, R.layout.thread_view_comment_7);
            setCommentFields(convertView, comment);
            listenForThumbnail(convertView, comment);
            break;

        case TYPE_SEPARATOR:
            convertView = setConvertView(convertView, R.layout.thread_view_separator);
            TextView numComments = (TextView) convertView.findViewById(R.id.textSeparator);
            numComments.setText(Integer.toString(getCount() - 2) + " Comments:");
            break;

        case TYPE_COMMENTMAX:
            final Comment commentMax = (Comment) getItem(position);
            convertView = setConvertView(convertView, R.layout.thread_view_comment_max);
            setCommentFields(convertView, commentMax);
            // TYPE_COMMENTMAX has an extra field: depth relative to the maximum 
            // depth layout.
            TextView depthMeter = (TextView) convertView.findViewById(R.id.thread_view_comment_depth_meter);
            depthMeter.setText("Max depth + " + Integer.toString(commentMax.getDepth() - 7));
            listenForThumbnail(convertView, comment);
            break;

        }
        return convertView;
    }

    /**
     * Listen for the thumbnail of a comment, onClick start the 
     * ExpandImageFragment.
     * 
     * @param convertView The view that contains the thumbnail.
     * @param comment The corresponding Comment object.
     */
    private void listenForThumbnail(View convertView, final Comment comment) {
        ImageButton thumbnail = (ImageButton) convertView.findViewById(R.id.thread_view_comment_thumbnail);
        if (thumbnail != null) {
            thumbnail.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    // Perform action on click
                    Fragment fragment = new ExpandImageFragment();
                    Bundle bundle = new Bundle();
                    bundle.putString("id", comment.getId());
                    fragment.setArguments(bundle);
                    Fragment fav = manager.findFragmentByTag("favThrFragment");
                    Fragment favCom = manager.findFragmentByTag("favComFragment");
                    if (fav != null) {
                        manager.beginTransaction().add(R.id.container, fragment, "picFrag").addToBackStack(null)
                                .commit();
                    } else if (favCom != null) {
                        manager.beginTransaction().add(R.id.container, fragment, "picFrag").addToBackStack(null)
                                .commit();
                    } else {
                        manager.beginTransaction().add(R.id.fragment_container, fragment, "picFrag")
                                .addToBackStack(null).commit();
                    }
                    manager.executePendingTransactions();
                }
            });
        }
    }

    /**
     * Assign convertview to layout.
     * 
     * @param convertView
     *            The View to inflate
     * @param layout
     *            An R.Layout resource.
     * @return convertView The inflated View.
     */
    private View setConvertView(View convertView, int layout) {
        if (convertView == null) {
            LayoutInflater inflater = (LayoutInflater) context.getSystemService(Context.LAYOUT_INFLATER_SERVICE);
            convertView = inflater.inflate(layout, null);
        }
        return convertView;
    }

    /**
     * Listener for the buttons of the thread op (TYPE_OP) listView item. Comments have
     * listeners in ThreadViewFragment because they are only displayed after
     * onItemClick, threadComment buttons have a listener in the adapter for
     * ease of access to the permanently displayed buttons.
     * 
     * @param convertView view that contains the ThreadComments OP.
     * @param thread threadComment object that contains the ThreadComments OP.
     * 
     */
    private void listenForThreadButtons(View convertView, final ThreadComment thread) {
        // Here handle button presses
        final ImageButton replyButton = (ImageButton) convertView.findViewById(R.id.comment_reply_button);

        final ImageButton starButton = (ImageButton) convertView.findViewById(R.id.comment_star_button);

        if (FavouritesLog.getInstance(context).hasThreadComment(thread.getId())) {
            starButton.setImageResource(R.drawable.ic_rating_marked);
        }

        final ImageButton mapButton = (ImageButton) convertView.findViewById(R.id.thread_map_button);

        final ImageButton editButton = (ImageButton) convertView.findViewById(R.id.thread_edit_button);

        if (HashHelper.getHash(thread.getBodyComment().getUser()).equals(thread.getBodyComment().getHash())) {
            editButton.setVisibility(View.VISIBLE);
        }

        if (thread.getBodyComment().hasImage()) {
            listenForThumbnail(convertView, thread.getBodyComment());
        }

        // Listen for the star(favourites) button.
        if (starButton != null) {
            starButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    // Perform action on click depending on if the thread has
                    // been favourited. 
                    if (!FavouritesLog.getInstance(context).hasThreadComment(thread.getId())) {
                        Toast.makeText(context, "Thread saved to Favourites.", Toast.LENGTH_SHORT).show();
                        starButton.setImageResource(R.drawable.ic_rating_marked);
                        FavouritesLog log = FavouritesLog.getInstance(context);
                        log.addThreadComment(thread);
                    } else {
                        Toast.makeText(context, "Thread removed from Favourites.", Toast.LENGTH_SHORT).show();
                        starButton.setImageResource(R.drawable.ic_rating_important);
                        FavouritesLog log = FavouritesLog.getInstance(context);
                        log.removeThreadComment(thread);
                    }
                }
            });
        }
        // Listen for the edit button.
        if (editButton != null) {
            editButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    Fragment fragment = new EditFragment();
                    Bundle bundle = new Bundle();
                    bundle.putInt("threadIndex", id);
                    bundle.putString("commentId", thread.getBodyComment().getId());
                    fragment.setArguments(bundle);
                    manager.beginTransaction().replace(R.id.fragment_container, fragment, "editFrag")
                            .addToBackStack(null).commit();
                    manager.executePendingTransactions();
                }
            });
        }

        // Listen for the map button.
        if (mapButton != null) {
            mapButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    // Perform action on click to launch mapFragment
                    Bundle bundle = new Bundle();
                    bundle.putParcelable("thread_comment", thread.getBodyComment());

                    Fragment mapFrag = new MapViewFragment();
                    mapFrag.setArguments(bundle);
                    Fragment fav = manager.findFragmentByTag("favThrFragment");
                    if (fav != null) {
                        manager.beginTransaction().replace(R.id.container, mapFrag, "mapFrag").addToBackStack(null)
                                .commit();
                    } else {
                        manager.beginTransaction().replace(R.id.fragment_container, mapFrag, "mapFrag")
                                .addToBackStack(null).commit();
                    }
                    manager.executePendingTransactions();
                }
            });
        }

        // Listen for the reply button.
        if (replyButton != null) {
            replyButton.setOnClickListener(new View.OnClickListener() {
                public void onClick(View v) {
                    // Perform action on click to launch postCommentFragment
                    Fragment fragment = new PostFragment();
                    Bundle bundle = new Bundle();
                    bundle.putParcelable("cmt", thread.getBodyComment());
                    bundle.putLong("id", id);
                    fragment.setArguments(bundle);
                    Fragment fav = manager.findFragmentByTag("favThrFragment");
                    if (fav != null) {
                        manager.beginTransaction().replace(R.id.container, fragment, "postFrag")
                                .addToBackStack(null).commit();
                        manager.executePendingTransactions();
                    } else {
                        manager.beginTransaction().replace(R.id.fragment_container, fragment, "postFrag")
                                .addToBackStack(null).commit();
                        manager.executePendingTransactions();
                    }
                }

            });
        }
    }

    /**
     * Sets the required fields of the orignal post of the thread.
     * Title, creator, comment, timestamp, location.
     * 
     * @param convertView
     *            View container of a listView item.
     */
    private void setOPFields(View convertView) {
        // Thread title
        TextView title = (TextView) convertView.findViewById(R.id.thread_view_op_threadTitle);
        // Special case of Viewing a Favourite Comment in ThreadView
        if (thread.getTitle().equals("")) {
            title.setVisibility(View.GONE);
            LinearLayout buttons = (LinearLayout) convertView.findViewById(R.id.thread_view_op_buttons);
            buttons.setVisibility(View.GONE);
        } else {
            title.setText(thread.getTitle());
        }
        // Thread creator
        TextView threadBy = (TextView) convertView.findViewById(R.id.thread_view_op_commentBy);
        threadBy.setText(
                "Posted by " + thread.getBodyComment().getUser() + "#" + thread.getBodyComment().getHash() + "  ");
        if (HashHelper.getHash(thread.getBodyComment().getUser()).equals(thread.getBodyComment().getHash())) {
            threadBy.setBackgroundResource(R.drawable.username_background_thread_rect);
            threadBy.setTextColor(Color.WHITE);
        }

        // Thread body comment
        TextView body = (TextView) convertView.findViewById(R.id.thread_view_op_commentBody);
        body.setText(thread.getBodyComment().getTextPost());
        // Thread timestamp
        TextView threadTime = (TextView) convertView.findViewById(R.id.thread_view_op_commentDate);
        threadTime.setText(thread.getBodyComment().getCommentDateString());
        // Location text
        TextView origPostLocationText = (TextView) convertView.findViewById(R.id.thread_view_op_locationText);
        GeoLocation loc = thread.getBodyComment().getLocation();
        String locDescriptor = loc.getLocationDescription();
        if (loc != null) {
            if (locDescriptor != null) {
                origPostLocationText.setText("near: " + locDescriptor);
            } else {
                // The rounding of long and lat for max 4 decimal digits.
                DecimalFormat format = new DecimalFormat();
                format.setRoundingMode(RoundingMode.HALF_EVEN);
                format.setMinimumFractionDigits(0);
                format.setMaximumFractionDigits(4);

                origPostLocationText.setText("Latitude: " + format.format(loc.getLatitude()) + " Longitude: "
                        + format.format(loc.getLongitude()));
            }
        }

        // Set the thumbnail if there is an image
        if (thread.getBodyComment().hasImage()) {
            ImageButton thumbnail = (ImageButton) convertView.findViewById(R.id.thread_view_comment_thumbnail);
            thumbnail.setVisibility(View.VISIBLE);
            thumbnail.setFocusable(false);
            thumbnail.setImageBitmap(thread.getBodyComment().getImageThumb());
        }
    }

    /**
     * Sets all the required views for a comment reply. Comment text,
     * creator, time.
     * 
     * @param convertView View that contains the comment.
     * @param reply the Comment object that we are setting fields for.
     * 
     */
    private void setCommentFields(View convertView, Comment reply) {
        if (reply.hasImage()) {
            ImageButton thumbnail = (ImageButton) convertView.findViewById(R.id.thread_view_comment_thumbnail);
            thumbnail.setVisibility(View.VISIBLE);
            thumbnail.setFocusable(false);
            thumbnail.setImageBitmap(reply.getImageThumb());
        } else {
            ImageButton thumbnail = (ImageButton) convertView.findViewById(R.id.thread_view_comment_thumbnail);
            thumbnail.setVisibility(View.GONE);
            thumbnail.setFocusable(false);
        }
        // Comment body
        TextView replyBody = (TextView) convertView.findViewById(R.id.thread_view_comment_commentBody);
        replyBody.setText(reply.getTextPost());
        // Comment creator
        TextView replyBy = (TextView) convertView.findViewById(R.id.thread_view_comment_commentBy);
        replyBy.setText(reply.getUser() + "#" + reply.getHash() + "  ");
        String username = PreferencesManager.getInstance().getUser();

        if (HashHelper.getHash(reply.getUser()).equals(reply.getHash())) {
            replyBy.setBackgroundResource(R.drawable.username_background_rect);
            replyBy.setTextColor(Color.WHITE);
        }
        // Comment timestamp
        TextView replyTime = (TextView) convertView.findViewById(R.id.thread_view_comment_commentDate);
        replyTime.setText(reply.getCommentDateString());
    }
}