package com.vaguehope.onosendai.ui;
import java.lang.ref.WeakReference;
import java.util.ArrayList;
import java.util.List;
import android.app.ActionBar;
import android.app.Activity;
import android.content.ClipData;
import android.content.ClipboardManager;
import android.content.Context;
import android.content.Intent;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnClickListener;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemClickListener;
import android.widget.Button;
import android.widget.ListView;
import com.vaguehope.onosendai.R;
import com.vaguehope.onosendai.config.Config;
import com.vaguehope.onosendai.config.Prefs;
import com.vaguehope.onosendai.model.OutboxAdapter;
import com.vaguehope.onosendai.model.OutboxTweet;
import com.vaguehope.onosendai.model.OutboxTweet.OutboxAction;
import com.vaguehope.onosendai.model.OutboxTweet.OutboxTweetStatus;
import com.vaguehope.onosendai.provider.SendOutboxService;
import com.vaguehope.onosendai.storage.DbClient;
import com.vaguehope.onosendai.storage.DbInterface;
import com.vaguehope.onosendai.storage.DbInterface.OutboxListener;
import com.vaguehope.onosendai.util.CollectionHelper;
import com.vaguehope.onosendai.util.CollectionHelper.Function;
import com.vaguehope.onosendai.util.DialogHelper;
import com.vaguehope.onosendai.util.DialogHelper.Listener;
import com.vaguehope.onosendai.util.LogWrapper;
import com.vaguehope.onosendai.util.StringHelper;
import com.vaguehope.onosendai.util.Titleable;
public class OutboxActivity extends Activity {
private static final LogWrapper LOG = new LogWrapper("OUTBOX");
private DbClient bndDb;
private RefreshUiHandler refreshUiHandler;
private OutboxAdapter adaptor;
private boolean showSent = false;
@Override
protected void onCreate (final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.outbox);
Config conf;
try {
final Prefs prefs = new Prefs(getBaseContext());
conf = prefs.asConfig();
}
catch (final Exception e) { // No point continuing if any exception.
DialogHelper.alertAndClose(this, e);
return;
}
final ActionBar ab = getActionBar();
ab.setDisplayHomeAsUpEnabled(true);
this.refreshUiHandler = new RefreshUiHandler(this);
final ListView outboxList = (ListView) findViewById(R.id.outboxList);
outboxList.setEmptyView(findViewById(R.id.empty));
this.adaptor = new OutboxAdapter(this, conf);
outboxList.setAdapter(this.adaptor);
outboxList.setOnItemClickListener(this.listClickListener);
((Button) findViewById(R.id.resetPermanentFailures)).setOnClickListener(new OnClickListener() {
@Override
public void onClick (final View v) {
resetPermanentFailures();
}
});
((Button) findViewById(R.id.sendPending)).setOnClickListener(new OnClickListener() {
@Override
public void onClick (final View v) {
requestSendPendingNow();
}
});
}
@Override
public void onDestroy () {
if (this.bndDb != null) this.bndDb.dispose();
super.onDestroy();
}
@Override
public void onResume () {
super.onResume();
resumeDb();
}
@Override
public void onPause () {
suspendDb();
super.onPause();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
protected LogWrapper getLog () {
return LOG;
}
protected OutboxAdapter getAdaptor () {
return this.adaptor;
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
private void resumeDb () {
if (this.bndDb == null) {
LOG.d("Binding DB service...");
this.bndDb = new DbClient(this, LOG.getPrefix(), new Runnable() {
@Override
public void run () {
/*
* this convoluted method is because the service connection
* won't finish until this thread processes messages again
* (i.e., after it exits this thread). if we try to talk to
* the DB service before then, it will NPE.
*/
getDb().addOutboxListener(getOutboxListener());
refreshUi();
getLog().d("DB service bound.");
}
});
}
else { // because we stop listening in onPause(), we must resume if the user comes back.
this.bndDb.getDb().addOutboxListener(getOutboxListener());
refreshUi();
LOG.d("DB service rebound.");
}
}
private void suspendDb () {
// We might be pausing before the callback has come.
if (this.bndDb.getDb() != null) {
this.bndDb.getDb().removeOutboxListener(getOutboxListener());
}
else {
// If we have not even had the callback yet, cancel it.
this.bndDb.clearReadyListener();
}
LOG.d("DB service released.");
}
protected DbInterface getDb () {
final DbClient d = this.bndDb;
if (d == null) return null;
return d.getDb();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@Override
public boolean onCreateOptionsMenu (final Menu menu) {
getMenuInflater().inflate(R.menu.outboxmenu, menu);
return super.onCreateOptionsMenu(menu);
}
@Override
public boolean onOptionsItemSelected (final MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
case R.id.mnuShowSent:
item.setChecked(!item.isChecked());
item.setTitle(item.isChecked() ? R.string.outbox_menu_hide_sent : R.string.outbox_menu_show_sent);
this.showSent = item.isChecked();
refreshUi();
return true;
default:
return super.onOptionsItemSelected(item);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
protected OutboxListener getOutboxListener () {
return this.outboxListener;
}
private final OutboxListener outboxListener = new OutboxListener() {
@Override
public void outboxChanged () {
refreshUi();
}
};
protected void refreshUi () {
this.refreshUiHandler.sendEmptyMessage(0);
}
private static class RefreshUiHandler extends Handler {
private final WeakReference<OutboxActivity> parentRef;
public RefreshUiHandler (final OutboxActivity parent) {
this.parentRef = new WeakReference<OutboxActivity>(parent);
}
@Override
public void handleMessage (final Message msg) {
final OutboxActivity parent = this.parentRef.get();
if (parent != null) parent.msgOnUiThread(msg);
}
}
protected void msgOnUiThread (final Message msg) {
refreshListOnUiThread();
}
private void refreshListOnUiThread () {
final DbInterface db = getDb();
if (db != null) {
final List<OutboxTweet> entries = this.showSent
? db.getAllOutboxEntries()
: db.getUnsentOutboxEntries();
this.adaptor.setInputData(entries);
}
else {
LOG.w("Failed to refresh outbox as DB was not bound.");
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
private final OnItemClickListener listClickListener = new OnItemClickListener() {
@Override
public void onItemClick (final AdapterView<?> parent, final View view, final int position, final long id) {
itemClicked(getAdaptor().getInputData().get(position));
}
};
private enum OutboxItemAction implements Titleable {
EDIT("Edit") { //ES
@Override
public void onClick (final OutboxActivity oa, final OutboxTweet ot) {
oa.getDb().updateOutboxEntry(ot.setPaused());
oa.startActivity(new Intent(oa, PostActivity.class)
.putExtra(PostActivity.ARG_ACCOUNT_ID, ot.getAccountId())
.putStringArrayListExtra(PostActivity.ARG_SVCS, new ArrayList<String>(ot.getSvcMetasList()))
.putExtra(PostActivity.ARG_IN_REPLY_TO_SID, ot.getInReplyToSid())
.putExtra(PostActivity.ARG_BODY, ot.getBody())
.putExtra(PostActivity.ARG_ATTACHMENT, ot.getAttachment())
.putExtra(PostActivity.ARG_OUTBOX_UID, ot.getUid().longValue()));
}
@Override
public boolean applicable (final OutboxTweet ot) {
return ot.getStatus() != OutboxTweetStatus.SENT && ot.getAction() == OutboxAction.POST;
}
},
REPLY("Reply") { //ES
@Override
public void onClick (final OutboxActivity oa, final OutboxTweet ot) {
oa.startActivity(new Intent(oa, PostActivity.class)
.putExtra(PostActivity.ARG_ACCOUNT_ID, ot.getAccountId())
.putStringArrayListExtra(PostActivity.ARG_SVCS, new ArrayList<String>(ot.getSvcMetasList()))
.putExtra(PostActivity.ARG_IN_REPLY_TO_SID, ot.getTempSid()));
}
@Override
public boolean applicable (final OutboxTweet ot) {
return ot.getStatus() != OutboxTweetStatus.SENT && ot.getAction() == OutboxAction.POST;
}
},
VIEW_ERROR("View Error") { //ES
@Override
public void onClick (final OutboxActivity oa, final OutboxTweet ot) {
DialogHelper.alert(oa, ot.getLastError());
}
@Override
public boolean applicable (final OutboxTweet ot) {
return StringHelper.notEmpty(ot.getLastError());
}
},
COPY_BODY("Copy Body") { //ES
@Override
public void onClick (final OutboxActivity oa, final OutboxTweet ot) {
((ClipboardManager) oa.getSystemService(Context.CLIPBOARD_SERVICE))
.setPrimaryClip(ClipData.newPlainText("Tweet", ot.getBody())); //ES
}
@Override
public boolean applicable (final OutboxTweet ot) {
return ot.getAction() == OutboxAction.POST;
}
},
COPY_ERROR("Copy Error") { //ES
@Override
public void onClick (final OutboxActivity oa, final OutboxTweet ot) {
((ClipboardManager) oa.getSystemService(Context.CLIPBOARD_SERVICE))
.setPrimaryClip(ClipData.newPlainText("Error Message", ot.getLastError())); //ES
}
@Override
public boolean applicable (final OutboxTweet ot) {
return StringHelper.notEmpty(ot.getLastError());
}
},
DELETE("Delete") { //ES
@Override
public void onClick (final OutboxActivity oa, final OutboxTweet ot) {
DialogHelper.askYesNo(oa, "Delete outbox item?", "Delete", "Keep", new Runnable() { //ES
@Override
public void run () {
oa.getDb().deleteFromOutbox(ot);
}
});
}
@Override
public boolean applicable (final OutboxTweet ot) {
return ot.getStatus() != OutboxTweetStatus.SENT;
}
};
private final String title;
private OutboxItemAction (final String title) {
this.title = title;
}
@Override
public String getUiTitle () {
return this.title;
}
public abstract boolean applicable (OutboxTweet ot);
public abstract void onClick (OutboxActivity oa, OutboxTweet ot);
}
protected void itemClicked (final OutboxTweet ot) {
final List<OutboxItemAction> actions = CollectionHelper.filter(OutboxItemAction.values(), new Function<OutboxItemAction, Boolean>() {
@Override
public Boolean exec (final OutboxItemAction input) {
return input.applicable(ot);
}
}, new ArrayList<OutboxItemAction>());
DialogHelper.askItem(this, "Outbox Item", actions, new Listener<OutboxItemAction>() { //ES
@Override
public void onAnswer (final OutboxItemAction answer) {
answer.onClick(OutboxActivity.this, ot);
}
});
}
protected void resetPermanentFailures () {
for (final OutboxTweet ot : getDb().getOutboxEntries(OutboxTweetStatus.PERMANENTLY_FAILED)) {
getDb().updateOutboxEntry(ot.setPending());
}
}
protected void requestSendPendingNow () {
startService(new Intent(this, SendOutboxService.class)
.putExtra(SendOutboxService.ARG_TRY_SEND_ALL, true));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
}