ca.psiphon.ploggy.FragmentFriendList.java Source code

Java tutorial

Introduction

Here is the source code for ca.psiphon.ploggy.FragmentFriendList.java

Source

/*
 * Copyright (c) 2013, Psiphon Inc.
 * All rights reserved.
 *
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 *
 */

package ca.psiphon.ploggy;

import java.util.List;

import android.app.AlertDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.os.Bundle;
import android.support.v4.app.ListFragment;
import android.view.ContextMenu;
import android.view.LayoutInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.widget.AdapterView;
import android.widget.BaseAdapter;
import android.widget.ImageView;
import android.widget.ListView;
import android.widget.TextView;

import com.squareup.otto.Subscribe;

/**
 * User interface which displays a list of friends.
    
 * This class subscribes to friend and status events to update displayed data
 * while in the foreground.
 */
public class FragmentFriendList extends ListFragment {

    private static final String LOG_TAG = "Friend List";

    private boolean mIsResumed = false;
    private FriendAdapter mFriendAdapter;
    Utils.FixedDelayExecutor mRefreshUIExecutor;

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = super.onCreateView(inflater, container, savedInstanceState);
        try {
            mFriendAdapter = new FriendAdapter(getActivity());
        } catch (Utils.ApplicationError e) {
            Log.addEntry(LOG_TAG, "failed to initialize friend adapter");
        }

        // Refresh the message list every 5 seconds. This updates "time ago" displays.
        // TODO: event driven redrawing?
        mRefreshUIExecutor = new Utils.FixedDelayExecutor(new Runnable() {
            @Override
            public void run() {
                updateFriends();
            }
        }, 5000);

