/**
* 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.util.NotificationsUtil;
import com.joelapenna.foursquared.widget.FriendRequestsAdapter;
import android.app.ListActivity;
import android.app.ProgressDialog;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.AsyncTask;
import android.os.Bundle;
import android.os.Handler;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.widget.EditText;
import android.widget.TextView;
import android.widget.Toast;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* Presents the user with a list of pending friend requests that they can
* approve or deny.
*
* @date February 12, 2010
* @author Mark Wyszomierski (markww@gmail.com), foursquare.
*/
public class FriendRequestsActivity extends ListActivity {
private static final String TAG = "FriendRequestsActivity";
private static final boolean DEBUG = FoursquaredSettings.DEBUG;
private static final int MENU_REFRESH = 0;
private StateHolder mStateHolder;
private ProgressDialog mDlgProgress;
private EditText mEditTextFilter;
private FriendRequestsAdapter mListAdapter;
private TextView mTextViewNoRequests;
private Handler mHandler;
private BroadcastReceiver mLoggedOutReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
if (DEBUG) Log.d(TAG, "onReceive: " + intent);
finish();
}
};
@Override
public void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (DEBUG) Log.d(TAG, "onCreate()");
setContentView(R.layout.friend_requests_activity);
registerReceiver(mLoggedOutReceiver, new IntentFilter(Foursquared.INTENT_ACTION_LOGGED_OUT));
mHandler = new Handler();
mEditTextFilter = (EditText)findViewById(R.id.editTextFilter);
mEditTextFilter.addTextChangedListener(new TextWatcher() {
public void afterTextChanged(Editable s) {
}
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
}
public void onTextChanged(CharSequence s, int start, int before, int count) {
// Run the filter operation after a brief waiting period in case the user
// is typing real fast.
mHandler.removeCallbacks(mRunnableFilter);
mHandler.postDelayed(mRunnableFilter, 700L);
}
});
mListAdapter = new FriendRequestsAdapter(this, mButtonRowClickHandler,
((Foursquared) getApplication()).getRemoteResourceManager());
getListView().setAdapter(mListAdapter);
getListView().setItemsCanFocus(true);
mTextViewNoRequests = (TextView)findViewById(R.id.textViewNoRequests);
Object retained = getLastNonConfigurationInstance();
if (retained != null && retained instanceof StateHolder) {
mStateHolder = (StateHolder) retained;
mStateHolder.setActivityForTaskFriendRequests(this);
mStateHolder.setActivityForTaskSendDecision(this);
decideShowNoFriendRequestsTextView();
} else {
mStateHolder = new StateHolder();
// Start searching for friend requests immediately on activity creation.
startProgressBar(getResources().getString(R.string.friend_requests_activity_label),
getResources().getString(R.string.friend_requests_progress_bar_find_requests));
mStateHolder.startTaskFriendRequests(FriendRequestsActivity.this);
}
}
@Override
public void onResume() {
super.onResume();
if (mStateHolder.getIsRunningFriendRequest()) {
startProgressBar(getResources().getString(R.string.friend_requests_activity_label),
getResources().getString(R.string.friend_requests_progress_bar_find_requests));
} else if (mStateHolder.getIsRunningApproval()) {
startProgressBar(getResources().getString(R.string.friend_requests_activity_label),
getResources()
.getString(R.string.friend_requests_progress_bar_approve_request));
} else if (mStateHolder.getIsRunningIgnore()) {
startProgressBar(getResources().getString(R.string.friend_requests_activity_label),
getResources()
.getString(R.string.friend_requests_progress_bar_ignore_request));
}
mListAdapter.setGroup(mStateHolder.getFoundFriendsFiltered());
mListAdapter.notifyDataSetChanged();
}
@Override
public void onPause() {
super.onPause();
stopProgressBar();
if (isFinishing()) {
mListAdapter.removeObserver();
}
}
@Override
public void onDestroy() {
super.onDestroy();
unregisterReceiver(mLoggedOutReceiver);
}
@Override
public Object onRetainNonConfigurationInstance() {
mStateHolder.setActivityForTaskFriendRequests(null);
mStateHolder.setActivityForTaskSendDecision(null);
return mStateHolder;
}
@Override
public boolean onCreateOptionsMenu(Menu menu) {
super.onCreateOptionsMenu(menu);
menu.add(Menu.NONE, MENU_REFRESH, Menu.NONE, R.string.refresh_label).setIcon(
R.drawable.ic_menu_refresh);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_REFRESH:
startProgressBar(getResources().getString(R.string.friend_requests_activity_label),
getResources().getString(R.string.friend_requests_progress_bar_find_requests));
mStateHolder.setRanFetchOnce(false);
mStateHolder.startTaskFriendRequests(FriendRequestsActivity.this);
decideShowNoFriendRequestsTextView();
return true;
}
return super.onOptionsItemSelected(item);
}
private void startProgressBar(String title, String message) {
if (mDlgProgress == null) {
mDlgProgress = ProgressDialog.show(this, title, message);
}
mDlgProgress.show();
}
private void stopProgressBar() {
if (mDlgProgress != null) {
mDlgProgress.dismiss();
mDlgProgress = null;
}
}
private void infoFriendRequest(User user) {
Intent intent = new Intent(this, UserDetailsActivity.class);
intent.putExtra(UserDetailsActivity.EXTRA_USER_PARCEL, user);
startActivity(intent);
}
private void approveFriendRequest(User user) {
startProgressBar(getResources().getString(R.string.friend_requests_activity_label),
getResources().getString(R.string.friend_requests_progress_bar_approve_request));
mStateHolder.startTaskSendDecision(FriendRequestsActivity.this, user.getId(), true);
}
private void denyFriendRequest(User user) {
startProgressBar(getResources().getString(R.string.friend_requests_activity_label),
getResources().getString(R.string.friend_requests_progress_bar_ignore_request));
mStateHolder.startTaskSendDecision(FriendRequestsActivity.this, user.getId(), false);
}
private void decideShowNoFriendRequestsTextView() {
if (mStateHolder.getRanFetchOnce() &&
mStateHolder.getFoundFriendsCount() < 1) {
mTextViewNoRequests.setVisibility(View.VISIBLE);
}
else {
mTextViewNoRequests.setVisibility(View.GONE);
}
}
private void onFriendRequestsTaskComplete(Group<User> users, HashMap<String, Group<User>> usersAlpha, Exception ex) {
// Recreate the adapter, cleanup beforehand.
mListAdapter.removeObserver();
mListAdapter = new FriendRequestsAdapter(this, mButtonRowClickHandler,
((Foursquared) getApplication()).getRemoteResourceManager());
try {
// Populate the list control below now.
if (users != null) {
mStateHolder.setFoundFriends(users, usersAlpha);
if (DEBUG) {
Log.e(TAG, "Alpha-sorted requests map:");
for (Map.Entry<String, Group<User>> it : usersAlpha.entrySet()) {
Log.e(TAG, it.getKey());
for (User jt : it.getValue()) {
Log.e(TAG, " " + getUsersDisplayName(jt));
}
}
}
} else {
// If error, feed list adapter empty user group.
mStateHolder.setFoundFriends(null, null);
NotificationsUtil.ToastReasonForFailure(FriendRequestsActivity.this, ex);
}
mListAdapter.setGroup(mStateHolder.getFoundFriendsFiltered());
} finally {
getListView().setAdapter(mListAdapter);
mStateHolder.setIsRunningFriendRequest(false);
mStateHolder.setRanFetchOnce(true);
decideShowNoFriendRequestsTextView();
stopProgressBar();
}
}
private void onFriendRequestDecisionTaskComplete(User user, boolean isApproving, Exception ex) {
try {
// If sending the request was successful, then we need to remove
// that user from the list adapter. We do a linear search to find the
// matching row.
if (user != null) {
mStateHolder.removeUser(user);
mListAdapter.setGroup(mStateHolder.getFoundFriendsFiltered());
mListAdapter.notifyDataSetChanged();
// This should generate the message: "You're now friends with [name]!" if
// the user chose to approve the request, otherwise we show no toast, just
// remove from the list.
if (isApproving) {
Toast.makeText(this,
getResources().getString(R.string.friend_requests_approved) + " " +
getUsersDisplayName(user) + "!",
Toast.LENGTH_SHORT).show();
}
} else {
NotificationsUtil.ToastReasonForFailure(this, ex);
}
} finally {
decideShowNoFriendRequestsTextView();
mStateHolder.setIsRunningApprval(false);
mStateHolder.setIsRunningIgnore(false);
stopProgressBar();
}
}
private static class GetFriendRequestsTask extends AsyncTask<Void, Void, Group<User>> {
private FriendRequestsActivity mActivity;
private Exception mReason;
private HashMap<String, Group<User>> mRequestsAlpha;
public GetFriendRequestsTask(FriendRequestsActivity activity) {
mActivity = activity;
mRequestsAlpha = new LinkedHashMap<String, Group<User>>();
}
public void setActivity(FriendRequestsActivity activity) {
mActivity = activity;
}
@Override
protected Group<User> doInBackground(Void... params) {
try {
Foursquared foursquared = (Foursquared) mActivity.getApplication();
Foursquare foursquare = foursquared.getFoursquare();
Group<User> requests = foursquare.friendRequests();
for (User it : requests) {
String name = getUsersDisplayName(it).toUpperCase();
String first = name.substring(0, 1);
Group<User> block = mRequestsAlpha.get(first);
if (block == null) {
block = new Group<User>();
mRequestsAlpha.put(first, block);
}
block.add(it);
}
return requests;
} catch (Exception e) {
if (DEBUG) Log.d(TAG, "FindFriendsTask: Exception doing add friends by name", e);
mReason = e;
}
return null;
}
@Override
protected void onPostExecute(Group<User> users) {
if (DEBUG) Log.d(TAG, "FindFriendsTask: onPostExecute()");
if (mActivity != null) {
mActivity.onFriendRequestsTaskComplete(users, mRequestsAlpha, mReason);
}
}
@Override
protected void onCancelled() {
if (mActivity != null) {
mActivity.onFriendRequestsTaskComplete(null, null, new Exception(
"Friend search cancelled."));
}
}
}
private static class SendFriendRequestDecisionTask extends AsyncTask<Void, Void, User> {
private FriendRequestsActivity mActivity;
private boolean mIsApproving;
private String mUserId;
private Exception mReason;
public SendFriendRequestDecisionTask(FriendRequestsActivity activity,
String userId,
boolean isApproving) {
mActivity = activity;
mUserId = userId;
mIsApproving = isApproving;
}
public void setActivity(FriendRequestsActivity activity) {
mActivity = activity;
}
@Override
protected User doInBackground(Void... params) {
try {
Foursquared foursquared = (Foursquared) mActivity.getApplication();
Foursquare foursquare = foursquared.getFoursquare();
User user = null;
if (mIsApproving) {
user = foursquare.friendApprove(mUserId);
} else {
user = foursquare.friendDeny(mUserId);
}
return user;
} catch (Exception e) {
if (DEBUG)
Log.d(TAG, "SendFriendRequestTask: Exception doing send friend request.", e);
mReason = e;
}
return null;
}
@Override
protected void onPostExecute(User user) {
if (DEBUG) Log.d(TAG, "SendFriendRequestTask: onPostExecute()");
if (mActivity != null) {
mActivity.onFriendRequestDecisionTaskComplete(user, mIsApproving, mReason);
}
}
@Override
protected void onCancelled() {
if (mActivity != null) {
mActivity.onFriendRequestDecisionTaskComplete(null, mIsApproving,
new Exception("Friend request cancelled."));
}
}
}
private static class StateHolder {
GetFriendRequestsTask mTaskFriendRequests;
SendFriendRequestDecisionTask mTaskSendDecision;
boolean mIsRunningFriendRequests;
boolean mIsRunningApproval;
boolean mIsRunningIgnore;
boolean mRanFetchOnce;
private Group<User> mFoundFriends;
private Group<User> mFoundFriendsFiltered;
private HashMap<String, Group<User>> mFoundFriendsAlpha;
public StateHolder() {
mFoundFriends = new Group<User>();
mFoundFriendsFiltered = null;
mFoundFriendsAlpha = null;
mIsRunningFriendRequests = false;
mIsRunningApproval = false;
mIsRunningIgnore = false;
mRanFetchOnce = false;
}
public void startTaskFriendRequests(FriendRequestsActivity activity) {
mIsRunningFriendRequests = true;
mTaskFriendRequests = new GetFriendRequestsTask(activity);
mTaskFriendRequests.execute();
}
public void startTaskSendDecision(FriendRequestsActivity activity, String userId,
boolean approve) {
mIsRunningApproval = approve;
mIsRunningIgnore = !approve;
mTaskSendDecision = new SendFriendRequestDecisionTask(activity, userId, approve);
mTaskSendDecision.execute();
}
public void setActivityForTaskFriendRequests(FriendRequestsActivity activity) {
if (mTaskFriendRequests != null) {
mTaskFriendRequests.setActivity(activity);
}
}
public void setActivityForTaskSendDecision(FriendRequestsActivity activity) {
if (mTaskSendDecision != null) {
mTaskSendDecision.setActivity(activity);
}
}
public void setIsRunningFriendRequest(boolean isRunning) {
mIsRunningFriendRequests = isRunning;
}
public boolean getIsRunningFriendRequest() {
return mIsRunningFriendRequests;
}
public boolean getIsRunningApproval() {
return mIsRunningApproval;
}
public void setIsRunningApprval(boolean isRunning) {
mIsRunningApproval = isRunning;
}
public boolean getIsRunningIgnore() {
return mIsRunningIgnore;
}
public void setIsRunningIgnore(boolean isRunning) {
mIsRunningIgnore = isRunning;
}
public boolean getRanFetchOnce() {
return mRanFetchOnce;
}
public void setRanFetchOnce(boolean ranFetchOnce) {
mRanFetchOnce = ranFetchOnce;
}
public int getFoundFriendsCount() {
return mFoundFriends.size();
}
public Group<User> getFoundFriendsFiltered() {
if (mFoundFriendsFiltered == null) {
return mFoundFriends;
}
return mFoundFriendsFiltered;
}
public void setFoundFriends(Group<User> requests, HashMap<String, Group<User>> alpha) {
if (requests != null) {
mFoundFriends = requests;
mFoundFriendsFiltered = null;
mFoundFriendsAlpha = alpha;
} else {
mFoundFriends = new Group<User>();
mFoundFriendsFiltered = null;
mFoundFriendsAlpha = null;
}
}
public void filterFriendRequests(String filterString) {
// If no filter, just keep using the original found friends group.
// If a filter is supplied, reconstruct the group using the alpha
// map so we don't have to go through the entire list.
mFoundFriendsFiltered = null;
if (!TextUtils.isEmpty(filterString)) {
filterString = filterString.toUpperCase();
Group<User> alpha = mFoundFriendsAlpha.get(filterString.substring(0, 1));
mFoundFriendsFiltered = new Group<User>();
if (alpha != null) {
for (User it : alpha) {
String name = getUsersDisplayName(it).toUpperCase();
if (name.startsWith(filterString)) {
mFoundFriendsFiltered.add(it);
}
}
}
}
}
public void removeUser(User user) {
for (User it : mFoundFriends) {
if (it.getId().equals(user.getId())) {
mFoundFriends.remove(it);
break;
}
}
if (mFoundFriendsFiltered != null) {
for (User it : mFoundFriendsFiltered) {
if (it.getId().equals(user.getId())) {
mFoundFriendsFiltered.remove(it);
break;
}
}
}
String name = getUsersDisplayName(user).toUpperCase();
String first = name.substring(0, 1);
Group<User> alpha = mFoundFriendsAlpha.get(first);
for (User it : alpha) {
if (it.getId().equals(user.getId())) {
alpha.remove(it);
break;
}
}
}
}
private static String getUsersDisplayName(User user) {
StringBuilder sb = new StringBuilder(64);
if (!TextUtils.isEmpty(user.getFirstname())) {
sb.append(user.getFirstname());
sb.append(" ");
}
if (!TextUtils.isEmpty(user.getLastname())) {
sb.append(user.getLastname());
}
return sb.toString();
}
private FriendRequestsAdapter.ButtonRowClickHandler mButtonRowClickHandler =
new FriendRequestsAdapter.ButtonRowClickHandler() {
@Override
public void onBtnClickIgnore(User user) {
denyFriendRequest(user);
}
@Override
public void onBtnClickAdd(User user) {
approveFriendRequest(user);
}
@Override
public void onInfoAreaClick(User user) {
infoFriendRequest(user);
}
};
private Runnable mRunnableFilter = new Runnable() {
public void run() {
mStateHolder.filterFriendRequests(mEditTextFilter.getText().toString());
mListAdapter.setGroup(mStateHolder.getFoundFriendsFiltered());
mListAdapter.notifyDataSetChanged();
}
};
}