package com.vaguehope.onosendai.ui;
import java.io.File;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.List;
import java.util.Set;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.TimeUnit;
import org.json.JSONException;
import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.ProgressDialog;
import android.content.Context;
import android.content.DialogInterface;
import android.content.DialogInterface.OnCancelListener;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.MediaStore;
import android.text.method.TextKeyListener;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.ViewGroup.LayoutParams;
import android.widget.AdapterView;
import android.widget.AdapterView.OnItemSelectedListener;
import android.widget.ImageView;
import android.widget.MultiAutoCompleteTextView;
import android.widget.Spinner;
import android.widget.TextView;
import android.widget.Toast;
import com.vaguehope.onosendai.C;
import com.vaguehope.onosendai.R;
import com.vaguehope.onosendai.config.Account;
import com.vaguehope.onosendai.config.AccountAdaptor;
import com.vaguehope.onosendai.config.Config;
import com.vaguehope.onosendai.config.Prefs;
import com.vaguehope.onosendai.images.HybridBitmapCache;
import com.vaguehope.onosendai.images.ImageLoadRequest;
import com.vaguehope.onosendai.images.ImageLoader;
import com.vaguehope.onosendai.images.ImageLoaderUtils;
import com.vaguehope.onosendai.model.Meta;
import com.vaguehope.onosendai.model.MetaType;
import com.vaguehope.onosendai.model.OutboxTweet;
import com.vaguehope.onosendai.model.OutboxTweet.OutboxAction;
import com.vaguehope.onosendai.model.OutboxTweet.OutboxTweetStatus;
import com.vaguehope.onosendai.model.Tweet;
import com.vaguehope.onosendai.provider.EnabledServiceRefs;
import com.vaguehope.onosendai.provider.SendOutboxService;
import com.vaguehope.onosendai.provider.ServiceRef;
import com.vaguehope.onosendai.storage.AttachmentStorage;
import com.vaguehope.onosendai.storage.DbBindingAsyncTask;
import com.vaguehope.onosendai.storage.DbClient;
import com.vaguehope.onosendai.storage.DbInterface;
import com.vaguehope.onosendai.storage.DbProvider;
import com.vaguehope.onosendai.storage.UsernameSearchAdapter;
import com.vaguehope.onosendai.util.DateHelper;
import com.vaguehope.onosendai.util.DialogHelper;
import com.vaguehope.onosendai.util.DialogHelper.Listener;
import com.vaguehope.onosendai.util.ImageMetadata;
import com.vaguehope.onosendai.util.IoHelper;
import com.vaguehope.onosendai.util.LogWrapper;
import com.vaguehope.onosendai.util.Result;
import com.vaguehope.onosendai.util.StringHelper;
import com.vaguehope.onosendai.util.exec.ExecUtils;
import com.vaguehope.onosendai.widget.adaptor.PopupPositioniner;
import com.vaguehope.onosendai.widget.adaptor.TextCounterWatcher;
import com.vaguehope.onosendai.widget.adaptor.UsernameTokenizer;
public class PostActivity extends Activity implements ImageLoader, DbProvider {
public static final String ARG_ACCOUNT_ID = "account_id";
public static final String ARG_IN_REPLY_TO_SID = "in_reply_to_sid";
public static final String ARG_IN_REPLY_TO_UID = "in_reply_to_uid";
/**
* See MetaType.REPLYTO
*/
public static final String ARG_ALT_REPLY_TO_SID = "reply_to_sid";
public static final String ARG_MENTIONS = "mentions";
public static final String ARG_BODY = "body"; // If present mentions will not be prepended to body.
public static final String ARG_BODY_CURSOR_POSITION = "cursor_position";
public static final String ARG_SVCS = "svcs";
public static final String ARG_ATTACHMENT = "post_attachment_uri";
public static final String ARG_OUTBOX_UID = "outbox_uid";
private static final String ARG_TMP_ATTACHMENT = "post_tmp_attachment_uri";
protected static final LogWrapper LOG = new LogWrapper("PA");
private static final int PROMPT_RESIZE_MIN_SIZE = 512 * 1024;
private Bundle intentExtras;
private long inReplyToUid;
private String inReplyToSid;
private String altReplyToSid;
private String[] mentions;
private Long outboxUid;
private DbClient bndDb;
private HybridBitmapCache imageCache;
private ExecutorService exec;
private Prefs prefs;
private Config conf;
private AccountAdaptor accountAdaptor;
private Spinner spnAccount;
private ViewGroup llSubAccounts;
private MultiAutoCompleteTextView txtBody;
private final EnabledServiceRefs enabledPostToAccounts = new EnabledServiceRefs();
private boolean askAccountOnActivate;
private String txtBodyInitialText;
private Uri attachment;
private Uri tmpAttachment;
@Override
protected void onCreate (final Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.post);
Collection<Account> accounts;
try {
this.prefs = new Prefs(getBaseContext());
accounts = this.prefs.readAccounts();
this.conf = this.prefs.asConfig();
}
catch (final Exception e) { // No point continuing if any exception.
DialogHelper.alertAndClose(this, e);
return;
}
this.imageCache = new HybridBitmapCache(getBaseContext(), C.MAX_MEMORY_IMAGE_CACHE);
this.exec = ExecUtils.newBoundedCachedThreadPool(C.NET_MAX_THREADS, LOG);
this.intentExtras = getIntent().getExtras();
if (this.intentExtras != null) {
this.inReplyToUid = this.intentExtras.getLong(ARG_IN_REPLY_TO_UID);
this.inReplyToSid = this.intentExtras.getString(ARG_IN_REPLY_TO_SID);
this.altReplyToSid = this.intentExtras.getString(ARG_ALT_REPLY_TO_SID);
this.mentions = this.intentExtras.getStringArray(ARG_MENTIONS);
final long outboxUidOrDefault = this.intentExtras.getLong(ARG_OUTBOX_UID, -1);
if (outboxUidOrDefault >= 0) this.outboxUid = outboxUidOrDefault;
}
LOG.i("inReplyToUid=%d inReplyToSid=%s altReplyToSid=%s mentions=%s outboxUid=%s",
this.inReplyToUid, this.inReplyToSid, this.altReplyToSid, Arrays.toString(this.mentions), this.outboxUid);
final ActionBar ab = getActionBar();
ab.setDisplayHomeAsUpEnabled(true);
ab.setDisplayShowTitleEnabled(false);
ab.setDisplayShowCustomEnabled(true);
this.spnAccount = new Spinner(ab.getThemedContext());
this.spnAccount.setLayoutParams(new LayoutParams(LayoutParams.WRAP_CONTENT, LayoutParams.MATCH_PARENT));
this.spnAccount.setPadding(0, 0, 0, 0);
ab.setCustomView(this.spnAccount);
setupAccounts(savedInstanceState, accounts, this.conf);
setupAttachemnt(savedInstanceState);
setupTextBody();
}
@Override
public void onDestroy () {
if (this.imageCache != null) this.imageCache.clean();
if (this.exec != null) this.exec.shutdown();
this.bndDb.dispose();
super.onDestroy();
}
@Override
public void onResume () {
super.onResume();
resumeDb();
if (this.askAccountOnActivate) {
askAccountAndShrinkPicture();
this.askAccountOnActivate = false;
}
}
@Override
protected void onSaveInstanceState (final Bundle outState) {
outState.putString(ARG_ACCOUNT_ID, getSelectedAccount().getId());
this.enabledPostToAccounts.addToBundle(outState);
outState.putParcelable(ARG_ATTACHMENT, this.attachment);
outState.putParcelable(ARG_TMP_ATTACHMENT, this.tmpAttachment);
super.onSaveInstanceState(outState);
}
@Override
public void onBackPressed () {
if (isTxtBodyUnedited()) {
finish();
return;
}
DialogHelper.askYesNo(this, "Save draft?", "Save", "Discard", new Runnable() { //ES
@Override
public void run () {
saveDraft();
}
}, new Runnable() {
@Override
public void run () {
finish();
}
});
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@Override
public void loadImage (final ImageLoadRequest req) {
ImageLoaderUtils.loadImage(this.imageCache, req, this.exec);
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
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 () {
LOG.d("DB service bound.");
showInReplyToTweetDetails();
setupAutoComplete();
}
});
}
}
@Override
public DbInterface getDb () {
final DbClient d = this.bndDb;
if (d == null) return null;
return d.getDb();
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
private void setupAccounts (final Bundle savedInstanceState, final Collection<Account> accounts, final Config conf) {
String accountId = null;
Account account = null;
if (savedInstanceState != null) accountId = savedInstanceState.getString(ARG_ACCOUNT_ID);
if (accountId == null && this.intentExtras != null) accountId = this.intentExtras.getString(ARG_ACCOUNT_ID);
if (accountId != null) {
account = conf.getAccount(accountId);
final List<String> svcs = this.intentExtras != null ? this.intentExtras.getStringArrayList(ARG_SVCS) : null;
LOG.i("accountId=%s svcs=%s", account.getId(), svcs);
this.enabledPostToAccounts.setAccount(account);
this.enabledPostToAccounts.fromBundle(savedInstanceState);
if (svcs != null && !this.enabledPostToAccounts.isServicesPreSpecified()) {
for (final String svc : svcs) {
this.enabledPostToAccounts.enable(ServiceRef.parseServiceMeta(svc));
}
this.enabledPostToAccounts.setServicesPreSpecified(true);
}
}
else {
this.askAccountOnActivate = true;
}
this.accountAdaptor = new AccountAdaptor(getBaseContext(), accounts);
this.spnAccount.setAdapter(this.accountAdaptor);
setSelectedAccount(account);
this.spnAccount.setOnItemSelectedListener(this.accountOnItemSelectedListener);
this.llSubAccounts = (ViewGroup) findViewById(R.id.llSubAccounts);
}
private void setupAttachemnt (final Bundle savedInstanceState) {
if (savedInstanceState != null) {
this.attachment = savedInstanceState.getParcelable(ARG_ATTACHMENT);
this.tmpAttachment = savedInstanceState.getParcelable(ARG_TMP_ATTACHMENT);
}
if (this.attachment == null && this.intentExtras != null) this.attachment = this.intentExtras.getParcelable(ARG_ATTACHMENT);
if (this.attachment == null && Intent.ACTION_SEND.equals(getIntent().getAction())
&& getIntent().getType() != null
&& getIntent().getType().startsWith("image/")) {
final Uri intentUri = getIntent().getParcelableExtra(Intent.EXTRA_STREAM);
if (ImageMetadata.isUnderstoodResource(intentUri)) {
this.attachment = intentUri;
}
else {
DialogHelper.alert(this, "Unknown resource:\n" + intentUri);
}
}
redrawAttachment();
}
private void setupTextBody () {
this.txtBody = (MultiAutoCompleteTextView) findViewById(R.id.txtBody);
final TextView txtCharRemaining = (TextView) findViewById(R.id.txtCharRemaining);
this.txtBody.addTextChangedListener(new TextCounterWatcher(txtCharRemaining, this.txtBody));
if (Intent.ACTION_SEND.equals(getIntent().getAction()) && "text/plain".equals(getIntent().getType())) {
final String intentText = getIntent().getStringExtra(Intent.EXTRA_TEXT);
if (intentText != null) {
this.txtBody.setText(intentText);
this.txtBody.setSelection(this.txtBody.getText().length());
}
}
}
public void setupAutoComplete () {
if (this.txtBody.getAdapter() != null) return;
this.txtBody.setThreshold(1);
this.txtBody.setTokenizer(new UsernameTokenizer());
this.txtBody.setAdapter(new UsernameSearchAdapter(this));
this.txtBody.addTextChangedListener(new PopupPositioniner(this.txtBody));
this.txtBody.setKeyListener(TextKeyListener.getInstance(true, TextKeyListener.Capitalize.SENTENCES));
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
protected AccountAdaptor getAccountAdaptor () {
return this.accountAdaptor;
}
private Account getSelectedAccount () {
return this.accountAdaptor.getAccount(this.spnAccount.getSelectedItemPosition());
}
protected void setSelectedAccount (final Account account) {
if (account == null) return;
this.spnAccount.setSelection(this.accountAdaptor.getAccountPosition(account));
}
protected void askAccountAndShrinkPicture () {
final List<Account> items = readAccountsOrAlert();
if (items == null) return;
DialogHelper.askItem(this, getString(R.string.post_account_dlg_title), items, new Listener<Account>() {
@Override
public void onAnswer (final Account account) {
setSelectedAccount(account);
checkAndAskShrinkPicture();
}
});
}
private List<Account> readAccountsOrAlert () {
try {
return new ArrayList<Account>(this.prefs.readAccounts());
}
catch (final JSONException e) {
DialogHelper.alert(this, "Failed to read accounts.", e); //ES
return null;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
protected void showInReplyToTweetDetails () {
OutboxTweet inReplyToOutboxTweet = null;
if (this.inReplyToSid != null) {
final Tweet tweet;
if (this.inReplyToUid > 0L) {
tweet = getDb().getTweetDetails(this.inReplyToUid);
}
else if (OutboxTweet.isTempSid(this.inReplyToSid)) {
inReplyToOutboxTweet = getDb().getOutboxEntry(OutboxTweet.uidFromTempSid(this.inReplyToSid));
tweet = inReplyToOutboxTweet != null ? inReplyToOutboxTweet.toTweet(this.conf) : null;
}
else {
tweet = getDb().getTweetDetails(this.inReplyToSid);
}
if (tweet != null) {
LOG.i("inReplyTo:%s", tweet.toFullString());
if (!this.enabledPostToAccounts.isServicesPreSpecified()) {
final Meta serviceMeta = tweet.getFirstMetaOfType(MetaType.SERVICE);
if (serviceMeta != null) setPostToAccountExclusive(ServiceRef.parseServiceMeta(serviceMeta));
}
final View view = findViewById(R.id.tweetReplyToDetails);
view.setVisibility(View.VISIBLE);
((TextView) view.findViewById(R.id.tweetDetailBody)).setText(tweet.getBody());
((TextView) view.findViewById(R.id.tweetDetailName)).setText(tweet.getFullname());
final ImageView tweetAvatar = (ImageView) view.findViewById(R.id.tweetDetailAvatar);
if (tweet.getAvatarUrl() != null) {
loadImage(new ImageLoadRequest(tweet.getAvatarUrl(), tweetAvatar));
}
else {
tweetAvatar.setVisibility(View.GONE);
}
final TextView tweetDate = (TextView) view.findViewById(R.id.tweetDetailDate);
if (tweet.getTime() > 0L) {
tweetDate.setText(DateHelper.formatDateTime(this, TimeUnit.SECONDS.toMillis(tweet.getTime())));
}
else {
tweetDate.setVisibility(View.GONE);
}
}
}
initBody(inReplyToOutboxTweet);
}
private void setPostToAccountExclusive (final ServiceRef svc) {
if (svc == null) return;
LOG.d("setPostToAccountExclusive(%s)", svc);
this.enabledPostToAccounts.enableExclusiveAndSetPreSpecified(svc);
PostToAccountLoaderTask.setAccountBtns(this.llSubAccounts, this.enabledPostToAccounts);
}
private void initBody (final OutboxTweet inReplyToOutboxTweet) {
final String intentBody = this.intentExtras != null ? this.intentExtras.getString(ARG_BODY) : null;
final String initialBody;
if (intentBody != null) {
initialBody = intentBody;
}
else {
final StringBuilder s = new StringBuilder();
if (this.mentions != null) {
for (final String mention : this.mentions) {
if (s.length() > 0) s.append(" ");
s.append("@").append(mention);
}
}
else if (inReplyToOutboxTweet != null) {
final List<String> bodyMentions = StringHelper.extractPattern(
StringHelper.MENTIONS_PATTERN, inReplyToOutboxTweet.getBody());
for (final String mention : bodyMentions) {
if (s.length() > 0) s.append(" ");
s.append("@").append(mention);
}
}
if (s.length() > 0) {
s.append(" ");
initialBody = s.toString();
}
else {
initialBody = "";
}
}
this.txtBody.setText(initialBody);
this.txtBodyInitialText = initialBody;
final int cursorPosition = this.intentExtras != null ? this.intentExtras.getInt(ARG_BODY_CURSOR_POSITION, -1) : -1;
this.txtBody.setSelection(cursorPosition >= 0 ? cursorPosition : this.txtBody.getText().length());
}
private boolean isTxtBodyUnedited () {
if (this.txtBodyInitialText == null) return false;
return this.txtBodyInitialText.length() == this.txtBody.getText().length()
&& this.txtBodyInitialText.equals(this.txtBody.getText().toString());
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
protected OutboxTweet makeOutboxTweet (final Account account, final Set<ServiceRef> svcs) {
final String replyToSidToSubmit;
if (!StringHelper.isEmpty(this.altReplyToSid)) {
replyToSidToSubmit = this.altReplyToSid;
}
else {
replyToSidToSubmit = this.inReplyToSid;
}
final OutboxTweet ot = new OutboxTweet(OutboxAction.POST, account, svcs, this.txtBody.getText().toString(), replyToSidToSubmit, this.attachment);
if (this.outboxUid != null) {
return ot.withUid(this.outboxUid);
}
else {
return ot;
}
}
protected void saveDraft () {
final Account account = getSelectedAccount();
final Set<ServiceRef> svcs = this.enabledPostToAccounts.copyOfServices();
final OutboxTweet ot = makeOutboxTweet(account, svcs).setPaused();
new SubmitToOutboxTask(this, ot, new Listener<Long>() {
@Override
public void onAnswer (final Long newId) {
finish();
}
}).execute();
}
protected void askPost (final boolean andContinue) {
final Account account = getSelectedAccount();
final Set<ServiceRef> svcs = this.enabledPostToAccounts.copyOfServices();
final AlertDialog.Builder dlgBld = new AlertDialog.Builder(this);
String msg;
switch (account.getProvider()) {
case SUCCESSWHALE:
case BUFFER:
msg = getString(R.string.post_confirm_post_subaccounts, account.getUiTitle(), ServiceRef.humanList(svcs, "\n"));
break;
default:
msg = getString(R.string.post_confirm_post, account.getUiTitle());
}
dlgBld.setMessage(msg);
final int msgId;
if (andContinue) {
msgId = this.outboxUid == null
? R.string.post_confirm_post_btn_post_and_continue
: R.string.post_confirm_post_btn_update_post_and_continue;
}
else {
msgId = this.outboxUid == null
? R.string.post_confirm_post_btn_post
: R.string.post_confirm_post_btn_update_post;
}
dlgBld.setPositiveButton(msgId,
new DialogInterface.OnClickListener() {
@Override
public void onClick (final DialogInterface dialog, final int which) {
final OutboxTweet ot = makeOutboxTweet(account, svcs).setPending();
submitPost(ot, andContinue);
}
});
dlgBld.setNegativeButton(R.string.post_confirm_post_btn_cancel, new DialogInterface.OnClickListener() {
@Override
public void onClick (final DialogInterface dialog, final int whichButton) {
dialog.cancel();
}
});
dlgBld.show();
}
protected void submitPost (final OutboxTweet ot, final boolean andContinue) {
new SubmitToOutboxTask(this, ot, new Listener<Long>() {
@Override
public void onAnswer (final Long newId) {
if (andContinue) {
startActivity(new Intent(PostActivity.this, 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.getUid() == null ? ot.withUid(newId) : ot).getTempSid()));
}
finish();
}
}).execute();
}
private final OnItemSelectedListener accountOnItemSelectedListener = new OnItemSelectedListener() {
@Override
public void onItemSelected (final AdapterView<?> parent, final View view, final int position, final long id) {
accountSelected(getAccountAdaptor().getAccount(position));
}
@Override
public void onNothingSelected (final AdapterView<?> arg0) {/**/}
};
protected void accountSelected (final Account account) {
switch (account.getProvider()) {
case SUCCESSWHALE:
case BUFFER:
this.enabledPostToAccounts.setAccount(account);
new PostToAccountLoaderTask(getApplicationContext(), this.llSubAccounts, this.enabledPostToAccounts).executeOnExecutor(this.exec, account);
break;
default:
this.llSubAccounts.setVisibility(View.GONE);
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
@Override
public boolean onCreateOptionsMenu (final Menu menu) {
getMenuInflater().inflate(R.menu.postmenu, menu);
if (this.outboxUid != null) {
final MenuItem mnuPost = menu.findItem(R.id.mnuPost);
mnuPost.setTitle(R.string.post_btn_update_post);
}
new Handler().post(new Runnable() {
@Override
public void run () {
extraMenuSetup();
}
});
return true;
}
protected void extraMenuSetup () {
final View mnuPost = findViewById(R.id.mnuPost);
mnuPost.setOnLongClickListener(new OnLongClickListener() {
@Override
public boolean onLongClick (final View v) {
askPost(true);
return true;
}
});
}
@Override
public boolean onPrepareOptionsMenu (final Menu menu) {
if (this.attachment == null) {
menu.findItem(R.id.mnuAttach).setVisible(true);
menu.findItem(R.id.mnuAttachmentGroup).setVisible(false);
}
else {
menu.findItem(R.id.mnuAttach).setVisible(false);
menu.findItem(R.id.mnuAttachmentGroup).setVisible(true);
}
return true;
}
@Override
public boolean onOptionsItemSelected (final MenuItem item) {
switch (item.getItemId()) {
case android.R.id.home:
onBackPressed();
return true;
case R.id.mnuPost:
askPost(false);
return true;
case R.id.mnuTextFilter:
showTextFiltersDlg();
return true;
case R.id.mnuSaveAsDraft:
saveDraft();
return true;
case R.id.mnuAttach:
case R.id.mnuAttachmentChange:
askChoosePicture();
return true;
case R.id.mnuAttachmentShrink:
showShrinkPictureDlg();
return true;
case R.id.mnuAttachmentRemove:
setAndRedrawAttachment(null);
return true;
default:
return false;
}
}
// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
private static final int SELECT_PICTURE = 105340; // NOSONAR Just a number.
private ImageMetadata redrawAttachment () {
final ImageMetadata metadata = new ImageMetadata(this, this.attachment);
final TextView txtAttached = (TextView) findViewById(R.id.txtAttached);
txtAttached.setText(getString(R.string.post_attachment_current, metadata.getUiTitle()));
txtAttached.setVisibility(metadata.exists() ? View.VISIBLE : View.GONE);
return metadata;
}
protected ImageMetadata setAndRedrawAttachment (final Uri att) {
this.attachment = att;
return redrawAttachment();
}
private void showTextFiltersDlg () {
TextFiltersDialog.show(this, this.txtBody.getText().toString(), new Listener<String>() {
@Override
public void onAnswer (final String filteredText) {
PostActivity.this.txtBody.setText(filteredText);
}
});
}
private void askChoosePicture () {
this.tmpAttachment = Uri.fromFile(AttachmentStorage.getExternalTempFile(".jpg"));
startActivityForResult(Intent.createChooser(
new Intent(Intent.ACTION_GET_CONTENT)
.setType("image/*")
.addCategory(Intent.CATEGORY_OPENABLE),
getString(R.string.post_attachment_ask_select))
.putExtra(Intent.EXTRA_INITIAL_INTENTS, new Intent[] {
new Intent(MediaStore.ACTION_IMAGE_CAPTURE)
.putExtra(MediaStore.EXTRA_OUTPUT, this.tmpAttachment) })
, SELECT_PICTURE);
}
protected void showShrinkPictureDlg () {
try {
final PictureResizeDialog dlg = new PictureResizeDialog(this, this.attachment);
final AlertDialog.Builder dlgBuilder = new AlertDialog.Builder(this);
dlgBuilder.setTitle(dlg.getUiTitle());
dlgBuilder.setView(dlg.getRootView());
dlgBuilder.setPositiveButton(android.R.string.ok, new android.content.DialogInterface.OnClickListener() {
@Override
public void onClick (final DialogInterface dialog, final int which) {
resizeAttachedPicture(dlg);
dialog.dismiss();
}
});
dlgBuilder.setNegativeButton("Cancel", new DialogInterface.OnClickListener() { //ES
@Override
public void onClick (final DialogInterface dialog, final int whichButton) {
dialog.cancel();
}
});
dlgBuilder.setOnCancelListener(new OnCancelListener() {
@Override
public void onCancel (final DialogInterface dialog) {
dlg.recycle();
}
});
dlgBuilder.create().show();
}
// XXX Cases of OutOfMemoryError have been reported, particularly on low end hardware.
// Try not to upset the user too much by not dying completely if possible.
catch (final Throwable t) {
DialogHelper.alert(this, t);
}
}
protected void resizeAttachedPicture (final PictureResizeDialog dlg) {
new PictureResizeDialog.ResizeToTempFileTask(dlg, new Listener<Result<Uri>>() {
@Override
public void onAnswer (final Result<Uri> result) {
dlg.recycle();
if (result.isSuccess()) {
setAndRedrawAttachment(result.getData());
}
else {
LOG.e("Failed to resize image.", result.getE());
DialogHelper.alert(PostActivity.this, result.getE());
}
}
}).execute();
}
@Override
protected void onActivityResult (final int requestCode, final int resultCode, final Intent intent) {
super.onActivityResult(requestCode, resultCode, intent);
switch (requestCode) {
case SELECT_PICTURE:
if (resultCode == RESULT_OK) onPictureChosen(intent);
break;
default:
}
}
private void onPictureChosen (final Intent intent) {
try {
Uri uri;
if (intent != null) {
uri = intent.getData();
}
else if (this.tmpAttachment != null) {
uri = Uri.fromFile(AttachmentStorage.moveToInternalStorage(this, new File(this.tmpAttachment.getPath())));
}
else {
DialogHelper.alert(this, "Media is missing from response intent."); //ES
return;
}
if (ImageMetadata.isUnderstoodResource(uri)) {
this.attachment = uri;
final ImageMetadata metadata = redrawAttachment();
checkAndAskShrinkPicture(metadata);
}
else {
DialogHelper.alert(this, "Unknown resource:\n" + uri); //ES
}
}
catch (final IOException e) {
DialogHelper.alert(this, e);
}
}
protected void checkAndAskShrinkPicture () {
checkAndAskShrinkPicture(new ImageMetadata(this, this.attachment));
}
private void checkAndAskShrinkPicture (final ImageMetadata metadata) {
if (metadata.getSize() < PROMPT_RESIZE_MIN_SIZE) return;
DialogHelper.askYesNo(this,
getString(R.string.post_attachment_ask_shrink, IoHelper.readableFileSize(metadata.getSize())),
getString(R.string.post_attachment_ask_shrink_btn_skrink),
getString(R.string.post_attachment_ask_shrink_btn_full_size),
new Runnable() {
@Override
public void run () {
showShrinkPictureDlg();
}
});
}
private static class SubmitToOutboxTask extends DbBindingAsyncTask<Void, String, Exception> {
private final OutboxTweet ot;
private ProgressDialog dialog;
private final Listener<Long> onSuccess;
private volatile Long newId;
public SubmitToOutboxTask (final Context context, final OutboxTweet ot, final Listener<Long> onSuccess) {
super(null, context);
this.ot = ot;
this.onSuccess = onSuccess;
}
@Override
protected LogWrapper getLog () {
return LOG;
}
@Override
protected void onPreExecute () {
this.dialog = ProgressDialog.show(getContext(), "Writing to Outbox", "...", true); //ES
}
@Override
protected void onProgressUpdate (final String... msgs) {
if (msgs == null || msgs.length < 1) return;
this.dialog.setMessage(msgs[0]);
}
@Override
protected Exception doInBackgroundWithDb (final DbInterface db, final Void... params) {
try {
final OutboxTweet otToAdd;
if (this.ot.getAttachment() != null && ImageMetadata.isRemoteResource(this.ot.getAttachment())) {
publishProgress("Caching attachment..."); //ES
final ImageMetadata im = new ImageMetadata(getContext(), this.ot.getAttachment());
final File internal = AttachmentStorage.getTempFile(getContext(), im.getName());
final InputStream is = im.open();
try {
IoHelper.copy(is, internal);
}
finally {
IoHelper.closeQuietly(is);
}
otToAdd = this.ot.withAttachment(Uri.fromFile(internal));
}
else {
otToAdd = this.ot;
}
publishProgress("Writing to Outbox..."); //ES
if (otToAdd.getUid() == null) {
this.newId = db.addPostToOutput(otToAdd);
}
else {
db.updateOutboxEntry(otToAdd);
}
return null;
}
catch (final IOException e) {
return e;
}
}
@Override
protected void onPostExecute (final Exception result) {
this.dialog.dismiss();
if (result == null) {
if (this.ot.getStatus() == OutboxTweetStatus.PENDING) {
getContext().startService(new Intent(getContext(), SendOutboxService.class));
final String msg;
if (this.ot.getUid() == null) {
msg = "Posted via Outbox"; //ES
}
else {
msg = "Updated Outbox item"; //ES
}
Toast.makeText(getContext(), msg, Toast.LENGTH_SHORT).show();
}
else if (this.ot.getStatus() == OutboxTweetStatus.PAUSED) {
Toast.makeText(getContext(), "Draft Saved to Outbox", Toast.LENGTH_SHORT).show(); //ES
}
else {
LOG.w("Unexpected OT status: " + this.ot.getStatus());
}
this.onSuccess.onAnswer(this.newId);
}
else {
LOG.e("Failed to add to outbox.", result);
DialogHelper.alert(getContext(), result);
}
}
}
}