/** * Copyright 2010 Mark Wyszomierski */ package com.joelapenna.foursquared; import com.joelapenna.foursquare.Foursquare; import com.joelapenna.foursquare.types.Group; import com.joelapenna.foursquare.types.User; import com.joelapenna.foursquared.app.LoadableListActivityWithViewAndHeader; import com.joelapenna.foursquared.location.LocationUtils; import com.joelapenna.foursquared.util.NotificationsUtil; import com.joelapenna.foursquared.util.UserUtils; import com.joelapenna.foursquared.widget.FriendActionableListAdapter; import com.joelapenna.foursquared.widget.SegmentedButton; import com.joelapenna.foursquared.widget.SegmentedButton.OnClickListenerSegmentedButton; import android.content.BroadcastReceiver; import android.content.Context; import android.content.Intent; import android.content.IntentFilter; import android.location.Location; import android.os.AsyncTask; import android.os.Bundle; import android.util.Log; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.widget.AdapterView; import android.widget.AdapterView.OnItemClickListener; import android.widget.LinearLayout; import android.widget.ListView; import android.widget.ScrollView; import java.util.ArrayList; import java.util.List; /** * Shows the logged-in user's followers and friends. If the user has no followers, then * they should just be shown UserDetailsFriendsActivity directly. * * @date September 25, 2010 * @author Mark Wyszomierski (markww@gmail.com) */ public class UserDetailsFriendsFollowersActivity extends LoadableListActivityWithViewAndHeader { static final String TAG = "UserDetailsFriendsFollowersActivity"; static final boolean DEBUG = FoursquaredSettings.DEBUG; public static final String EXTRA_USER_NAME = Foursquared.PACKAGE_NAME + ".UserDetailsFriendsFollowersActivity.EXTRA_USER_NAME"; private StateHolder mStateHolder; private FriendActionableListAdapter mListAdapter; private ScrollView mLayoutEmpty; private static final int MENU_REFRESH = 0; private BroadcastReceiver mLoggedOutReceiver = new BroadcastReceiver() { @Override public void onReceive(Context context, Intent intent) { if (DEBUG) Log.d(TAG, "onReceive: " + intent); finish(); } }; @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); registerReceiver(mLoggedOutReceiver, new IntentFilter(Foursquared.INTENT_ACTION_LOGGED_OUT)); Object retained = getLastNonConfigurationInstance(); if (retained != null && retained instanceof StateHolder) { mStateHolder = (StateHolder) retained; mStateHolder.setActivity(this); } else { if (getIntent().hasExtra(EXTRA_USER_NAME)) { mStateHolder = new StateHolder(getIntent().getStringExtra(EXTRA_USER_NAME)); mStateHolder.setFollowersOnly(true); } else { Log.e(TAG, TAG + " requires user name in intent extras."); finish(); return; } } ensureUi(); // Friend tips is shown first by default so auto-fetch it if necessary. if (!mStateHolder.getRanOnceFollowers()) { mStateHolder.startTask(this, true); } } @Override public void onPause() { super.onPause(); if (isFinishing()) { mStateHolder.cancelTasks(); mListAdapter.removeObserver(); } } @Override protected void onDestroy() { super.onDestroy(); unregisterReceiver(mLoggedOutReceiver); } @Override public Object onRetainNonConfigurationInstance() { mStateHolder.setActivity(null); return mStateHolder; } private void ensureUi() { LayoutInflater inflater = LayoutInflater.from(this); mLayoutEmpty = (ScrollView)inflater.inflate(R.layout.user_details_friends_activity_empty, null); mLayoutEmpty.setLayoutParams(new LinearLayout.LayoutParams( LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT)); mListAdapter = new FriendActionableListAdapter( this, mButtonRowClickHandler, ((Foursquared) getApplication()).getRemoteResourceManager()); if (mStateHolder.getFollowersOnly()) { mListAdapter.setGroup(mStateHolder.getFollowers()); if (mStateHolder.getFollowers().size() == 0) { if (mStateHolder.getRanOnceFollowers()) { setEmptyView(mLayoutEmpty); } else { setLoadingView(); } } } else { mListAdapter.setGroup(mStateHolder.getFriends()); if (mStateHolder.getFriends().size() == 0) { if (mStateHolder.getRanOnceFriends()) { setEmptyView(mLayoutEmpty); } else { setLoadingView(); } } } SegmentedButton buttons = getHeaderButton(); buttons.clearButtons(); buttons.addButtons( getString(R.string.user_details_friends_followers_activity_followers), getString(R.string.user_details_friends_followers_activity_friends)); if (mStateHolder.mFollowersOnly) { buttons.setPushedButtonIndex(0); } else { buttons.setPushedButtonIndex(1); } buttons.setOnClickListener(new OnClickListenerSegmentedButton() { @Override public void onClick(int index) { if (index == 0) { mStateHolder.setFollowersOnly(true); mListAdapter.setGroup(mStateHolder.getFollowers()); if (mStateHolder.getFollowers().size() < 1) { if (mStateHolder.getRanOnceFollowers()) { setEmptyView(mLayoutEmpty); } else { setLoadingView(); mStateHolder.startTask(UserDetailsFriendsFollowersActivity.this, true); } } } else { mStateHolder.setFollowersOnly(false); mListAdapter.setGroup(mStateHolder.getFriends()); if (mStateHolder.getFriends().size() < 1) { if (mStateHolder.getRanOnceFriends()) { setEmptyView(mLayoutEmpty); } else { setLoadingView(); mStateHolder.startTask(UserDetailsFriendsFollowersActivity.this, false); } } } mListAdapter.notifyDataSetChanged(); getListView().setSelection(0); } }); ListView listView = getListView(); listView.setAdapter(mListAdapter); listView.setSmoothScrollbarEnabled(false); listView.setItemsCanFocus(false); listView.setOnItemClickListener(new OnItemClickListener() { @Override public void onItemClick(AdapterView<?> parent, View view, int position, long id) { User user = (User) parent.getAdapter().getItem(position); Intent intent = new Intent(UserDetailsFriendsFollowersActivity.this, UserDetailsActivity.class); intent.putExtra(UserDetailsActivity.EXTRA_USER_PARCEL, user); startActivity(intent); } }); if (mStateHolder.getIsRunningTaskFollowers() || mStateHolder.getIsRunningTaskFriends()) { setProgressBarIndeterminateVisibility(true); } else { setProgressBarIndeterminateVisibility(false); } setTitle(getString(R.string.user_details_friends_followers_activity_title, mStateHolder.getUsername())); } private FriendActionableListAdapter.ButtonRowClickHandler mButtonRowClickHandler = new FriendActionableListAdapter.ButtonRowClickHandler() { @Override public void onBtnClickBtn1(User user) { if (mStateHolder.getFollowersOnly()) { updateFollowerStatus(user, true); } } @Override public void onBtnClickBtn2(User user) { if (mStateHolder.getFollowersOnly()) { updateFollowerStatus(user, false); } } }; @Override public boolean onCreateOptionsMenu(Menu menu) { super.onCreateOptionsMenu(menu); menu.add(Menu.NONE, MENU_REFRESH, Menu.NONE, R.string.refresh) .setIcon(R.drawable.ic_menu_refresh); return true; } @Override public boolean onOptionsItemSelected(MenuItem item) { switch (item.getItemId()) { case MENU_REFRESH: mStateHolder.startTask(this, mStateHolder.getFollowersOnly()); return true; } return super.onOptionsItemSelected(item); } /* * Leaving this out for now as it may be very costly for users with very large * friend networks. private void prepareResultIntent() { Group<User> followers = mStateHolder.getFollowers(); Group<User> friends = mStateHolder.getFollowers(); User[] followersArr = (User[])followers.toArray(new User[followers.size()]); User[] friendsArr = (User[])friends.toArray(new User[friends.size()]); Intent intent = new Intent(); intent.putExtra(EXTRA_FOLLOWERS_RETURNED, followersArr); intent.putExtra(EXTRA_FRIENDS_RETURNED, friendsArr); setResult(CODE, intent); } */ private void updateFollowerStatus(User user, boolean approve) { mStateHolder.startTaskUpdateFollower(this, user, approve); if (mStateHolder.getFollowersOnly()) { mListAdapter.notifyDataSetChanged(); if (mStateHolder.getFollowers().size() == 0) { setEmptyView(mLayoutEmpty); } } } private void onStartTaskUsers() { if (mListAdapter != null) { if (mStateHolder.getFollowersOnly()) { mStateHolder.setIsRunningTaskFollowers(true); mListAdapter.setGroup(mStateHolder.getFollowers()); } else { mStateHolder.setIsRunningTaskFriends(true); mListAdapter.setGroup(mStateHolder.getFriends()); } mListAdapter.notifyDataSetChanged(); } setProgressBarIndeterminateVisibility(true); setLoadingView(); } private void onStartUpdateFollower() { setProgressBarIndeterminateVisibility(true); } private void onTaskUsersComplete(Group<User> group, boolean friendsOnly, Exception ex) { SegmentedButton buttons = getHeaderButton(); boolean update = false; if (group != null) { if (friendsOnly) { mStateHolder.setFollowers(group); if (buttons.getSelectedButtonIndex() == 0) { mListAdapter.setGroup(mStateHolder.getFollowers()); update = true; } } else { mStateHolder.setFriends(group); if (buttons.getSelectedButtonIndex() == 1) { mListAdapter.setGroup(mStateHolder.getFriends()); update = true; } } } else { if (friendsOnly) { mStateHolder.setFollowers(new Group<User>()); if (buttons.getSelectedButtonIndex() == 0) { mListAdapter.setGroup(mStateHolder.getFollowers()); update = true; } } else { mStateHolder.setFriends(new Group<User>()); if (buttons.getSelectedButtonIndex() == 1) { mListAdapter.setGroup(mStateHolder.getFriends()); update = true; } } NotificationsUtil.ToastReasonForFailure(this, ex); } if (friendsOnly) { mStateHolder.setIsRunningTaskFollowers(false); mStateHolder.setRanOnceFollowers(true); if (mStateHolder.getFollowers().size() == 0 && buttons.getSelectedButtonIndex() == 0) { setEmptyView(mLayoutEmpty); } } else { mStateHolder.setIsRunningTaskFriends(false); mStateHolder.setRanOnceFriends(true); if (mStateHolder.getFriends().size() == 0 && buttons.getSelectedButtonIndex() == 1) { setEmptyView(mLayoutEmpty); } } if (update) { mListAdapter.notifyDataSetChanged(); getListView().setSelection(0); } if (!mStateHolder.areAnyTasksRunning()) { setProgressBarIndeterminateVisibility(false); } } private void onTaskUpdateFollowerComplete(TaskUpdateFollower task, User user, boolean approve, Exception ex) { if (user != null) { if (UserUtils.isFriend(user)) { if (mStateHolder.addFriend(user)) { mListAdapter.notifyDataSetChanged(); } } } mStateHolder.removeTaskUpdateFollower(task); if (!mStateHolder.areAnyTasksRunning()) { setProgressBarIndeterminateVisibility(false); } } private static class TaskUsers extends AsyncTask<Void, Void, Group<User>> { private UserDetailsFriendsFollowersActivity mActivity; private boolean mFollowersOnly; private Exception mReason; public TaskUsers(UserDetailsFriendsFollowersActivity activity, boolean followersOnly) { mActivity = activity; mFollowersOnly = followersOnly; } @Override protected void onPreExecute() { mActivity.onStartTaskUsers(); } @Override protected Group<User> doInBackground(Void... params) { try { Foursquared foursquared = (Foursquared) mActivity.getApplication(); Foursquare foursquare = foursquared.getFoursquare(); Location loc = foursquared.getLastKnownLocation(); if (mFollowersOnly) { return foursquare.friendRequests(); } else { return foursquare.friends(null, LocationUtils.createFoursquareLocation(loc)); } } catch (Exception e) { mReason = e; } return null; } @Override protected void onPostExecute(Group<User> users) { if (mActivity != null) { mActivity.onTaskUsersComplete(users, mFollowersOnly, mReason); } } @Override protected void onCancelled() { if (mActivity != null) { mActivity.onTaskUsersComplete(null, mFollowersOnly, mReason); } } public void setActivity(UserDetailsFriendsFollowersActivity activity) { mActivity = activity; } } private static class TaskUpdateFollower extends AsyncTask<Void, Void, User> { private UserDetailsFriendsFollowersActivity mActivity; private String mUserId; private boolean mApprove; private Exception mReason; private boolean mDone; public TaskUpdateFollower(UserDetailsFriendsFollowersActivity activity, String userId, boolean approve) { mActivity = activity; mUserId = userId; mApprove = approve; mDone = false; } @Override protected void onPreExecute() { mActivity.onStartUpdateFollower(); } @Override protected User doInBackground(Void... params) { try { Foursquared foursquared = (Foursquared) mActivity.getApplication(); Foursquare foursquare = foursquared.getFoursquare(); if (mApprove) { return foursquare.friendApprove(mUserId); } else { return foursquare.friendDeny(mUserId); } } catch (Exception e) { mReason = e; } return null; } @Override protected void onPostExecute(User user) { if (mActivity != null) { mActivity.onTaskUpdateFollowerComplete(this, user, mApprove, mReason); } mDone = true; } @Override protected void onCancelled() { if (mActivity != null) { mActivity.onTaskUpdateFollowerComplete(this, null, mApprove, mReason); } mDone = true; } public void setActivity(UserDetailsFriendsFollowersActivity activity) { mActivity = activity; } public boolean getIsDone() { return mDone; } } private static class StateHolder { private String mUsername; private Group<User> mFollowers; private Group<User> mFriends; private TaskUsers mTaskFollowers; private TaskUsers mTaskFriends; private boolean mIsRunningTaskFollowers; private boolean mIsRunningTaskFriends; private boolean mFollowersOnly; private boolean mRanOnceFollowers; private boolean mRanOnceFriends; private List<TaskUpdateFollower> mTasksUpdateFollowers; public StateHolder(String username) { mUsername = username; mIsRunningTaskFollowers = false; mIsRunningTaskFriends = false; mRanOnceFriends = false; mRanOnceFollowers = false; mFollowers = new Group<User>(); mFriends = new Group<User>(); mFollowersOnly = true; mTasksUpdateFollowers = new ArrayList<TaskUpdateFollower>(); } public String getUsername() { return mUsername; } public Group<User> getFollowers() { return mFollowers; } public void setFollowers(Group<User> followers) { mFollowers = followers; } public Group<User> getFriends() { return mFriends; } public void setFriends(Group<User> friends) { mFriends = friends; } public void startTask(UserDetailsFriendsFollowersActivity activity, boolean followersOnly) { if (followersOnly) { if (mIsRunningTaskFollowers) { return; } mIsRunningTaskFollowers = true; mTaskFollowers = new TaskUsers(activity, followersOnly); mTaskFollowers.execute(); } else { if (mIsRunningTaskFriends) { return; } mIsRunningTaskFriends = true; mTaskFriends = new TaskUsers(activity, followersOnly); mTaskFriends.execute(); } } public void startTaskUpdateFollower(UserDetailsFriendsFollowersActivity activity, User user, boolean approve) { for (User it : mFollowers) { if (it.getId().equals(user.getId())) { mFollowers.remove(it); break; } } TaskUpdateFollower task = new TaskUpdateFollower(activity, user.getId(), approve); task.execute(); mTasksUpdateFollowers.add(task); } public void setActivity(UserDetailsFriendsFollowersActivity activity) { if (mTaskFollowers != null) { mTaskFollowers.setActivity(activity); } if (mTaskFriends != null) { mTaskFriends.setActivity(activity); } for (TaskUpdateFollower it : mTasksUpdateFollowers) { it.setActivity(activity); } } public boolean getIsRunningTaskFollowers() { return mIsRunningTaskFollowers; } public void setIsRunningTaskFollowers(boolean isRunning) { mIsRunningTaskFollowers = isRunning; } public boolean getIsRunningTaskFriends() { return mIsRunningTaskFriends; } public void setIsRunningTaskFriends(boolean isRunning) { mIsRunningTaskFriends = isRunning; } public void cancelTasks() { if (mTaskFollowers != null) { mTaskFollowers.setActivity(null); mTaskFollowers.cancel(true); } if (mTaskFriends != null) { mTaskFriends.setActivity(null); mTaskFriends.cancel(true); } for (TaskUpdateFollower it : mTasksUpdateFollowers) { it.setActivity(null); it.cancel(true); } } public boolean getFollowersOnly() { return mFollowersOnly; } public void setFollowersOnly(boolean followersOnly) { mFollowersOnly = followersOnly; } public boolean getRanOnceFollowers() { return mRanOnceFollowers; } public void setRanOnceFollowers(boolean ranOnce) { mRanOnceFollowers = ranOnce; } public boolean getRanOnceFriends() { return mRanOnceFriends; } public void setRanOnceFriends(boolean ranOnce) { mRanOnceFriends = ranOnce; } public boolean areAnyTasksRunning() { return mIsRunningTaskFollowers || mIsRunningTaskFriends || mTasksUpdateFollowers.size() > 0; } public boolean addFriend(User user) { for (User it : mFriends) { if (it.getId().equals(user.getId())) { return false; } } mFriends.add(user); return true; } public void removeTaskUpdateFollower(TaskUpdateFollower task) { mTasksUpdateFollowers.remove(task); // Try to cleanup anyone we missed, this could happen for a brief period // during rotation. for (int i = mTasksUpdateFollowers.size()-1; i > -1; i--) { if (mTasksUpdateFollowers.get(i).getIsDone()) { mTasksUpdateFollowers.remove(i); } } } } }