Java tutorial
/* * Tweetings - Twitter client for Android * * Copyright (C) 2012-2013 RBD Solutions Limited <apps@tweetings.net> * Copyright (C) 2012 Mariotaku Lee <mariotaku.lee@gmail.com> * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.dwdesign.tweetings.activity; import static android.os.Environment.getExternalStorageDirectory; import static android.os.Environment.getExternalStorageState; import static com.dwdesign.tweetings.util.Utils.getAccountColors; import static com.dwdesign.tweetings.util.Utils.getAccountUsername; import static com.dwdesign.tweetings.util.Utils.getActivatedAccountIds; import static com.dwdesign.tweetings.util.Utils.getImagePathFromUri; import static com.dwdesign.tweetings.util.Utils.getImageUploadStatus; import static com.dwdesign.tweetings.util.Utils.getShareStatus; import static com.dwdesign.tweetings.util.Utils.isNullOrEmpty; import static com.dwdesign.tweetings.util.Utils.parseString; import static com.dwdesign.tweetings.util.Utils.showErrorToast; import static com.dwdesign.tweetings.util.Utils.openImage; import static com.dwdesign.tweetings.util.Utils.getTwitterAccessToken; import java.io.BufferedReader; import java.io.File; import java.io.InputStreamReader; import java.net.URLEncoder; import java.nio.charset.Charset; import java.util.ArrayList; import java.util.Hashtable; import java.util.List; import java.util.Timer; import java.util.TimerTask; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import javax.xml.xpath.XPath; import javax.xml.xpath.XPathConstants; import javax.xml.xpath.XPathExpression; import javax.xml.xpath.XPathFactory; import org.apache.http.HttpResponse; import org.apache.http.client.HttpClient; import org.apache.http.client.methods.HttpGet; import org.apache.http.client.methods.HttpPost; import org.apache.http.entity.StringEntity; import org.apache.http.entity.mime.HttpMultipartMode; import org.apache.http.entity.mime.MultipartEntity; import org.apache.http.entity.mime.content.StringBody; import org.apache.http.impl.client.DefaultHttpClient; import org.apache.http.protocol.HTTP; import org.json.JSONObject; import org.w3c.dom.Document; //import com.aviary.android.feather.FeatherActivity; //import com.aviary.android.feather.library.utils.StringUtils; import com.dwdesign.actionbarcompat.ActionBar; import com.dwdesign.menubar.MenuBar; import com.dwdesign.menubar.MenuBar.OnMenuItemClickListener; import com.dwdesign.popupmenu.PopupMenu; import com.dwdesign.tweetings.R; import com.dwdesign.tweetings.provider.TweetStore.Drafts; import com.dwdesign.tweetings.util.ArrayUtils; import com.dwdesign.tweetings.service.TweetingsService; import com.dwdesign.tweetings.util.GetExternalCacheDirAccessor; import com.dwdesign.tweetings.util.ServiceInterface; import com.dwdesign.tweetings.util.EnvironmentAccessor; import com.dwdesign.tweetings.view.ColorView; import android.app.Activity; import android.app.AlertDialog; import android.app.Dialog; import android.app.ProgressDialog; import android.content.ActivityNotFoundException; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.DialogInterface; import android.content.Intent; import android.content.SharedPreferences; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.BitmapFactory; import android.graphics.Color; import android.graphics.PorterDuff.Mode; import android.graphics.drawable.Drawable; import android.location.Criteria; import android.location.Location; import android.location.LocationListener; import android.location.LocationManager; import android.net.Uri; import android.os.AsyncTask; import android.os.Build; import android.os.Bundle; import android.os.Environment; import android.provider.MediaStore; import android.support.v4.app.DialogFragment; import android.support.v4.app.Fragment; import android.support.v4.app.NavUtils; import android.support.v4.app.FragmentActivity; import android.text.Editable; import android.text.InputType; import android.text.Spannable; import android.text.SpannableString; import android.text.TextWatcher; import android.view.KeyEvent; import android.text.method.ArrowKeyMovementMethod; import android.text.style.ClickableSpan; import android.util.Log; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.View.OnLongClickListener; import android.view.inputmethod.EditorInfo; import android.widget.EditText; import android.widget.ImageView; import android.widget.TextView; import android.widget.TextView.OnEditorActionListener; import android.widget.Toast; import com.twitter.Validator; import com.dwdesign.tweetings.fragment.BaseDialogFragment; import twitter4j.TwitterException; import twitter4j.auth.AccessToken; import twitter4j.auth.OAuthAuthorization; import twitter4j.conf.Configuration; import twitter4j.conf.ConfigurationBuilder; import twitter4j.internal.http.HttpParameter; import twitter4j.media.ImageUpload; import twitter4j.media.ImageUploadFactory; import twitter4j.media.MediaProvider; public class ComposeActivity extends BaseActivity implements TextWatcher, LocationListener, OnMenuItemClickListener, OnClickListener, OnLongClickListener, PopupMenu.OnMenuItemClickListener, OnEditorActionListener { private static final String FAKE_IMAGE_LINK = "https://www.example.com/fake_image.jpg"; private static final String INTENT_KEY_CONTENT_MODIFIED = "content_modified"; private static final String INTENT_KEY_IS_NAVIGATE_UP = "is_navigate_up"; private static final String INTENT_KEY_IS_POSSIBLY_SENSITIVE = "is_possibly_sensitive"; private ImageUpload upload; private Timer uploadTimer; private ServiceInterface mService; private LocationManager mLocationManager; private SharedPreferences mPreferences; private Location mRecentLocation; private ContentResolver mResolver; private final Validator mValidator = new Validator(); private AttachedImageThumbnailTask mAttachedImageThumbnailTask; private ActionBar mActionBar; private PopupMenu mPopupMenu; private static final int THUMBNAIL_SIZE = 36; private static final int ACTION_REQUEST_FEATHER = 100; private ColorView mColorIndicator; private EditText mEditText; private TextView mTextCount; private ImageView mImageThumbnailPreview; private MenuBar mMenuBar; private boolean mIsImageAttached, mIsPhotoAttached; private long[] mAccountIds; private String mText; private Uri mImageUri; private long mInReplyToStatusId = -1; private String mInReplyToText = null; private String mInReplyToScreenName, mInReplyToName; private boolean mIsQuote, mUploadUseExtension, mIsBuffer, mContentModified, mIsPossiblySensitive; private String mUploadProvider; private ProgressDialog progressDialog; private ArrayList<String> tweetParts; private String mScheduleDate; private DialogFragment mUnsavedTweetDialogFragment; @Override public void afterTextChanged(final Editable s) { } @Override public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) { } @Override public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) { switch (requestCode) { case REQUEST_SCHEDULE_DATE: { if (resultCode == Activity.RESULT_OK) { Bundle bundle = intent.getExtras(); mScheduleDate = bundle.getString(INTENT_KEY_SCHEDULE_DATE_TIME); } else { if (mScheduleDate != null) { mScheduleDate = null; } } setMenu(); break; } case REQUEST_TAKE_PHOTO: { if (resultCode == Activity.RESULT_OK) { final File file = new File(mImageUri.getPath()); if (file.exists()) { mIsImageAttached = false; mIsPhotoAttached = true; mImageThumbnailPreview.setVisibility(View.VISIBLE); reloadAttachedImageThumbnail(file); } else { mIsPhotoAttached = false; } setMenu(); boolean isAutoUpload = mPreferences.getBoolean(PREFERENCE_KEY_AUTO_UPLOAD, false); if (!isNullOrEmpty(mUploadProvider) && mIsPhotoAttached && isAutoUpload) { postMedia(); } } break; } case REQUEST_PICK_IMAGE: { if (resultCode == Activity.RESULT_OK) { final Uri uri = intent.getData(); final File file = uri == null ? null : new File(getImagePathFromUri(this, uri)); if (file != null && file.exists()) { mImageUri = Uri.fromFile(file); mIsPhotoAttached = false; mIsImageAttached = true; mImageThumbnailPreview.setVisibility(View.VISIBLE); reloadAttachedImageThumbnail(file); } else { mIsImageAttached = false; } setMenu(); boolean isAutoUpload = mPreferences.getBoolean(PREFERENCE_KEY_AUTO_UPLOAD, false); if (!isNullOrEmpty(mUploadProvider) && mIsImageAttached && isAutoUpload) { postMedia(); } } break; } case REQUEST_SELECT_ACCOUNT: { if (resultCode == Activity.RESULT_OK) { final Bundle bundle = intent.getExtras(); if (bundle == null) { break; } final long[] account_ids = bundle.getLongArray(INTENT_KEY_IDS); if (account_ids != null) { mAccountIds = account_ids; if (mInReplyToStatusId <= 0 && !Intent.ACTION_SEND.equals(getIntent().getAction())) { final SharedPreferences.Editor editor = mPreferences.edit(); editor.putString(PREFERENCE_KEY_COMPOSE_ACCOUNTS, ArrayUtils.toString(mAccountIds, ',', false)); editor.commit(); } mColorIndicator.setColor(getAccountColors(this, account_ids)); } } break; } case REQUEST_EDIT_IMAGE: { if (resultCode == Activity.RESULT_OK) { final Uri uri = intent.getData(); final File file = uri == null ? null : new File(getImagePathFromUri(this, uri)); if (file != null && file.exists()) { mImageUri = Uri.fromFile(file); reloadAttachedImageThumbnail(file); } else { break; } setMenu(); } break; } case REQUEST_EXTENSION_COMPOSE: { if (resultCode == Activity.RESULT_OK) { final Bundle extras = intent.getExtras(); if (extras == null) { break; } final String text = extras.getString(INTENT_KEY_TEXT); final String append = extras.getString(INTENT_KEY_APPEND_TEXT); if (text != null) { mEditText.setText(text); mText = parseString(mEditText.getText()); } else if (append != null) { mEditText.append(append); mText = parseString(mEditText.getText()); } } break; } case ACTION_REQUEST_FEATHER: if (resultCode == RESULT_OK) { final Uri uri = intent.getData(); final File file = uri == null ? null : new File(getImagePathFromUri(this, uri)); if (file != null && file.exists()) { mImageUri = Uri.fromFile(file); reloadAttachedImageThumbnail(file); } else { break; } setMenu(); } break; } } @Override public void onBackPressed() { final String text = mEditText != null ? parseString(mEditText.getText()) : null; if (mContentModified && !isNullOrEmpty(text)) { mUnsavedTweetDialogFragment = (DialogFragment) Fragment.instantiate(this, UnsavedTweetDialogFragment.class.getName()); final Bundle args = new Bundle(); args.putBoolean(INTENT_KEY_IS_NAVIGATE_UP, false); mUnsavedTweetDialogFragment.setArguments(args); mUnsavedTweetDialogFragment.show(getSupportFragmentManager(), "unsaved_tweet"); return; } super.onBackPressed(); } @Override public void onClick(final View view) { switch (view.getId()) { case R.id.image_thumbnail_preview: { if (mPopupMenu != null) { mPopupMenu.dismiss(); } mPopupMenu = PopupMenu.getInstance(this, view); mPopupMenu.inflate(R.menu.action_attached_image); mPopupMenu.setOnMenuItemClickListener(this); final Menu menu = mPopupMenu.getMenu(); final MenuItem itemUpload = menu.findItem(MENU_UPLOAD); if (!isNullOrEmpty(mUploadProvider) && itemUpload != null) { itemUpload.setVisible(true); } mPopupMenu.show(); break; } } } @Override public void onContentChanged() { super.onContentChanged(); mColorIndicator = (ColorView) findViewById(R.id.account_colors); mEditText = (EditText) findViewById(R.id.edit_text); mTextCount = (TextView) findViewById(R.id.text_count); mImageThumbnailPreview = (ImageView) findViewById(R.id.image_thumbnail_preview); mMenuBar = (MenuBar) findViewById(R.id.menu_bar); } @Override public void onCreate(final Bundle savedInstanceState) { mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE); mPreferences = getSharedPreferences(SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE); mService = getTweetingsApplication().getServiceInterface(); mResolver = getContentResolver(); super.onCreate(savedInstanceState); setContentView(R.layout.compose); mActionBar = getSupportActionBar(); mActionBar.setDisplayHomeAsUpEnabled(true); mUploadProvider = mPreferences.getString(PREFERENCE_KEY_IMAGE_UPLOADER, null); final Bundle bundle = savedInstanceState != null ? savedInstanceState : getIntent().getExtras(); final long account_id = bundle != null ? bundle.getLong(INTENT_KEY_ACCOUNT_ID) : -1; mAccountIds = bundle != null ? bundle.getLongArray(INTENT_KEY_IDS) : null; mInReplyToStatusId = bundle != null ? bundle.getLong(INTENT_KEY_IN_REPLY_TO_ID) : -1; mInReplyToScreenName = bundle != null ? bundle.getString(INTENT_KEY_IN_REPLY_TO_SCREEN_NAME) : null; mInReplyToName = bundle != null ? bundle.getString(INTENT_KEY_IN_REPLY_TO_NAME) : null; mInReplyToText = bundle != null ? bundle.getString(INTENT_KEY_IN_REPLY_TO_TWEET) : null; mIsImageAttached = bundle != null ? bundle.getBoolean(INTENT_KEY_IS_IMAGE_ATTACHED) : false; mIsPhotoAttached = bundle != null ? bundle.getBoolean(INTENT_KEY_IS_PHOTO_ATTACHED) : false; mImageUri = bundle != null ? (Uri) bundle.getParcelable(INTENT_KEY_IMAGE_URI) : null; final String[] mentions = bundle != null ? bundle.getStringArray(INTENT_KEY_MENTIONS) : null; final String account_username = getAccountUsername(this, account_id); int text_selection_start = -1; mIsBuffer = bundle != null ? bundle.getBoolean(INTENT_KEY_IS_BUFFER, false) : false; if (mInReplyToStatusId > 0) { if (bundle != null && bundle.getString(INTENT_KEY_TEXT) != null && (mentions == null || mentions.length < 1)) { mText = bundle.getString(INTENT_KEY_TEXT); } else if (mentions != null) { final StringBuilder builder = new StringBuilder(); for (final String mention : mentions) { if (mentions.length == 1 && mentions[0].equalsIgnoreCase(account_username)) { builder.append('@' + account_username + ' '); } else if (!mention.equalsIgnoreCase(account_username)) { builder.append('@' + mention + ' '); } } mText = builder.toString(); text_selection_start = mText.indexOf(' ') + 1; } mIsQuote = bundle != null ? bundle.getBoolean(INTENT_KEY_IS_QUOTE, false) : false; final boolean display_name = mPreferences.getBoolean(PREFERENCE_KEY_DISPLAY_NAME, false); final String name = display_name ? mInReplyToName : mInReplyToScreenName; if (name != null) { setTitle(getString(mIsQuote ? R.string.quote_user : R.string.reply_to, name)); } if (mAccountIds == null || mAccountIds.length == 0) { mAccountIds = new long[] { account_id }; } TextView replyText = (TextView) findViewById(R.id.reply_text); if (!isNullOrEmpty(mInReplyToText)) { replyText.setVisibility(View.VISIBLE); replyText.setText(mInReplyToText); } else { replyText.setVisibility(View.GONE); } } else { if (mentions != null) { final StringBuilder builder = new StringBuilder(); for (final String mention : mentions) { if (mentions.length == 1 && mentions[0].equalsIgnoreCase(account_username)) { builder.append('@' + account_username + ' '); } else if (!mention.equalsIgnoreCase(account_username)) { builder.append('@' + mention + ' '); } } mText = builder.toString(); } if (mAccountIds == null || mAccountIds.length == 0) { final long[] ids_in_prefs = ArrayUtils .fromString(mPreferences.getString(PREFERENCE_KEY_COMPOSE_ACCOUNTS, null), ','); final long[] activated_ids = getActivatedAccountIds(this); final long[] intersection = ArrayUtils.intersection(ids_in_prefs, activated_ids); mAccountIds = intersection.length > 0 ? intersection : activated_ids; } final String action = getIntent().getAction(); if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) { setTitle(R.string.share); final Bundle extras = getIntent().getExtras(); if (extras != null) { if (mText == null) { final CharSequence extra_subject = extras.getCharSequence(Intent.EXTRA_SUBJECT); final CharSequence extra_text = extras.getCharSequence(Intent.EXTRA_TEXT); mText = getShareStatus(this, parseString(extra_subject), parseString(extra_text)); } else { mText = bundle.getString(INTENT_KEY_TEXT); } if (mImageUri == null) { final Uri extra_stream = extras.getParcelable(Intent.EXTRA_STREAM); final String content_type = getIntent().getType(); if (extra_stream != null && content_type != null && content_type.startsWith("image/")) { final String real_path = getImagePathFromUri(this, extra_stream); final File file = real_path != null ? new File(real_path) : null; if (file != null && file.exists()) { mImageUri = Uri.fromFile(file); mIsImageAttached = true; mIsPhotoAttached = false; } else { mImageUri = null; mIsImageAttached = false; } } } } } else if (bundle != null) { if (bundle.getString(INTENT_KEY_TEXT) != null) { mText = bundle.getString(INTENT_KEY_TEXT); } } } final File image_file = mImageUri != null && "file".equals(mImageUri.getScheme()) ? new File(mImageUri.getPath()) : null; final boolean image_file_valid = image_file != null && image_file.exists(); mImageThumbnailPreview.setVisibility(image_file_valid ? View.VISIBLE : View.GONE); if (image_file_valid) { reloadAttachedImageThumbnail(image_file); } mImageThumbnailPreview.setOnClickListener(this); mImageThumbnailPreview.setOnLongClickListener(this); mMenuBar.setOnMenuItemClickListener(this); mMenuBar.inflate(R.menu.menu_compose); mMenuBar.show(); if (mPreferences.getBoolean(PREFERENCE_KEY_QUICK_SEND, false)) { mEditText.setRawInputType(InputType.TYPE_CLASS_TEXT | InputType.TYPE_TEXT_FLAG_CAP_SENTENCES | InputType.TYPE_TEXT_FLAG_MULTI_LINE | InputType.TYPE_TEXT_FLAG_AUTO_CORRECT); mEditText.setMovementMethod(ArrowKeyMovementMethod.getInstance()); mEditText.setImeOptions(EditorInfo.IME_ACTION_GO); mEditText.setOnEditorActionListener(this); } mEditText.addTextChangedListener(this); if (mText != null) { mEditText.setText(mText); if (mIsQuote) { mEditText.setSelection(0); } else if (text_selection_start != -1 && text_selection_start < mEditText.length() && mEditText.length() > 0) { mEditText.setSelection(text_selection_start, mEditText.length() - 1); } else if (mEditText.length() > 0) { mEditText.setSelection(mEditText.length()); } } invalidateSupportOptionsMenu(); setMenu(); if (mColorIndicator != null) { mColorIndicator.setOrientation(ColorView.VERTICAL); mColorIndicator.setColor(getAccountColors(this, mAccountIds)); } mContentModified = savedInstanceState != null ? savedInstanceState.getBoolean(INTENT_KEY_CONTENT_MODIFIED) : false; mIsPossiblySensitive = savedInstanceState != null ? savedInstanceState.getBoolean(INTENT_KEY_IS_POSSIBLY_SENSITIVE) : false; } @Override public boolean onCreateOptionsMenu(final Menu menu) { getMenuInflater().inflate(R.menu.menu_compose_actionbar, menu); return super.onCreateOptionsMenu(menu); } @Override public boolean onEditorAction(final TextView view, final int actionId, final KeyEvent event) { if (event == null) return false; switch (event.getKeyCode()) { case KeyEvent.KEYCODE_ENTER: { if (event.getAction() == KeyEvent.ACTION_UP) { send(); } return true; } } return false; } /** Sets the mRecentLocation object to the current location of the device **/ @Override public void onLocationChanged(final Location location) { mRecentLocation = location; } @Override public boolean onLongClick(final View view) { switch (view.getId()) { case R.id.image_thumbnail_preview: { onClick(view); return true; } } return false; } public void removeAttachments() { if (mIsImageAttached && !mIsPhotoAttached) { mImageUri = null; } else if (mIsPhotoAttached && !mIsImageAttached) { final File image_file = mImageUri != null && "file".equals(mImageUri.getScheme()) ? new File(mImageUri.getPath()) : null; if (image_file != null) { image_file.delete(); } mImageUri = null; } mIsPhotoAttached = false; mIsImageAttached = false; reloadAttachedImageThumbnail(null); setMenu(); } @SuppressWarnings("deprecation") @Override public boolean onMenuItemClick(final MenuItem item) { switch (item.getItemId()) { case MENU_TAKE_PHOTO: { if (item.getTitle().equals(getString(R.string.remove_photo))) { if (mImageUri == null) return false; removeAttachments(); } else { takePhoto(); } break; } case MENU_LAST_PHOTO: { String[] projection = new String[] { MediaStore.Images.ImageColumns._ID, MediaStore.Images.ImageColumns.DATA, MediaStore.Images.ImageColumns.BUCKET_DISPLAY_NAME, MediaStore.Images.ImageColumns.DATE_TAKEN, MediaStore.Images.ImageColumns.MIME_TYPE }; final Cursor cursor = managedQuery(MediaStore.Images.Media.EXTERNAL_CONTENT_URI, projection, null, null, MediaStore.Images.ImageColumns.DATE_TAKEN + " DESC"); if (cursor.moveToFirst()) { String imageLocation = cursor.getString(1); File imageFile = new File(imageLocation); if (imageFile != null && imageFile.exists()) { // TODO: is there a better way to do this? mImageUri = Uri.fromFile(imageFile); mIsPhotoAttached = false; mIsImageAttached = true; mImageThumbnailPreview.setVisibility(View.VISIBLE); reloadAttachedImageThumbnail(imageFile); } else { mIsImageAttached = false; } setMenu(); boolean isAutoUpload = mPreferences.getBoolean(PREFERENCE_KEY_AUTO_UPLOAD, false); if (!isNullOrEmpty(mUploadProvider) && mIsImageAttached && isAutoUpload) { postMedia(); } } break; } case MENU_ADD_IMAGE: { if (item.getTitle().equals(getString(R.string.remove_image))) { if (mImageUri == null) return false; removeAttachments(); } else { pickImage(); } break; } case MENU_SHORTEN_LINKS: { String s = parseString(mEditText.getText()); new UrlShortenerTask().execute(s); break; } case MENU_ADD_TO_BUFFER: { if (mIsBuffer) { mIsBuffer = false; } else { mIsBuffer = true; } setMenu(); break; } case MENU_SCHEDULE_TWEET: { if (mScheduleDate != null) { mScheduleDate = null; setMenu(); } else { Intent intent = new Intent(INTENT_ACTION_SCHEDULE_TWEET); if (mScheduleDate != null) { Bundle bundle = new Bundle(); bundle.putString(INTENT_KEY_SCHEDULE_DATE_TIME, mScheduleDate); intent.putExtras(bundle); } startActivityForResult(intent, REQUEST_SCHEDULE_DATE); } break; } case MENU_UPLOAD: { if (!isNullOrEmpty(mUploadProvider) && mIsImageAttached) { postMedia(true); } break; } case MENU_ADD_LOCATION: { final boolean attach_location = mPreferences.getBoolean(PREFERENCE_KEY_ATTACH_LOCATION, false); if (!attach_location) { getLocation(); } mPreferences.edit().putBoolean(PREFERENCE_KEY_ATTACH_LOCATION, !attach_location).commit(); setMenu(); break; } case MENU_DRAFTS: { startActivity(new Intent(INTENT_ACTION_DRAFTS)); break; } case MENU_DELETE: { if (mImageUri == null) return false; removeAttachments(); break; } case MENU_EDIT: { if (mImageUri == null) return false; final Intent intent = new Intent(INTENT_ACTION_EXTENSION_EDIT_IMAGE); intent.setData(mImageUri); startActivityForResult(Intent.createChooser(intent, getString(R.string.open_with_extensions)), REQUEST_EDIT_IMAGE); // Create the intent needed to start feather /*Intent newIntent = new Intent( this, FeatherActivity.class ); // set the source image uri newIntent.setData( mImageUri ); // pass the required api key ( http://developers.aviary.com/ ) newIntent.putExtra( "API_KEY", AVIARY_SDK_API_KEY ); // pass the uri of the destination image file (optional) // This will be the same uri you will receive in the onActivityResult //newIntent.putExtra( "output", mImageUri); // format of the destination image (optional) newIntent.putExtra( "output-format", Bitmap.CompressFormat.JPEG.name() ); // output format quality (optional) newIntent.putExtra( "output-quality", 85 ); // you can force feather to display only a certain tools // newIntent.putExtra( "tools-list", new String[]{"ADJUST", "BRIGHTNESS" } ); // enable fast rendering preview newIntent.putExtra( "effect-enable-fast-preview", true ); // limit the image size // You can pass the current display size as max image size because after // the execution of Aviary you can save the HI-RES image so you don't need a big // image for the preview // newIntent.putExtra( "max-image-size", 800 ); // you want to hide the exit alert dialog shown when back is pressed // without saving image first // newIntent.putExtra( "hide-exit-unsave-confirmation", true ); // ..and start feather startActivityForResult( newIntent, ACTION_REQUEST_FEATHER );*/ break; } case MENU_VIEW: { openImage(this, mImageUri, false); break; } case MENU_TOGGLE_SENSITIVE: { final boolean has_media = (mIsImageAttached || mIsPhotoAttached) && mImageUri != null; if (!has_media) return false; mIsPossiblySensitive = !mIsPossiblySensitive; setMenu(); break; } } return true; } @Override public boolean onOptionsItemSelected(final MenuItem item) { switch (item.getItemId()) { case MENU_HOME: { final String text = mEditText != null ? parseString(mEditText.getText()) : null; if (mContentModified && !isNullOrEmpty(text)) { mUnsavedTweetDialogFragment = (DialogFragment) Fragment.instantiate(this, UnsavedTweetDialogFragment.class.getName()); final Bundle args = new Bundle(); args.putBoolean(INTENT_KEY_IS_NAVIGATE_UP, true); mUnsavedTweetDialogFragment.setArguments(args); mUnsavedTweetDialogFragment.show(getSupportFragmentManager(), "unsaved_tweet"); } else { NavUtils.navigateUpFromSameTask(this); } break; } case MENU_SEND: { send(); break; } case MENU_SELECT_ACCOUNT: { final Intent intent = new Intent(INTENT_ACTION_SELECT_ACCOUNT); final Bundle bundle = new Bundle(); bundle.putBoolean(INTENT_KEY_ACTIVATED_ONLY, true); bundle.putLongArray(INTENT_KEY_IDS, mAccountIds); intent.putExtras(bundle); startActivityForResult(intent, REQUEST_SELECT_ACCOUNT); break; } } return super.onOptionsItemSelected(item); } protected void send() { final String text_orig = mEditText != null ? parseString(mEditText.getText()) : null; final String atext = mIsPhotoAttached || mIsImageAttached ? mUploadUseExtension ? getImageUploadStatus(this, FAKE_IMAGE_LINK, text_orig) : text_orig + " " + FAKE_IMAGE_LINK : text_orig; final int count = mValidator.getTweetLength(atext); if (!isNullOrEmpty(mUploadProvider) && (mIsPhotoAttached || mIsImageAttached)) { postMedia(); } else if (count > 140) { if (mScheduleDate != null) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.schedule_tweet)); builder.setMessage(getString(R.string.schedule_tweet_too_long)); builder.setCancelable(true); AlertDialog alert = builder.create(); alert.show(); } else { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.tweet_too_long)); builder.setMessage(getString(R.string.confirm_twitlonger)); builder.setCancelable(true); builder.setPositiveButton(getString(R.string.send_to_twitlonger), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { postTwitlonger(); } }); builder.setNeutralButton(getString(R.string.split_tweets), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { splitTweets(); } }); builder.setNegativeButton(getString(R.string.cancel), new DialogInterface.OnClickListener() { @Override public void onClick(DialogInterface dialog, int id) { //actuallyPost(); } }); // AlertDialog alert = builder.create(); alert.show(); } } else { if (mScheduleDate != null && isNullOrEmpty(mUploadProvider) && (mIsPhotoAttached || mIsImageAttached)) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.schedule_tweet)); builder.setMessage(getString(R.string.schedule_tweet_pic_twitter)); builder.setCancelable(true); AlertDialog alert = builder.create(); alert.show(); } else if (mScheduleDate != null && mIsBuffer == true) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.schedule_tweet)); builder.setMessage(getString(R.string.schedule_tweet_buffer)); builder.setCancelable(true); AlertDialog alert = builder.create(); alert.show(); } else if (mIsBuffer == true && isNullOrEmpty(mUploadProvider) && (mIsPhotoAttached || mIsImageAttached)) { AlertDialog.Builder builder = new AlertDialog.Builder(this); builder.setTitle(getString(R.string.add_to_buffer)); builder.setMessage(getString(R.string.buffer_pic_twitter)); builder.setCancelable(true); AlertDialog alert = builder.create(); alert.show(); } else { actuallyPost(); } } } protected void splitTweets() { String text = mEditText != null ? parseString(mEditText.getText()) : null; String[] chunks = text.split("\\s+"); String replyTo = null; if (chunks.length >= 1) { final String firstWord = chunks[0]; final char firstCharacter = firstWord.charAt(0); if (Character.toString(firstCharacter).equals("@")) { replyTo = firstWord; } } String currentPart = ""; tweetParts = new ArrayList<String>(); for (final String word : chunks) { int checkLength = 120; if (tweetParts.size() > 1 && replyTo != null) { checkLength = 120 - replyTo.length() + 1; } if (currentPart.length() + word.length() + 1 > checkLength) { if (tweetParts.size() >= 1 && replyTo != null) { tweetParts.add(replyTo + " " + currentPart); } else { tweetParts.add(currentPart); } currentPart = ""; } if (currentPart.equals("")) { currentPart = word; } else { currentPart = currentPart + " " + word; } } if (!currentPart.equals("")) { if (tweetParts.size() >= 1 && replyTo != null) { tweetParts.add(replyTo + " " + currentPart); } else { tweetParts.add(currentPart); } } if (tweetParts.size() >= 1) { for (int i = tweetParts.size(); i > 0; i--) { String part = tweetParts.get(i - 1); String postString = part + " (" + getString(R.string.split_part) + " " + Integer.toString(i) + ")"; final boolean attach_location = mPreferences.getBoolean(PREFERENCE_KEY_ATTACH_LOCATION, false); final boolean has_media = (mIsImageAttached || mIsPhotoAttached) && mImageUri != null; mService.updateStatus(mAccountIds, postString, attach_location ? mRecentLocation : null, mImageUri, mInReplyToStatusId, has_media && mIsPossiblySensitive, mIsPhotoAttached && !mIsImageAttached); } setResult(Activity.RESULT_OK); finish(); } } protected void actuallyPost() { final String text = mEditText != null ? parseString(mEditText.getText()) : null; final boolean attach_location = mPreferences.getBoolean(PREFERENCE_KEY_ATTACH_LOCATION, false); if (mScheduleDate != null) { mService.scheduleStatus(mScheduleDate, mAccountIds, text, attach_location ? mRecentLocation : null, mImageUri, mInReplyToStatusId, mIsPhotoAttached && !mIsImageAttached); } else if (mIsBuffer == true) { mService.bufferStatus(mAccountIds, text); } else { final boolean has_media = (mIsImageAttached || mIsPhotoAttached) && mImageUri != null; mService.updateStatus(mAccountIds, text, attach_location ? mRecentLocation : null, mImageUri, mInReplyToStatusId, has_media && mIsPossiblySensitive, mIsPhotoAttached && !mIsImageAttached); } setResult(Activity.RESULT_OK); finish(); } protected void postMedia() { postMedia(false); } protected void postMedia(boolean immediate_upload) { final String image_path = getImagePathFromUri(this, mImageUri); //final File image_file = image_path != null ? new File(image_path) : null; UploadMediaTask task = new UploadMediaTask(); task.setUploadImmediately(immediate_upload); long accountId = 0; if (mAccountIds != null && mAccountIds.length > 0) { accountId = mAccountIds[0]; } final AccessToken accessToken = getTwitterAccessToken(this, accountId); task.setAccessToken(accessToken); task.execute(image_path); } private class UploadMediaTask extends AsyncTask<String, Integer, String> { private ProgressDialog dialog; private AccessToken accessToken; private boolean uploadImmediately = false; public void setAccessToken(AccessToken accessToken) { this.accessToken = accessToken; } public void setUploadImmediately(boolean uploadImmediately) { this.uploadImmediately = uploadImmediately; } // can use UI thread here protected void onPreExecute() { dialog = new ProgressDialog(ComposeActivity.this); dialog.setMessage(getString(R.string.uploading_media)); dialog.setIndeterminate(false); dialog.setCancelable(false); dialog.setProgressStyle(ProgressDialog.STYLE_HORIZONTAL); dialog.setProgress(0); dialog.show(); } // automatically done on worker thread (separate from UI thread) protected String doInBackground(final String... args) { String path = args[0]; uploadTimer = new Timer(); uploadTimer.schedule(new TimerTask() { @Override public void run() { UploadProgressMethod(dialog); } }, 0, 50); String url = null; String consumer_key = mPreferences.getString(PREFERENCE_KEY_CONSUMER_KEY, CONSUMER_KEY); String consumer_secret = mPreferences.getString(PREFERENCE_KEY_CONSUMER_SECRET, CONSUMER_SECRET); if (isNullOrEmpty(consumer_key) || isNullOrEmpty(consumer_secret)) { consumer_key = CONSUMER_KEY; consumer_secret = CONSUMER_SECRET; } if (mUploadProvider.equals("twitpic")) { ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setMediaProviderAPIKey(TWITPIC_API_KEY); cb.setOAuthConsumerKey(consumer_key); cb.setOAuthConsumerSecret(consumer_secret); cb.setOAuthAccessToken(accessToken.getToken()); cb.setOAuthAccessTokenSecret(accessToken.getTokenSecret()); Configuration conf = cb.build(); upload = new ImageUploadFactory(conf).getInstance(MediaProvider.TWITPIC); } else if (mUploadProvider.equals("yfrog")) { ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setMediaProviderAPIKey(YFROG_API_KEY); cb.setOAuthConsumerKey(consumer_key); cb.setOAuthConsumerSecret(consumer_secret); cb.setOAuthAccessToken(accessToken.getToken()); cb.setOAuthAccessTokenSecret(accessToken.getTokenSecret()); Configuration conf = cb.build(); upload = new ImageUploadFactory(conf).getInstance(MediaProvider.YFROG); } else if (mUploadProvider.equals("imgly")) { ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setOAuthConsumerKey(consumer_key); cb.setOAuthConsumerSecret(consumer_secret); cb.setOAuthAccessToken(accessToken.getToken()); cb.setOAuthAccessTokenSecret(accessToken.getTokenSecret()); Configuration conf = cb.build(); upload = new ImageUploadFactory(conf).getInstance(MediaProvider.IMG_LY); } else if (mUploadProvider.equals("lockerz")) { ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setMediaProviderAPIKey(PLIXI_API_KEY); cb.setOAuthConsumerKey(consumer_key); cb.setOAuthConsumerSecret(consumer_secret); cb.setOAuthAccessToken(accessToken.getToken()); cb.setOAuthAccessTokenSecret(accessToken.getTokenSecret()); Configuration conf = cb.build(); upload = new ImageUploadFactory(conf).getInstance(MediaProvider.LOCKERZ); } else if (mUploadProvider.equals("moby")) { ConfigurationBuilder cb = new ConfigurationBuilder(); cb.setMediaProviderAPIKey(MOBY_API_KEY); cb.setOAuthConsumerKey(consumer_key); cb.setOAuthConsumerSecret(consumer_secret); cb.setOAuthAccessToken(accessToken.getToken()); cb.setOAuthAccessTokenSecret(accessToken.getTokenSecret()); Configuration conf = cb.build(); upload = new ImageUploadFactory(conf).getInstance(MediaProvider.MOBYPICTURE); } try { url = upload.upload(new File(path)); } catch (TwitterException te) { te.printStackTrace(); } if (uploadTimer != null) { uploadTimer.cancel(); } return url; } protected void onProgressUpdate(Integer... progress) { } // can use UI thread here protected void onPostExecute(final String result) { if (dialog.isShowing()) { dialog.dismiss(); } if (result == null) { Dialog superSimpleDlg = new Dialog(ComposeActivity.this); superSimpleDlg.setTitle(R.string.error_upload); superSimpleDlg.show(); } else { ComposeActivity.this.uploadComplete(result); boolean isAutoUpload = mPreferences.getBoolean(PREFERENCE_KEY_AUTO_UPLOAD, false); if (!isAutoUpload && !uploadImmediately) { ComposeActivity.this.send(); } } } } public void uploadComplete(String text) { String oldText = parseString(mEditText.getText()); mEditText.setText(oldText + " " + text); removeAttachments(); } private void UploadProgressMethod(final ProgressDialog dialog) { //This method is called directly by the timer //and runs in the same thread as the timer. //We call the method that will work with the UI //through the runOnUiThread method. this.runOnUiThread(new Runnable() { public void run() { // use data here if (upload != null && upload.getRequestLength() > 0) { float progress = (float) upload.getRequestStatus() / (float) upload.getRequestLength(); float longPc = progress * 100; int percentage = Math.round(longPc); if (percentage >= 100) { dialog.setProgress(100); dialog.setIndeterminate(true); dialog.setMessage(getString(R.string.uploading_media_wait)); } else { dialog.setProgress(percentage); } } } }); } private class UrlShortenerTask extends AsyncTask<String, Void, String> { private ProgressDialog dialog; // can use UI thread here protected void onPreExecute() { dialog = new ProgressDialog(ComposeActivity.this); dialog.setMessage(getString(R.string.shortening)); dialog.setIndeterminate(true); dialog.setCancelable(false); dialog.show(); } // automatically done on worker thread (separate from UI thread) protected String doInBackground(final String... args) { String text = ComposeActivity.this.doShrink(args[0]); return text; } // can use UI thread here protected void onPostExecute(final String result) { if (dialog.isShowing()) { dialog.dismiss(); } if (result == null) { Dialog superSimpleDlg = new Dialog(ComposeActivity.this); superSimpleDlg.setTitle(R.string.error_shorten); superSimpleDlg.show(); } else { ComposeActivity.this.urlShortenerComplete(result); } } } public void urlShortenerComplete(String text) { //String s = parseString(mEditText.getText()); mEditText.setText(text); } private final void gatherLinks(ArrayList<Hyperlink> links, Spannable s, Pattern pattern) { // Matcher matching the pattern Matcher m = pattern.matcher(s); while (m.find()) { int start = m.start(); int end = m.end(); /* * Hyperlink is basically used like a structure for storing the information about * where the link was found. */ Hyperlink spec = new Hyperlink(); spec.textSpan = s.subSequence(start, end); spec.span = new InternalURLSpan(spec.textSpan.toString()); spec.start = start; spec.end = end; links.add(spec); } } public class InternalURLSpan extends ClickableSpan { private String clickedSpan; public InternalURLSpan(String clickedString) { clickedSpan = clickedString; } @Override public void onClick(View textView) { } } /* * Class for storing the information about the Link Location */ class Hyperlink { CharSequence textSpan; InternalURLSpan span; int start; int end; } private String doShrink(String text) { ArrayList<Hyperlink> listOfLinks = new ArrayList<Hyperlink>(); SpannableString linkableText = new SpannableString(text); Pattern hyperLinksPattern = Pattern .compile("([Hh][tT][tT][pP][sS]?:\\/\\/[^ ,'\">\\]\\)]*[^\\. ,'\">\\]\\)])"); gatherLinks(listOfLinks, linkableText, hyperLinksPattern); for (int i = 0; i < listOfLinks.size(); i++) { Hyperlink link = listOfLinks.get(i); final String provider = mPreferences.getString(PREFERENCE_KEY_URL_SHORTENER, "tinyurl"); if (!link.textSpan.toString().toLowerCase().contains("http://tl.gd") && !link.textSpan.toString().toLowerCase().contains("http://bit.ly") && !link.textSpan.toString().toLowerCase().contains("http://t.co") && !link.textSpan.toString().toLowerCase().contains("http://tinyurl.com") && !link.textSpan.toString().toLowerCase().contains("http://is.gd") && !link.textSpan.toString().toLowerCase().contains("http://tr.im") && !link.textSpan.toString().toLowerCase().contains("http://twitpic.com") && !link.textSpan.toString().toLowerCase().contains("http://yfrog.com") && !link.textSpan.toString().toLowerCase().contains("http://pic.gd") && !link.textSpan.toString().toLowerCase().contains("http://lockerz.com") && !link.textSpan.toString().toLowerCase().contains("http://twitgoo.com") && !link.textSpan.toString().toLowerCase().contains("http://twitrpix.com") && !link.textSpan.toString().toLowerCase().contains("http://img.ly") && !link.textSpan.toString().toLowerCase().contains("http://j.mp") && !link.textSpan.toString().toLowerCase().contains("http://cli.cs")) { try { String finalUrl = ""; if (provider.equals("googl")) { HttpClient httpClient = new DefaultHttpClient(); HttpPost postRequest = new HttpPost("https://www.googleapis.com/urlshortener/v1/url"); postRequest.addHeader("Content-Type", "application/json"); StringEntity reqEntity = new StringEntity( "{\"longUrl\": \"" + link.textSpan.toString() + "\"}", HTTP.UTF_8); postRequest.setEntity(reqEntity); HttpResponse response = httpClient.execute(postRequest); BufferedReader reader = new BufferedReader( new InputStreamReader(response.getEntity().getContent(), "UTF-8")); String sResponse; StringBuilder s = new StringBuilder(); while ((sResponse = reader.readLine()) != null) { s = s.append(sResponse); } JSONObject jObject = new JSONObject(s.toString()); text = text.replace(link.textSpan.toString(), jObject.getString("id")); } else { if (provider.equals("tinyurl")) { finalUrl = "http://tinyurl.com/api-create.php?url=" + URLEncoder.encode(link.textSpan.toString()); } else if (provider.equals("isgd")) { finalUrl = "http://is.gd/api.php?longurl=" + URLEncoder.encode(link.textSpan.toString()); } else if (provider.equals("bitly")) { finalUrl = "http://api.bitly.com/v3/shorten?format=txt&login=" + BITLY_USER + "&apiKey=" + BITLY_API_KEY + "&longUrl=" + URLEncoder.encode(link.textSpan.toString()); } else if (provider.equals("jmp")) { finalUrl = "http://api.bitly.com/v3/shorten?domain=j.mp&format=txt&login=" + BITLY_USER + "&apiKey=" + BITLY_API_KEY + "&longUrl=" + URLEncoder.encode(link.textSpan.toString()); } else if (provider.equals("vgd")) { finalUrl = "http://v.gd/create.php?format=simple&url=" + URLEncoder.encode(link.textSpan.toString()); } else if (provider.equals("trim")) { finalUrl = "http://api.tr.im/v1/trim_simple?url=" + URLEncoder.encode(link.textSpan.toString()); } else if (provider.equals("googl")) { finalUrl = "http://v.gd/create.php?format=simple&url=" + URLEncoder.encode(link.textSpan.toString()); } HttpClient httpclient = new DefaultHttpClient(); HttpResponse response = httpclient.execute(new HttpGet(finalUrl)); BufferedReader reader = new BufferedReader( new InputStreamReader(response.getEntity().getContent(), "UTF-8")); String sResponse; StringBuilder s = new StringBuilder(); while ((sResponse = reader.readLine()) != null) { s = s.append(sResponse); } text = text.replace(link.textSpan.toString(), s.toString()); } } catch (Exception e) { Toast.makeText(getApplicationContext(), "Network exception" + e.getMessage(), Toast.LENGTH_SHORT).show(); } } } return text; } private class TwitlongerTask extends AsyncTask<String, Void, String> { //private final ProgressDialog dialog = new ProgressDialog(ComposeActivity.this); private ProgressDialog dialog; // can use UI thread here protected void onPreExecute() { dialog = new ProgressDialog(ComposeActivity.this); dialog.setMessage(getString(R.string.twitlongering)); dialog.setIndeterminate(true); dialog.setCancelable(false); dialog.show(); } // automatically done on worker thread (separate from UI thread) protected String doInBackground(final String... args) { String text = ComposeActivity.this.uploadTwitlonger(args[0]); return text; } // can use UI thread here protected void onPostExecute(final String result) { if (dialog.isShowing()) { dialog.dismiss(); } if (result == null) { Dialog superSimpleDlg = new Dialog(ComposeActivity.this); superSimpleDlg.setTitle(R.string.error_upload_twitlonger); superSimpleDlg.show(); } else { ComposeActivity.this.twitlongerComplete(result); ComposeActivity.this.actuallyPost(); } } } private String uploadTwitlonger(String text) { String finalUrl = "http://www.twitlonger.com/api_post/"; final String atext = parseString(mEditText.getText()); String screen_name = null; if (mAccountIds != null && mAccountIds.length > 0) { screen_name = getAccountUsername(this, mAccountIds[0]); } try { HttpClient httpclient = new DefaultHttpClient(); HttpPost postRequest = new HttpPost(finalUrl); MultipartEntity reqEntity = new MultipartEntity(HttpMultipartMode.BROWSER_COMPATIBLE); reqEntity.addPart("username", new StringBody(screen_name)); reqEntity.addPart("application", new StringBody(TWIT_LONGER_USER)); reqEntity.addPart("api_key", new StringBody(TWIT_LONGER_API_KEY)); reqEntity.addPart("message", new StringBody(atext, Charset.forName("UTF-8"))); postRequest.setEntity(reqEntity); HttpResponse response = httpclient.execute(postRequest); DocumentBuilderFactory domFactory = DocumentBuilderFactory.newInstance(); domFactory.setNamespaceAware(true); // never forget this! DocumentBuilder builder = domFactory.newDocumentBuilder(); Document doc = builder.parse(response.getEntity().getContent()); XPathFactory factory = XPathFactory.newInstance(); XPath xpath = factory.newXPath(); XPathExpression expr; expr = xpath.compile("//twitlonger/post/content/text()"); Object result = expr.evaluate(doc, XPathConstants.STRING); Log.d("ComposeActivity.uploadTwitlonger", "path: " + atext + " " + result.toString()); return result.toString(); } catch (Exception e) { //Toast.makeText(getApplicationContext(), "Network exception" + e.getMessage(), Toast.LENGTH_SHORT).show(); } return null; } protected void postTwitlonger() { final String text = mEditText != null ? parseString(mEditText.getText()) : null; new TwitlongerTask().execute(text); } public void twitlongerComplete(String text) { mEditText.setText(text); } @Override public boolean onPrepareOptionsMenu(final Menu menu) { if (menu == null || mEditText == null || mTextCount == null) return false; final String text_orig = parseString(mEditText.getText()); final String text = mIsPhotoAttached || mIsImageAttached ? mUploadUseExtension ? getImageUploadStatus(this, FAKE_IMAGE_LINK, text_orig) : text_orig + " " + FAKE_IMAGE_LINK : text_orig; final int count = mValidator.getTweetLength(text); final float hue = count < 140 ? count >= 130 ? 5 * (140 - count) : 50 : 0; final float[] hsv = new float[] { hue, 1.0f, 1.0f }; mTextCount.setTextColor(count >= 130 ? Color.HSVToColor(0x80, hsv) : 0x80808080); mTextCount.setText(parseString(140 - count)); final MenuItem sendItem = menu.findItem(MENU_SEND); if (sendItem != null) { sendItem.setEnabled(text_orig.length() > 0); } return super.onPrepareOptionsMenu(menu); } @Override public void onProviderDisabled(final String provider) { } @Override public void onProviderEnabled(final String provider) { } @Override public void onSaveInstanceState(final Bundle outState) { mText = parseString(mEditText.getText()); outState.putLongArray(INTENT_KEY_IDS, mAccountIds); outState.putString(INTENT_KEY_TEXT, mText); outState.putLong(INTENT_KEY_IN_REPLY_TO_ID, mInReplyToStatusId); outState.putString(INTENT_KEY_IN_REPLY_TO_NAME, mInReplyToName); outState.putString(INTENT_KEY_IN_REPLY_TO_SCREEN_NAME, mInReplyToScreenName); outState.putBoolean(INTENT_KEY_IS_QUOTE, mIsQuote); outState.putBoolean(INTENT_KEY_IS_IMAGE_ATTACHED, mIsImageAttached); outState.putBoolean(INTENT_KEY_IS_PHOTO_ATTACHED, mIsPhotoAttached); outState.putParcelable(INTENT_KEY_IMAGE_URI, mImageUri); outState.putBoolean(INTENT_KEY_CONTENT_MODIFIED, mContentModified); outState.putBoolean(INTENT_KEY_IS_POSSIBLY_SENSITIVE, mIsPossiblySensitive); super.onSaveInstanceState(outState); } @Override public void onStart() { super.onStart(); final String component = mPreferences.getString(PREFERENCE_KEY_IMAGE_UPLOADER, null); mUploadUseExtension = !isNullOrEmpty(component); if (mMenuBar != null) { setMenu(); } } @Override public void onStatusChanged(final String provider, final int status, final Bundle extras) { } @Override public void onTextChanged(final CharSequence s, final int start, final int before, final int count) { invalidateSupportOptionsMenu(); mContentModified = true; } public void saveToDrafts() { final String text = mEditText != null ? parseString(mEditText.getText()) : null; final ContentValues values = new ContentValues(); values.put(Drafts.TEXT, text); values.put(Drafts.ACCOUNT_IDS, ArrayUtils.toString(mAccountIds, ',', false)); values.put(Drafts.IN_REPLY_TO_STATUS_ID, mInReplyToStatusId); values.put(Drafts.IN_REPLY_TO_NAME, mInReplyToName); values.put(Drafts.IN_REPLY_TO_SCREEN_NAME, mInReplyToScreenName); values.put(Drafts.IS_QUOTE, mIsQuote ? 1 : 0); values.put(Drafts.IS_QUEUED, false); values.put(Drafts.IS_POSSIBLY_SENSITIVE, mIsPossiblySensitive ? 1 : 0); if (mImageUri != null) { values.put(Drafts.IS_IMAGE_ATTACHED, mIsImageAttached); values.put(Drafts.IS_PHOTO_ATTACHED, mIsPhotoAttached); values.put(Drafts.IMAGE_URI, parseString(mImageUri)); } mResolver.insert(Drafts.CONTENT_URI, values); } @Override public void onStop() { if (mPopupMenu != null) { mPopupMenu.dismiss(); } super.onStop(); } /** * The Location Manager manages location providers. This code searches for * the best provider of data (GPS, WiFi/cell phone tower lookup, some other * mechanism) and finds the last known location. **/ private boolean getLocation() { final Criteria criteria = new Criteria(); criteria.setAccuracy(Criteria.ACCURACY_FINE); final String provider = mLocationManager.getBestProvider(criteria, true); if (provider != null) { mRecentLocation = mLocationManager.getLastKnownLocation(provider); } else { Toast.makeText(this, R.string.cannot_get_location, Toast.LENGTH_SHORT).show(); } return provider != null; } private void pickImage() { final Intent i = new Intent(Intent.ACTION_PICK, MediaStore.Images.Media.EXTERNAL_CONTENT_URI); try { startActivityForResult(i, REQUEST_PICK_IMAGE); } catch (final ActivityNotFoundException e) { showErrorToast(this, null, e, false); } } private void reloadAttachedImageThumbnail(final File file) { if (mAttachedImageThumbnailTask != null && mAttachedImageThumbnailTask.getStatus() == AsyncTask.Status.RUNNING) { mAttachedImageThumbnailTask.cancel(true); } mAttachedImageThumbnailTask = new AttachedImageThumbnailTask(file); mAttachedImageThumbnailTask.execute(); } private void setMenu() { final Menu menu = mMenuBar.getMenu(); final int activated_color = getResources().getColor(R.color.holo_blue_bright); final MenuItem itemAddImage = menu.findItem(MENU_ADD_IMAGE); final MenuItem itemLibrary = menu.findItem(MENU_LIBRARY_MENU); final Drawable iconAddImage = itemAddImage.getIcon().mutate(); final Drawable iconLibrary = itemLibrary.getIcon().mutate(); boolean menuSelected = false; if (mIsImageAttached && !mIsPhotoAttached) { iconAddImage.setColorFilter(activated_color, Mode.MULTIPLY); itemAddImage.setTitle(R.string.remove_image); menuSelected = true; } else { iconAddImage.clearColorFilter(); itemAddImage.setTitle(R.string.add_image); } final MenuItem itemTakePhoto = menu.findItem(MENU_TAKE_PHOTO); final Drawable iconTakePhoto = itemTakePhoto.getIcon().mutate(); if (!mIsImageAttached && mIsPhotoAttached) { iconTakePhoto.setColorFilter(activated_color, Mode.MULTIPLY); itemTakePhoto.setTitle(R.string.remove_photo); menuSelected = true; } else { iconTakePhoto.clearColorFilter(); itemTakePhoto.setTitle(R.string.take_photo); } if (menuSelected == true) { iconLibrary.setColorFilter(activated_color, Mode.MULTIPLY); } else { iconLibrary.clearColorFilter(); } final MenuItem itemAttachLocation = menu.findItem(MENU_ADD_LOCATION); final Drawable iconAttachLocation = itemAttachLocation.getIcon().mutate(); final boolean attach_location = mPreferences.getBoolean(PREFERENCE_KEY_ATTACH_LOCATION, false); if (attach_location && getLocation()) { iconAttachLocation.setColorFilter(activated_color, Mode.MULTIPLY); itemAttachLocation.setTitle(R.string.remove_location); } else { mPreferences.edit().putBoolean(PREFERENCE_KEY_ATTACH_LOCATION, false).commit(); iconAttachLocation.clearColorFilter(); itemAttachLocation.setTitle(R.string.add_location); } final MenuItem itemMore = menu.findItem(R.id.more_submenu); boolean moreHighlighted = false; if (itemMore != null) { final MenuItem itemDrafts = menu.findItem(R.id.drafts); final MenuItem itemToggleSensitive = menu.findItem(MENU_TOGGLE_SENSITIVE); if (itemMore != null) { if (itemDrafts != null) { final Cursor drafts_cur = getContentResolver().query(Drafts.CONTENT_URI, new String[0], null, null, null); final Drawable iconMore = itemMore.getIcon().mutate(); final Drawable iconDrafts = itemDrafts.getIcon().mutate(); if (drafts_cur.getCount() > 0) { moreHighlighted = true; iconDrafts.setColorFilter(activated_color, Mode.MULTIPLY); } else { iconDrafts.clearColorFilter(); } drafts_cur.close(); } if (itemToggleSensitive != null) { final boolean has_media = (mIsImageAttached || mIsPhotoAttached) && mImageUri != null; if (has_media && isNullOrEmpty(mUploadProvider)) { itemToggleSensitive.setVisible(true); } else { itemToggleSensitive.setVisible(false); } if (has_media && isNullOrEmpty(mUploadProvider)) { final Drawable iconToggleSensitive = itemToggleSensitive.getIcon().mutate(); if (mIsPossiblySensitive) { moreHighlighted = true; iconToggleSensitive.setColorFilter(activated_color, Mode.MULTIPLY); } else { iconToggleSensitive.clearColorFilter(); } } } } } if (mScheduleDate != null) { final MenuItem itemSchedule = menu.findItem(R.id.schedule_tweet); final Drawable iconSchedule = itemSchedule.getIcon().mutate(); iconSchedule.setColorFilter(activated_color, Mode.MULTIPLY); moreHighlighted = true; itemSchedule.setTitle(getString(R.string.schedule_clear)); } else { final MenuItem itemSchedule = menu.findItem(R.id.schedule_tweet); final Drawable iconSchedule = itemSchedule.getIcon().mutate(); iconSchedule.clearColorFilter(); itemSchedule.setTitle(getString(R.string.schedule_tweet)); } final String consumer_key = mPreferences.getString(PREFERENCE_KEY_CONSUMER_KEY, null); final String consumer_secret = mPreferences.getString(PREFERENCE_KEY_CONSUMER_SECRET, null); if (!isNullOrEmpty(consumer_key) && !isNullOrEmpty(consumer_secret)) { final MenuItem itemSchedule = menu.findItem(R.id.schedule_tweet); itemSchedule.setVisible(false); } final MenuItem bufferItem = menu.findItem(MENU_ADD_TO_BUFFER); if (bufferItem != null) { final String buffer_authorised = mPreferences.getString(PREFERENCE_KEY_BUFFERAPP_ACCESS_TOKEN, null); final Drawable iconBuffer = bufferItem.getIcon().mutate(); if (buffer_authorised != null && !buffer_authorised.equals("")) { bufferItem.setVisible(true); if (mIsBuffer) { iconBuffer.setColorFilter(activated_color, Mode.MULTIPLY); moreHighlighted = true; } else { iconBuffer.clearColorFilter(); } } else { bufferItem.setVisible(false); mIsBuffer = false; } } if (itemMore != null) { final Drawable iconMore = itemMore.getIcon().mutate(); if (moreHighlighted == true) { iconMore.setColorFilter(activated_color, Mode.MULTIPLY); } else { iconMore.clearColorFilter(); } } mMenuBar.invalidate(); invalidateSupportOptionsMenu(); } private void takePhoto() { final Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE); if (getExternalStorageState().equals(Environment.MEDIA_MOUNTED)) { final File cache_dir = EnvironmentAccessor.getExternalCacheDir(this); final File file = new File(cache_dir, "tmp_photo_" + System.currentTimeMillis() + ".jpg"); mImageUri = Uri.fromFile(file); intent.putExtra(MediaStore.EXTRA_OUTPUT, mImageUri); try { startActivityForResult(intent, REQUEST_TAKE_PHOTO); } catch (final ActivityNotFoundException e) { showErrorToast(this, null, e, false); } } } public static class UnsavedTweetDialogFragment extends BaseDialogFragment implements DialogInterface.OnClickListener { private boolean mIsNavigateUp; @Override public void onActivityCreated(Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); final Bundle args = getArguments(); if (args != null) { mIsNavigateUp = args.getBoolean(INTENT_KEY_IS_NAVIGATE_UP); } } @Override public void onClick(final DialogInterface dialog, final int which) { final FragmentActivity activity = getActivity(); switch (which) { case DialogInterface.BUTTON_POSITIVE: { if (activity instanceof ComposeActivity) { ((ComposeActivity) activity).saveToDrafts(); if (mIsNavigateUp) { NavUtils.navigateUpFromSameTask(activity); } else { activity.finish(); } } break; } case DialogInterface.BUTTON_NEGATIVE: { if (mIsNavigateUp) { NavUtils.navigateUpFromSameTask(activity); } else { activity.finish(); } break; } } } @Override public Dialog onCreateDialog(final Bundle savedInstanceState) { final AlertDialog.Builder builder = new AlertDialog.Builder(getActivity()); builder.setMessage(R.string.unsaved_tweet); builder.setPositiveButton(R.string.save, this); builder.setNeutralButton(android.R.string.cancel, null); builder.setNegativeButton(R.string.discard, this); return builder.create(); } } class AttachedImageThumbnailTask extends AsyncTask<Void, Void, Bitmap> { private final File file; public AttachedImageThumbnailTask(final File file) { this.file = file; } @Override protected Bitmap doInBackground(final Void... args) { if (file != null && file.exists()) { final int thumbnail_size_px = (int) (THUMBNAIL_SIZE * getResources().getDisplayMetrics().density); final BitmapFactory.Options o = new BitmapFactory.Options(); o.inJustDecodeBounds = true; BitmapFactory.decodeFile(file.getPath(), o); final int tmp_width = o.outWidth; final int tmp_height = o.outHeight; if (tmp_width == 0 || tmp_height == 0) return null; final BitmapFactory.Options o2 = new BitmapFactory.Options(); o2.inSampleSize = Math.round(Math.max(tmp_width, tmp_height) / thumbnail_size_px); return BitmapFactory.decodeFile(file.getPath(), o2); } return null; } @Override protected void onPostExecute(final Bitmap result) { mImageThumbnailPreview.setVisibility(result != null ? View.VISIBLE : View.GONE); mImageThumbnailPreview.setImageBitmap(result); super.onPostExecute(result); } } }