/**
* Copyright 2010 Mark Wyszomierski
*/
package com.joelapenna.foursquared;
import com.joelapenna.foursquare.Foursquare;
import com.joelapenna.foursquare.error.FoursquareException;
import com.joelapenna.foursquare.types.Group;
import com.joelapenna.foursquare.types.Tip;
import com.joelapenna.foursquared.app.LoadableListActivityWithViewAndHeader;
import com.joelapenna.foursquared.location.LocationUtils;
import com.joelapenna.foursquared.util.MenuUtils;
import com.joelapenna.foursquared.util.NotificationsUtil;
import com.joelapenna.foursquared.widget.SegmentedButton;
import com.joelapenna.foursquared.widget.SegmentedButton.OnClickListenerSegmentedButton;
import com.joelapenna.foursquared.widget.TipsListAdapter;
import android.app.Activity;
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 java.util.Observable;
import java.util.Observer;
/**
* Shows a list of nearby tips. User can sort tips by friends-only.
*
* @date August 31, 2010
* @author Mark Wyszomierski (markww@gmail.com)
*/
public class TipsActivity extends LoadableListActivityWithViewAndHeader {
static final String TAG = "TipsActivity";
static final boolean DEBUG = FoursquaredSettings.DEBUG;
private static final int ACTIVITY_TIP = 500;
private StateHolder mStateHolder;
private TipsListAdapter mListAdapter;
private SearchLocationObserver mSearchLocationObserver = new SearchLocationObserver();
private View 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 {
mStateHolder = new StateHolder();
mStateHolder.setFriendsOnly(true);
}
ensureUi();
// Friend tips is shown first by default so auto-fetch it if necessary.
if (!mStateHolder.getRanOnceTipsFriends()) {
mStateHolder.startTaskTips(this, true);
}
}
@Override
public void onResume() {
super.onResume();
((Foursquared) getApplication()).requestLocationUpdates(mSearchLocationObserver);
}
@Override
public void onPause() {
super.onPause();
((Foursquared) getApplication()).removeLocationUpdates(mSearchLocationObserver);
if (isFinishing()) {
mStateHolder.cancelTasks();
mListAdapter.removeObserver();
unregisterReceiver(mLoggedOutReceiver);
}
}
@Override
public Object onRetainNonConfigurationInstance() {
mStateHolder.setActivity(null);
return mStateHolder;
}
private void ensureUi() {
LayoutInflater inflater = LayoutInflater.from(this);
mLayoutEmpty = inflater.inflate(R.layout.tips_activity_empty, null);
mLayoutEmpty.setLayoutParams(new LinearLayout.LayoutParams(
LinearLayout.LayoutParams.FILL_PARENT, LinearLayout.LayoutParams.FILL_PARENT));
mListAdapter = new TipsListAdapter(this,
((Foursquared) getApplication()).getRemoteResourceManager(), R.layout.tip_list_item);
if (mStateHolder.getFriendsOnly()) {
mListAdapter.setGroup(mStateHolder.getTipsFriends());
if (mStateHolder.getTipsFriends().size() == 0) {
if (mStateHolder.getRanOnceTipsFriends()) {
setEmptyView(mLayoutEmpty);
} else {
setLoadingView();
}
}
} else {
mListAdapter.setGroup(mStateHolder.getTipsEveryone());
if (mStateHolder.getTipsEveryone().size() == 0) {
if (mStateHolder.getRanOnceTipsEveryone()) {
setEmptyView(mLayoutEmpty);
} else {
setLoadingView();
}
}
}
SegmentedButton buttons = getHeaderButton();
buttons.clearButtons();
buttons.addButtons(
getString(R.string.tips_activity_btn_friends_only),
getString(R.string.tips_activity_btn_everyone));
if (mStateHolder.mFriendsOnly) {
buttons.setPushedButtonIndex(0);
} else {
buttons.setPushedButtonIndex(1);
}
buttons.setOnClickListener(new OnClickListenerSegmentedButton() {
@Override
public void onClick(int index) {
if (index == 0) {
mStateHolder.setFriendsOnly(true);
mListAdapter.setGroup(mStateHolder.getTipsFriends());
if (mStateHolder.getTipsFriends().size() < 1) {
if (mStateHolder.getRanOnceTipsFriends()) {
setEmptyView(mLayoutEmpty);
} else {
setLoadingView();
mStateHolder.startTaskTips(TipsActivity.this, true);
}
}
} else {
mStateHolder.setFriendsOnly(false);
mListAdapter.setGroup(mStateHolder.getTipsEveryone());
if (mStateHolder.getTipsEveryone().size() < 1) {
if (mStateHolder.getRanOnceTipsEveryone()) {
setEmptyView(mLayoutEmpty);
} else {
setLoadingView();
mStateHolder.startTaskTips(TipsActivity.this, false);
}
}
}
mListAdapter.notifyDataSetChanged();
getListView().setSelection(0);
}
});
ListView listView = getListView();
listView.setAdapter(mListAdapter);
listView.setSmoothScrollbarEnabled(false);
listView.setOnItemClickListener(new OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
Tip tip = (Tip) parent.getAdapter().getItem(position);
Intent intent = new Intent(TipsActivity.this, TipActivity.class);
intent.putExtra(TipActivity.EXTRA_TIP_PARCEL, tip);
startActivityForResult(intent, ACTIVITY_TIP);
}
});
if (mStateHolder.getIsRunningTaskTipsFriends() ||
mStateHolder.getIsRunningTaskTipsEveryone()) {
setProgressBarIndeterminateVisibility(true);
} else {
setProgressBarIndeterminateVisibility(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);
MenuUtils.addPreferencesToMenu(this, menu);
return true;
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
switch (item.getItemId()) {
case MENU_REFRESH:
mStateHolder.startTaskTips(this, mStateHolder.getFriendsOnly());
return true;
}
return super.onOptionsItemSelected(item);
}
@Override
protected void onActivityResult(int requestCode, int resultCode, Intent data) {
// We don't care about the returned to-do (if any) since we're not bound
// to a venue in this activity for update. We just update the status member
// of the target tip.
if (requestCode == ACTIVITY_TIP && resultCode == Activity.RESULT_OK) {
if (data.hasExtra(TipActivity.EXTRA_TIP_RETURNED)) {
Log.d(TAG, "onActivityResult(), return tip intent extra found, processing.");
updateTip((Tip)data.getParcelableExtra(TipActivity.EXTRA_TIP_RETURNED));
} else {
Log.d(TAG, "onActivityResult(), no return tip intent extra found.");
}
}
}
private void updateTip(Tip tip) {
mStateHolder.updateTip(tip);
mListAdapter.notifyDataSetInvalidated();
}
private void onStartTaskTips() {
if (mListAdapter != null) {
if (mStateHolder.getFriendsOnly()) {
mStateHolder.setIsRunningTaskTipsFriends(true);
mListAdapter.setGroup(mStateHolder.getTipsFriends());
} else {
mStateHolder.setIsRunningTaskTipsEveryone(true);
mListAdapter.setGroup(mStateHolder.getTipsEveryone());
}
mListAdapter.notifyDataSetChanged();
}
setProgressBarIndeterminateVisibility(true);
setLoadingView();
}
private void onTaskTipsComplete(Group<Tip> group, boolean friendsOnly, Exception ex) {
SegmentedButton buttons = getHeaderButton();
boolean update = false;
if (group != null) {
if (friendsOnly) {
mStateHolder.setTipsFriends(group);
if (buttons.getSelectedButtonIndex() == 0) {
mListAdapter.setGroup(mStateHolder.getTipsFriends());
update = true;
}
} else {
mStateHolder.setTipsEveryone(group);
if (buttons.getSelectedButtonIndex() == 1) {
mListAdapter.setGroup(mStateHolder.getTipsEveryone());
update = true;
}
}
}
else {
if (friendsOnly) {
mStateHolder.setTipsFriends(new Group<Tip>());
if (buttons.getSelectedButtonIndex() == 0) {
mListAdapter.setGroup(mStateHolder.getTipsFriends());
update = true;
}
} else {
mStateHolder.setTipsEveryone(new Group<Tip>());
if (buttons.getSelectedButtonIndex() == 1) {
mListAdapter.setGroup(mStateHolder.getTipsEveryone());
update = true;
}
}
NotificationsUtil.ToastReasonForFailure(this, ex);
}
if (friendsOnly) {
mStateHolder.setIsRunningTaskTipsFriends(false);
mStateHolder.setRanOnceTipsFriends(true);
if (mStateHolder.getTipsFriends().size() == 0 &&
buttons.getSelectedButtonIndex() == 0) {
setEmptyView(mLayoutEmpty);
}
} else {
mStateHolder.setIsRunningTaskTipsEveryone(false);
mStateHolder.setRanOnceTipsEveryone(true);
if (mStateHolder.getTipsEveryone().size() == 0 &&
buttons.getSelectedButtonIndex() == 1) {
setEmptyView(mLayoutEmpty);
}
}
if (update) {
mListAdapter.notifyDataSetChanged();
getListView().setSelection(0);
}
if (!mStateHolder.getIsRunningTaskTipsFriends() &&
!mStateHolder.getIsRunningTaskTipsEveryone()) {
setProgressBarIndeterminateVisibility(false);
}
}
/**
* Gets friends of the current user we're working for.
*/
private static class TaskTips extends AsyncTask<Void, Void, Group<Tip>> {
private TipsActivity mActivity;
private boolean mFriendsOnly;
private Exception mReason;
public TaskTips(TipsActivity activity, boolean friendsOnly) {
mActivity = activity;
mFriendsOnly = friendsOnly;
}
@Override
protected void onPreExecute() {
mActivity.onStartTaskTips();
}
@Override
protected Group<Tip> doInBackground(Void... params) {
try {
Foursquared foursquared = (Foursquared) mActivity.getApplication();
Foursquare foursquare = foursquared.getFoursquare();
Location loc = foursquared.getLastKnownLocation();
if (loc == null) {
try { Thread.sleep(3000); } catch (InterruptedException ex) {}
loc = foursquared.getLastKnownLocation();
if (loc == null) {
throw new FoursquareException("Your location could not be determined!");
}
}
return foursquare.tips(
LocationUtils.createFoursquareLocation(loc),
null,
mFriendsOnly ? "friends" : "nearby",
null,
30);
} catch (Exception e) {
mReason = e;
}
return null;
}
@Override
protected void onPostExecute(Group<Tip> tips) {
if (mActivity != null) {
mActivity.onTaskTipsComplete(tips, mFriendsOnly, mReason);
}
}
@Override
protected void onCancelled() {
if (mActivity != null) {
mActivity.onTaskTipsComplete(null, mFriendsOnly, mReason);
}
}
public void setActivity(TipsActivity activity) {
mActivity = activity;
}
}
private static class StateHolder {
/** Tips by friends. */
private Group<Tip> mTipsFriends;
/** Tips by everyone. */
private Group<Tip> mTipsEveryone;
private TaskTips mTaskTipsFriends;
private TaskTips mTaskTipsEveryone;
private boolean mIsRunningTaskTipsFriends;
private boolean mIsRunningTaskTipsEveryone;
private boolean mFriendsOnly;
private boolean mRanOnceTipsFriends;
private boolean mRanOnceTipsEveryone;
public StateHolder() {
mIsRunningTaskTipsFriends = false;
mIsRunningTaskTipsEveryone = false;
mRanOnceTipsFriends = false;
mRanOnceTipsEveryone = false;
mTipsFriends = new Group<Tip>();
mTipsEveryone = new Group<Tip>();
mFriendsOnly = true;
}
public Group<Tip> getTipsFriends() {
return mTipsFriends;
}
public void setTipsFriends(Group<Tip> tipsFriends) {
mTipsFriends = tipsFriends;
}
public Group<Tip> getTipsEveryone() {
return mTipsEveryone;
}
public void setTipsEveryone(Group<Tip> tipsEveryone) {
mTipsEveryone = tipsEveryone;
}
public void startTaskTips(TipsActivity activity,
boolean friendsOnly) {
if (friendsOnly) {
if (mIsRunningTaskTipsFriends) {
return;
}
mIsRunningTaskTipsFriends = true;
mTaskTipsFriends = new TaskTips(activity, friendsOnly);
mTaskTipsFriends.execute();
} else {
if (mIsRunningTaskTipsEveryone) {
return;
}
mIsRunningTaskTipsEveryone = true;
mTaskTipsEveryone = new TaskTips(activity, friendsOnly);
mTaskTipsEveryone.execute();
}
}
public void setActivity(TipsActivity activity) {
if (mTaskTipsFriends != null) {
mTaskTipsFriends.setActivity(activity);
}
if (mTaskTipsEveryone != null) {
mTaskTipsEveryone.setActivity(activity);
}
}
public boolean getIsRunningTaskTipsFriends() {
return mIsRunningTaskTipsFriends;
}
public void setIsRunningTaskTipsFriends(boolean isRunning) {
mIsRunningTaskTipsFriends = isRunning;
}
public boolean getIsRunningTaskTipsEveryone() {
return mIsRunningTaskTipsEveryone;
}
public void setIsRunningTaskTipsEveryone(boolean isRunning) {
mIsRunningTaskTipsEveryone = isRunning;
}
public void cancelTasks() {
if (mTaskTipsFriends != null) {
mTaskTipsFriends.setActivity(null);
mTaskTipsFriends.cancel(true);
}
if (mTaskTipsEveryone != null) {
mTaskTipsEveryone.setActivity(null);
mTaskTipsEveryone.cancel(true);
}
}
public boolean getFriendsOnly() {
return mFriendsOnly;
}
public void setFriendsOnly(boolean friendsOnly) {
mFriendsOnly = friendsOnly;
}
public boolean getRanOnceTipsFriends() {
return mRanOnceTipsFriends;
}
public void setRanOnceTipsFriends(boolean ranOnce) {
mRanOnceTipsFriends = ranOnce;
}
public boolean getRanOnceTipsEveryone() {
return mRanOnceTipsEveryone;
}
public void setRanOnceTipsEveryone(boolean ranOnce) {
mRanOnceTipsEveryone = ranOnce;
}
public void updateTip(Tip tip) {
updateTipFromArray(tip, mTipsFriends);
updateTipFromArray(tip, mTipsEveryone);
}
private void updateTipFromArray(Tip tip, Group<Tip> target) {
for (Tip it : target) {
if (it.getId().equals(tip.getId())) {
it.setStatus(tip.getStatus());
break;
}
}
}
}
/**
* This is really just a dummy observer to get the GPS running
* since this is the new splash page. After getting a fix, we
* might want to stop registering this observer thereafter so
* it doesn't annoy the user too much.
*/
private class SearchLocationObserver implements Observer {
@Override
public void update(Observable observable, Object data) {
}
}
}