        return view;
    }

    @Override
    public void onActivityCreated(Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        if (mFriendAdapter != null) {
            setListAdapter(mFriendAdapter);
        }
        registerForContextMenu(getListView());
        Events.register(this);
    }

    @Override
    public void onResume() {
        super.onResume();
        mIsResumed = true;
        mRefreshUIExecutor.start();
        Events.post(new Events.DisplayedFriends());
    }

    @Override
    public void onPause() {
        super.onPause();
        mIsResumed = false;
        mRefreshUIExecutor.stop();
    }

    @Override
    public void onDestroyView() {
        Events.unregister(this);
        super.onDestroyView();
    }

    @Override
    public void onListItemClick(ListView listView, View view, int position, long id) {
        Data.Friend friend = (Data.Friend) listView.getItemAtPosition(position);
        Intent intent = new Intent(getActivity(), ActivityFriendStatusDetails.class);
        Bundle bundle = new Bundle();
        bundle.putString(ActivityFriendStatusDetails.FRIEND_ID_BUNDLE_KEY, friend.mId);
        intent.putExtras(bundle);
        startActivity(intent);
    }

    @Override
    public void onCreateContextMenu(ContextMenu menu, View view, ContextMenu.ContextMenuInfo menuInfo) {
        super.onCreateContextMenu(menu, view, menuInfo);
        if (view.equals(getListView())) {
            getActivity().getMenuInflater().inflate(R.menu.friend_list_context, menu);
        }
    }

    @Override
    public boolean onContextItemSelected(MenuItem item) {
        AdapterView.AdapterContextMenuInfo info = (AdapterView.AdapterContextMenuInfo) item.getMenuInfo();
        if (item.getItemId() == R.id.action_friend_list_delete_friend) {
            final Data.Friend finalFriend = (Data.Friend) getListView().getItemAtPosition(info.position);
            new AlertDialog.Builder(getActivity()).setTitle(getString(R.string.label_delete_friend_title))
                    .setMessage(
                            getString(R.string.label_delete_friend_message, finalFriend.mPublicIdentity.mNickname))
                    .setPositiveButton(getString(R.string.label_delete_friend_positive),
                            new DialogInterface.OnClickListener() {
                                @Override
                                public void onClick(DialogInterface dialog, int which) {
                                    try {
                                        Data.getInstance().removeFriend(finalFriend.mId);
                                    } catch (Data.DataNotFoundError e) {
                                        // Ignore
                                    } catch (Utils.ApplicationError e) {
                                        Log.addEntry(LOG_TAG, "failed to delete friend: "
                                                + finalFriend.mPublicIdentity.mNickname);
                                    }
                                }
                            })
                    .setNegativeButton(getString(R.string.label_delete_friend_negative), null).show();
            return true;
        }
        return super.onContextItemSelected(item);
    }

    @Subscribe
    public void onAddedFriend(Events.AddedFriend addedFriend) {
        updateFriends();
    }

    @Subscribe
    public void onUpdatedFriend(Events.UpdatedFriend updatedFriend) {
        updateFriends();
    }

    @Subscribe
    public void onUpdatedFriendStatus(Events.UpdatedFriendStatus updatedFriendStatus) {
        updateFriends();
    }

    @Subscribe
    public void onDeletedFriend(Events.RemovedFriend removedFriend) {
        updateFriends();
    }

    @Subscribe
    public void onUpdatedNewMessages(Events.UpdatedNewMessages updatedNewMessages) {
        if (mIsResumed) {
            Events.post(new Events.DisplayedFriends());
        }
    }

    private void updateFriends() {
        try {
            mFriendAdapter.updateFriends();
        } catch (Utils.ApplicationError e) {
            Log.addEntry(LOG_TAG, "failed to update friend list");
        }
    }

    private static class FriendAdapter extends BaseAdapter {
        private final Context mContext;
        private List<Data.Friend> mFriends;

        public FriendAdapter(Context context) throws Utils.ApplicationError {
            mContext = context;
            mFriends = Data.getInstance().getFriends();
        }

        public void updateFriends() throws Utils.ApplicationError {
            mFriends = Data.getInstance().getFriends();
            notifyDataSetChanged();
        }

        @Override
        public View getView(int position, View view, ViewGroup parent) {
            if (view == null) {
                LayoutInflater inflater = (LayoutInflater) mContext
                        .getSystemService(Context.LAYOUT_INFLATER_SERVICE);
                view = inflater.inflate(R.layout.friend_list_row, null);
            }
            Data.Friend friend = mFriends.get(position);
            if (friend != null) {
                ImageView avatarImage = (ImageView) view.findViewById(R.id.friend_list_avatar_image);
                TextView nicknameText = (TextView) view.findViewById(R.id.friend_list_nickname_text);
                TextView lastTimestampText = (TextView) view.findViewById(R.id.friend_list_last_timestamp_text);
                TextView messageTimestampText = (TextView) view
                        .findViewById(R.id.friend_list_message_timestamp_text);
                TextView messageContentText = (TextView) view.findViewById(R.id.friend_list_message_content_text);
                TextView locationTimestampText = (TextView) view
                        .findViewById(R.id.friend_list_location_timestamp_text);
                TextView locationStreetAddressText = (TextView) view
                        .findViewById(R.id.friend_list_location_street_address_text);
                TextView locationDistanceText = (TextView) view
                        .findViewById(R.id.friend_list_location_distance_text);

                // Not hiding missing fields
                lastTimestampText.setText("");
                messageTimestampText.setText("");
                messageContentText.setText("");
                locationTimestampText.setText("");
                locationStreetAddressText.setText("");
                locationDistanceText.setText("");

                Robohash.setRobohashImage(mContext, avatarImage, true, friend.mPublicIdentity);
                nicknameText.setText(friend.mPublicIdentity.mNickname);
                try {
                    Data data = Data.getInstance();
                    Data.Location selfLocation = null;
                    try {
                        selfLocation = data.getCurrentSelfLocation();
                    } catch (Data.DataNotFoundError e) {
                        // Won't be able to compute distance
                    }

                    Data.Status friendStatus = data.getFriendStatus(friend.mId);

                    // Display most recent successful communication timestamp
                    String lastTimestamp = "";
                    if (friend.mLastReceivedStatusTimestamp != null && (friend.mLastSentStatusTimestamp == null
                            || friend.mLastReceivedStatusTimestamp.after(friend.mLastSentStatusTimestamp))) {
                        lastTimestamp = Utils.DateFormatter.formatRelativeDatetime(mContext,
                                friend.mLastReceivedStatusTimestamp, true);
                    } else if (friend.mLastSentStatusTimestamp != null) {
                        lastTimestamp = Utils.DateFormatter.formatRelativeDatetime(mContext,
                                friend.mLastSentStatusTimestamp, true);
                    }
                    lastTimestampText.setText(lastTimestamp);
                    if (lastTimestamp.length() > 0) {
                        // On touch, show log entries
                        lastTimestampText.setOnClickListener(new View.OnClickListener() {
                            @Override
                            public void onClick(View v) {
                                mContext.startActivity(new Intent(mContext, ActivityLogEntries.class));
                            }
                        });
                    }

                    if (friendStatus.mMessages.size() > 0) {
                        Data.Message message = friendStatus.mMessages.get(0);
                        messageContentText.setText(message.mContent);
                        messageTimestampText.setText(
                                Utils.DateFormatter.formatRelativeDatetime(mContext, message.mTimestamp, true));
                    }
                    if (friendStatus.mLocation.mTimestamp != null) {
                        locationTimestampText.setText(Utils.DateFormatter.formatRelativeDatetime(mContext,
                                friendStatus.mLocation.mTimestamp, true));
                        if (friendStatus.mLocation.mStreetAddress.length() > 0) {
                            locationStreetAddressText.setText(friendStatus.mLocation.mStreetAddress);
                        } else {
                            locationStreetAddressText.setText(R.string.prompt_no_street_address_reported);
                        }
                        if (selfLocation != null && selfLocation.mTimestamp != null) {
                            int distance = Utils.calculateLocationDistanceInMeters(selfLocation.mLatitude,
                                    selfLocation.mLongitude, friendStatus.mLocation.mLatitude,
                                    friendStatus.mLocation.mLongitude);
                            locationDistanceText.setText(Utils.formatDistance(mContext, distance));
                        } else {
                            locationDistanceText.setText(R.string.prompt_unknown_distance);
                        }
                    }
                } catch (Data.DataNotFoundError e) {
                    messageTimestampText.setText(R.string.prompt_no_status_updates_received);
                } catch (Utils.ApplicationError e) {
                    Log.addEntry(LOG_TAG, "failed to display friend");
                }
            }
            return view;
        }

        @Override
        public int getCount() {
            return mFriends.size();
        }

        @Override
        public Object getItem(int position) {
            return mFriends.get(position);
        }

        @Override
        public long getItemId(int position) {
            return position;
        }
    }
}