org.mariotaku.twidere.activity.support.ComposeActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.mariotaku.twidere.activity.support.ComposeActivity.java

Source

/*
 *             Twidere - Twitter client for Android
 * 
 *  Copyright (C) 2012-2014 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 org.mariotaku.twidere.activity.support;

import android.annotation.TargetApi;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.Dialog;
import android.app.NotificationManager;
import android.app.PendingIntent;
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.content.res.Resources;
import android.graphics.Bitmap;
import android.graphics.PorterDuff.Mode;
import android.graphics.Rect;
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.Parcelable;
import android.support.annotation.NonNull;
import android.support.v4.app.DialogFragment;
import android.support.v4.app.NotificationCompat;
import android.support.v4.app.NotificationCompat.Action;
import android.support.v4.util.LongSparseArray;
import android.support.v7.internal.view.SupportMenuInflater;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.ActionMenuView.OnMenuItemClickListener;
import android.support.v7.widget.FixedLinearLayoutManager;
import android.support.v7.widget.LinearLayoutManager;
import android.support.v7.widget.RecyclerView;
import android.support.v7.widget.RecyclerView.Adapter;
import android.support.v7.widget.RecyclerView.ItemDecoration;
import android.support.v7.widget.RecyclerView.State;
import android.support.v7.widget.RecyclerView.ViewHolder;
import android.text.Editable;
import android.text.Spannable;
import android.text.Spanned;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.text.style.ImageSpan;
import android.util.Log;
import android.view.ActionMode;
import android.view.ActionMode.Callback;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.MotionEvent;
import android.view.View;
import android.view.View.OnClickListener;
import android.view.View.OnLongClickListener;
import android.view.ViewGroup;
import android.view.Window;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;

import com.github.johnpersano.supertoasts.SuperToast;
import com.github.johnpersano.supertoasts.SuperToast.Duration;
import com.github.johnpersano.supertoasts.SuperToast.OnDismissListener;
import com.nostra13.universalimageloader.utils.IoUtils;
import com.twitter.Extractor;

import org.mariotaku.dynamicgridview.DraggableArrayAdapter;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.constant.SharedPreferenceConstants;
import org.mariotaku.twidere.fragment.support.BaseSupportDialogFragment;
import org.mariotaku.twidere.fragment.support.DraftsFragment;
import org.mariotaku.twidere.fragment.support.ViewStatusDialogFragment;
import org.mariotaku.twidere.model.DraftItem;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableLocation;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableMediaUpdate;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.ParcelableStatusUpdate;
import org.mariotaku.twidere.model.ParcelableUser;
import org.mariotaku.twidere.preference.ServicePickerPreference;
import org.mariotaku.twidere.provider.TwidereDataStore.Drafts;
import org.mariotaku.twidere.service.BackgroundOperationService;
import org.mariotaku.twidere.text.MarkForDeleteSpan;
import org.mariotaku.twidere.util.AsyncTaskUtils;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ContentValuesCreator;
import org.mariotaku.twidere.util.EditTextEnterHandler;
import org.mariotaku.twidere.util.EditTextEnterHandler.EnterListener;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.MathUtils;
import org.mariotaku.twidere.util.MediaLoaderWrapper;
import org.mariotaku.twidere.util.MenuUtils;
import org.mariotaku.twidere.util.ParseUtils;
import org.mariotaku.twidere.util.SharedPreferencesWrapper;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereArrayUtils;
import org.mariotaku.twidere.util.TwidereValidator;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.view.ActionIconView;
import org.mariotaku.twidere.view.BadgeView;
import org.mariotaku.twidere.view.ComposeEditText;
import org.mariotaku.twidere.view.ShapedImageView;
import org.mariotaku.twidere.view.StatusTextCountView;

import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.TreeSet;

public class ComposeActivity extends ThemedFragmentActivity
        implements LocationListener, OnMenuItemClickListener, OnClickListener, OnLongClickListener, Callback {

    // Constants
    private static final String FAKE_IMAGE_LINK = "https://www.example.com/fake_image.jpg";
    private static final String EXTRA_IS_POSSIBLY_SENSITIVE = "is_possibly_sensitive";
    private static final String EXTRA_SHOULD_SAVE_ACCOUNTS = "should_save_accounts";
    private static final String EXTRA_ORIGINAL_TEXT = "original_text";
    private static final String EXTRA_SHARE_SCREENSHOT = "share_screenshot";

    // Utility classes
    private final Extractor mExtractor = new Extractor();
    private TwidereValidator mValidator;
    private AsyncTwitterWrapper mTwitterWrapper;
    private LocationManager mLocationManager;
    private SharedPreferencesWrapper mPreferences;
    private ContentResolver mResolver;
    private AsyncTask<Object, Object, ?> mTask;
    private SupportMenuInflater mMenuInflater;

    // Views
    private GridView mMediaPreviewGrid;
    private ActionMenuView mMenuBar;
    private ComposeEditText mEditText;
    private View mSendView;
    private StatusTextCountView mSendTextCountView;
    private RecyclerView mAccountSelector;
    private View mAccountSelectorContainer;
    private DraftItem mDraftItem;
    private ShapedImageView mProfileImageView;
    private BadgeView mCountView;
    private View mAccountSelectorButton;
    private MediaLoaderWrapper mImageLoader;
    private View mLocationContainer;
    private ActionIconView mLocationIcon;
    private TextView mLocationText;

    // Adapters
    private MediaPreviewAdapter mMediaPreviewAdapter;
    private AccountIconsAdapter mAccountsAdapter;

    // Data fields
    private ParcelableLocation mRecentLocation;
    private ParcelableStatus mInReplyToStatus;
    private ParcelableUser mMentionUser;
    private String mOriginalText;
    private long mInReplyToStatusId;
    private boolean mIsPossiblySensitive, mShouldSaveAccounts;
    private boolean mImageUploaderUsed, mStatusShortenerUsed;
    private boolean mNavigateBackPressed;
    private boolean mTextChanged;

    @Override
    public int getThemeColor() {
        return ThemeUtils.getUserAccentColor(this);
    }

    @Override
    public int getThemeResourceId() {
        return ThemeUtils.getComposeThemeResource(this);
    }

    @Override
    public void onActivityResult(final int requestCode, final int resultCode, final Intent intent) {
        switch (requestCode) {
        case REQUEST_TAKE_PHOTO:
        case REQUEST_PICK_IMAGE:
        case REQUEST_OPEN_DOCUMENT: {
            if (resultCode == Activity.RESULT_OK) {
                final Uri src = intent.getData();
                mTask = AsyncTaskUtils.executeTask(
                        new AddMediaTask(this, src, createTempImageUri(), ParcelableMedia.TYPE_IMAGE, true));
            }
            break;
        }
        case REQUEST_EDIT_IMAGE: {
            if (resultCode == Activity.RESULT_OK) {
                final Uri uri = intent.getData();
                if (uri == null) {
                    break;
                }
                setMenu();
                updateTextCount();
            }
            break;
        }
        case REQUEST_EXTENSION_COMPOSE: {
            if (resultCode == Activity.RESULT_OK) {
                final String text = intent.getStringExtra(EXTRA_TEXT);
                final String append = intent.getStringExtra(EXTRA_APPEND_TEXT);
                final Uri imageUri = intent.getParcelableExtra(EXTRA_IMAGE_URI);
                if (text != null) {
                    mEditText.setText(text);
                } else if (append != null) {
                    mEditText.append(append);
                }
                if (imageUri != null) {
                }
                setMenu();
                updateTextCount();
            }
            break;
        }
        }

    }

    @Override
    public void onBackPressed() {
        if (mTask != null && mTask.getStatus() == AsyncTask.Status.RUNNING)
            return;
        final String text = mEditText != null ? ParseUtils.parseString(mEditText.getText()) : null;
        final boolean textChanged = text != null && !text.isEmpty() && !text.equals(mOriginalText);
        final boolean isEditingDraft = INTENT_ACTION_EDIT_DRAFT.equals(getIntent().getAction());
        if (textChanged || hasMedia() || isEditingDraft) {
            saveToDrafts();
            Toast.makeText(this, R.string.status_saved_to_draft, Toast.LENGTH_SHORT).show();
            finish();
        } else {
            mTask = AsyncTaskUtils.executeTask(new DiscardTweetTask(this));
        }
    }

    @Override
    public void onSaveInstanceState(final Bundle outState) {
        outState.putLongArray(EXTRA_ACCOUNT_IDS, mAccountsAdapter.getSelectedAccountIds());
        outState.putParcelableArrayList(EXTRA_MEDIA, new ArrayList<Parcelable>(getMediaList()));
        outState.putBoolean(EXTRA_IS_POSSIBLY_SENSITIVE, mIsPossiblySensitive);
        outState.putParcelable(EXTRA_STATUS, mInReplyToStatus);
        outState.putLong(EXTRA_STATUS_ID, mInReplyToStatusId);
        outState.putParcelable(EXTRA_USER, mMentionUser);
        outState.putParcelable(EXTRA_DRAFT, mDraftItem);
        outState.putBoolean(EXTRA_SHOULD_SAVE_ACCOUNTS, mShouldSaveAccounts);
        outState.putString(EXTRA_ORIGINAL_TEXT, mOriginalText);
        super.onSaveInstanceState(outState);
    }

    @Override
    protected void onStart() {
        super.onStart();
        mImageUploaderUsed = !ServicePickerPreference.isNoneValue(mPreferences.getString(KEY_MEDIA_UPLOADER, null));
        mStatusShortenerUsed = !ServicePickerPreference
                .isNoneValue(mPreferences.getString(KEY_STATUS_SHORTENER, null));
        startLocationUpdateIfEnabled();
        setMenu();
        updateTextCount();
        final int textSize = mPreferences.getInt(KEY_TEXT_SIZE, Utils.getDefaultTextSize(this));
        mEditText.setTextSize(textSize * 1.25f);
    }

    @Override
    protected void onStop() {
        saveAccountSelection();
        mLocationManager.removeUpdates(this);
        super.onStop();
    }

    @Override
    public void onClick(final View view) {
        switch (view.getId()) {
        case R.id.send: {
            if (isQuotingProtectedStatus()) {
                new RetweetProtectedStatusWarnFragment().show(getSupportFragmentManager(),
                        "retweet_protected_status_warning_message");
            } else {
                updateStatus();
            }
            break;
        }
        case R.id.account_selector_container: {
            setAccountSelectorVisible(false);
            break;
        }
        case R.id.account_selector_button: {
            final boolean isVisible = mAccountSelectorContainer.getVisibility() == View.VISIBLE;
            mAccountSelectorContainer.setVisibility(isVisible ? View.GONE : View.VISIBLE);
            break;
        }
        case R.id.location_container: {
            toggleLocation();
            break;
        }
        }
    }

    @Override
    public boolean onCreateActionMode(ActionMode mode, Menu menu) {
        final Window window = getWindow();
        final Rect rect = new Rect();
        window.getDecorView().getWindowVisibleDisplayFrame(rect);
        final int actionBarHeight = ThemeUtils.getActionBarHeight(this);
        final View contentView = window.findViewById(android.R.id.content);
        final int[] location = new int[2];
        contentView.getLocationOnScreen(location);
        if (location[1] > actionBarHeight) {
            contentView.setPadding(contentView.getPaddingLeft(), 0, contentView.getPaddingRight(),
                    contentView.getPaddingBottom());
            return true;
        }
        final int statusBarHeight = rect.top;
        contentView.getWindowVisibleDisplayFrame(rect);
        final int paddingTop = statusBarHeight + actionBarHeight - rect.top;
        contentView.setPadding(contentView.getPaddingLeft(), paddingTop, contentView.getPaddingRight(),
                contentView.getPaddingBottom());
        return true;
    }

    @Override
    public boolean onPrepareActionMode(ActionMode mode, Menu menu) {
        return true;
    }

    @Override
    public boolean onActionItemClicked(ActionMode mode, MenuItem item) {
        return false;
    }

    @Override
    public void onDestroyActionMode(ActionMode mode) {
        final Window window = getWindow();
        final View contentView = window.findViewById(android.R.id.content);
        contentView.setPadding(contentView.getPaddingLeft(), 0, contentView.getPaddingRight(),
                contentView.getPaddingBottom());
    }

    @Override
    public void onLocationChanged(final Location location) {
        setRecentLocation(ParcelableLocation.fromLocation(location));
    }

    @Override
    public void onStatusChanged(final String provider, final int status, final Bundle extras) {

    }

    @Override
    public void onProviderEnabled(final String provider) {
    }

    @Override
    public void onProviderDisabled(final String provider) {
    }

    @Override
    public boolean onLongClick(final View v) {
        switch (v.getId()) {
        case R.id.send: {
            Utils.showMenuItemToast(v, getString(R.string.send), true);
            return true;
        }
        }
        return false;
    }

    @Override
    public boolean onMenuItemClick(final MenuItem item) {
        switch (item.getItemId()) {
        case MENU_TAKE_PHOTO:
        case R.id.take_photo_sub_item: {
            takePhoto();
            break;
        }
        case MENU_ADD_IMAGE:
        case R.id.add_image_sub_item: {
            pickImage();
            break;
        }
        case MENU_ADD_LOCATION: {
            toggleLocation();
            break;
        }
        case MENU_DRAFTS: {
            Utils.openDrafts(this);
            break;
        }
        case MENU_DELETE: {
            AsyncTaskUtils.executeTask(new DeleteImageTask(this));
            break;
        }
        case MENU_TOGGLE_SENSITIVE: {
            if (!hasMedia())
                return false;
            mIsPossiblySensitive = !mIsPossiblySensitive;
            setMenu();
            updateTextCount();
            break;
        }
        case MENU_VIEW: {
            if (mInReplyToStatus == null)
                return false;
            final DialogFragment fragment = new ViewStatusDialogFragment();
            final Bundle args = new Bundle();
            args.putParcelable(EXTRA_STATUS, mInReplyToStatus);
            fragment.setArguments(args);
            fragment.show(getSupportFragmentManager(), "view_status");
            break;
        }
        case R.id.link_to_quoted_status: {
            final boolean newValue = !item.isChecked();
            item.setChecked(newValue);
            mPreferences.edit().putBoolean(KEY_LINK_TO_QUOTED_TWEET, newValue).apply();
            break;
        }
        default: {
            final Intent intent = item.getIntent();
            if (intent != null) {
                try {
                    final String action = intent.getAction();
                    if (INTENT_ACTION_EXTENSION_COMPOSE.equals(action)) {
                        final long[] accountIds = mAccountsAdapter.getSelectedAccountIds();
                        intent.putExtra(EXTRA_TEXT, ParseUtils.parseString(mEditText.getText()));
                        intent.putExtra(EXTRA_ACCOUNT_IDS, accountIds);
                        if (accountIds.length > 0) {
                            final long account_id = accountIds[0];
                            intent.putExtra(EXTRA_NAME, Utils.getAccountName(this, account_id));
                            intent.putExtra(EXTRA_SCREEN_NAME, Utils.getAccountScreenName(this, account_id));
                        }
                        if (mInReplyToStatusId > 0) {
                            intent.putExtra(EXTRA_IN_REPLY_TO_ID, mInReplyToStatusId);
                        }
                        if (mInReplyToStatus != null) {
                            intent.putExtra(EXTRA_IN_REPLY_TO_NAME, mInReplyToStatus.user_name);
                            intent.putExtra(EXTRA_IN_REPLY_TO_SCREEN_NAME, mInReplyToStatus.user_screen_name);
                        }
                        startActivityForResult(intent, REQUEST_EXTENSION_COMPOSE);
                    } else if (INTENT_ACTION_EXTENSION_EDIT_IMAGE.equals(action)) {
                        // final ComponentName cmp = intent.getComponent();
                        // if (cmp == null || !hasMedia()) return false;
                        // final String name = new
                        // File(mMediaUri.getPath()).getName();
                        // final Uri data =
                        // Uri.withAppendedPath(CacheFiles.CONTENT_URI,
                        // Uri.encode(name));
                        // intent.setData(data);
                        // grantUriPermission(cmp.getPackageName(), data,
                        // Intent.FLAG_GRANT_READ_URI_PERMISSION);
                        // startActivityForResult(intent,
                        // REQUEST_EDIT_IMAGE);
                    } else {
                        startActivity(intent);
                    }
                } catch (final ActivityNotFoundException e) {
                    Log.w(LOGTAG, e);
                    return false;
                }
            }
            break;
        }
        }
        return true;
    }

    @Override
    public boolean onTouchEvent(final MotionEvent event) {
        switch (event.getActionMasked()) {
        case MotionEvent.ACTION_DOWN: {
            final Rect rect = new Rect();
            getWindow().getDecorView().getHitRect(rect);
            if (!rect.contains(Math.round(event.getX()), Math.round(event.getY()))) {
                onBackPressed();
                return true;
            }
            break;
        }
        }
        return super.onTouchEvent(event);
    }

    @Override
    public void onContentChanged() {
        super.onContentChanged();
        mEditText = (ComposeEditText) findViewById(R.id.edit_text);
        mMediaPreviewGrid = (GridView) findViewById(R.id.media_thumbnail_preview);
        mMenuBar = (ActionMenuView) findViewById(R.id.menu_bar);
        mSendView = findViewById(R.id.send);
        mSendTextCountView = (StatusTextCountView) mSendView.findViewById(R.id.status_text_count);
        mAccountSelector = (RecyclerView) findViewById(R.id.account_selector);
        mAccountSelectorContainer = findViewById(R.id.account_selector_container);
        mProfileImageView = (ShapedImageView) findViewById(R.id.account_profile_image);
        mCountView = (BadgeView) findViewById(R.id.accounts_count);
        mAccountSelectorButton = findViewById(R.id.account_selector_button);
        mLocationContainer = findViewById(R.id.location_container);
        mLocationIcon = (ActionIconView) findViewById(R.id.location_icon);
        mLocationText = (TextView) findViewById(R.id.location_text);
    }

    @NonNull
    @Override
    public MenuInflater getMenuInflater() {
        if (mMenuInflater == null) {
            mMenuInflater = new SupportMenuInflater(this);
        }
        return mMenuInflater;
    }

    public void removeAllMedia(final List<ParcelableMediaUpdate> list) {
        mMediaPreviewAdapter.removeAll(list);
        updateMediaPreview();
    }

    public void saveToDrafts() {
        final String text = mEditText != null ? ParseUtils.parseString(mEditText.getText()) : null;
        final ParcelableStatusUpdate.Builder builder = new ParcelableStatusUpdate.Builder();
        builder.accounts(ParcelableAccount.getAccounts(this, mAccountsAdapter.getSelectedAccountIds()));
        builder.text(text);
        builder.inReplyToStatusId(mInReplyToStatusId);
        builder.location(mRecentLocation);
        builder.isPossiblySensitive(mIsPossiblySensitive);
        if (hasMedia()) {
            builder.media(getMedia());
        }
        final ContentValues values = ContentValuesCreator.createStatusDraft(builder.build());
        final Uri draftUri = mResolver.insert(Drafts.CONTENT_URI, values);
        displayNewDraftNotification(text, draftUri);
    }

    public void setSelectedAccounts(ParcelableAccount... accounts) {
        if (accounts.length == 1) {
            mCountView.setText(null);
            final ParcelableAccount account = accounts[0];
            mImageLoader.displayProfileImage(mProfileImageView, account.profile_image_url);
            mProfileImageView.setBorderColor(account.color);
        } else {
            mCountView.setText(String.valueOf(accounts.length));
            mImageLoader.cancelDisplayTask(mProfileImageView);
            mProfileImageView.setImageDrawable(null);
            mProfileImageView.setBorderColors(Utils.getAccountColors(accounts));
        }
    }

    @Override
    protected void onCreate(final Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        mLocationManager = (LocationManager) getSystemService(Context.LOCATION_SERVICE);
        mPreferences = SharedPreferencesWrapper.getInstance(this, SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE,
                SharedPreferenceConstants.class);

        final TwidereApplication app = TwidereApplication.getInstance(this);
        mTwitterWrapper = app.getTwitterWrapper();
        mResolver = getContentResolver();
        mValidator = new TwidereValidator(this);
        mImageLoader = app.getMediaLoaderWrapper();
        setContentView(R.layout.activity_compose);
        setFinishOnTouchOutside(false);
        final long[] defaultAccountIds = Utils.getAccountIds(this);
        if (defaultAccountIds.length <= 0) {
            final Intent intent = new Intent(INTENT_ACTION_TWITTER_LOGIN);
            intent.setClass(this, SignInActivity.class);
            startActivity(intent);
            finish();
            return;
        }
        mMenuBar.setOnMenuItemClickListener(this);
        setupEditText();
        mAccountSelectorContainer.setOnClickListener(this);
        mAccountSelectorButton.setOnClickListener(this);
        mLocationContainer.setOnClickListener(this);

        final LinearLayoutManager linearLayoutManager = new FixedLinearLayoutManager(this);
        linearLayoutManager.setOrientation(LinearLayoutManager.VERTICAL);
        linearLayoutManager.setStackFromEnd(true);
        mAccountSelector.setLayoutManager(linearLayoutManager);
        mAccountSelector.addItemDecoration(new SpacingItemDecoration(this));
        mAccountSelector.setItemAnimator(new RecyclerView.ItemAnimator() {
            @Override
            public void runPendingAnimations() {

            }

            @Override
            public boolean animateRemove(ViewHolder holder) {
                return false;
            }

            @Override
            public boolean animateAdd(ViewHolder holder) {
                return false;
            }

            @Override
            public boolean animateMove(ViewHolder holder, int fromX, int fromY, int toX, int toY) {
                Log.d(LOGTAG, String.format("animateMove"));
                return false;
            }

            @Override
            public boolean animateChange(ViewHolder oldHolder, ViewHolder newHolder, int fromLeft, int fromTop,
                    int toLeft, int toTop) {
                Log.d(LOGTAG, String.format("animateChange"));
                return false;
            }

            @Override
            public void endAnimation(ViewHolder item) {
                Log.d(LOGTAG, String.format("endAnimation"));
            }

            @Override
            public void endAnimations() {

            }

            @Override
            public boolean isRunning() {
                return false;
            }
        });
        mAccountsAdapter = new AccountIconsAdapter(this);
        mAccountSelector.setAdapter(mAccountsAdapter);
        mAccountsAdapter.setAccounts(ParcelableAccount.getAccounts(this, false, false));

        mMediaPreviewAdapter = new MediaPreviewAdapter(this);
        mMediaPreviewGrid.setAdapter(mMediaPreviewAdapter);

        final Intent intent = getIntent();

        if (savedInstanceState != null) {
            // Restore from previous saved state
            mAccountsAdapter.setSelectedAccountIds(savedInstanceState.getLongArray(EXTRA_ACCOUNT_IDS));
            mIsPossiblySensitive = savedInstanceState.getBoolean(EXTRA_IS_POSSIBLY_SENSITIVE);
            final ArrayList<ParcelableMediaUpdate> mediaList = savedInstanceState
                    .getParcelableArrayList(EXTRA_MEDIA);
            if (mediaList != null) {
                addMedia(mediaList);
            }
            mInReplyToStatus = savedInstanceState.getParcelable(EXTRA_STATUS);
            mInReplyToStatusId = savedInstanceState.getLong(EXTRA_STATUS_ID);
            mMentionUser = savedInstanceState.getParcelable(EXTRA_USER);
            mDraftItem = savedInstanceState.getParcelable(EXTRA_DRAFT);
            mShouldSaveAccounts = savedInstanceState.getBoolean(EXTRA_SHOULD_SAVE_ACCOUNTS);
            mOriginalText = savedInstanceState.getString(EXTRA_ORIGINAL_TEXT);
        } else {
            // The context was first created
            final int notificationId = intent.getIntExtra(EXTRA_NOTIFICATION_ID, -1);
            final long notificationAccount = intent.getLongExtra(EXTRA_NOTIFICATION_ACCOUNT, -1);
            if (notificationId != -1) {
                mTwitterWrapper.clearNotificationAsync(notificationId, notificationAccount);
            }
            if (!handleIntent(intent)) {
                handleDefaultIntent(intent);
            }
            final long[] accountIds = mAccountsAdapter.getSelectedAccountIds();
            if (accountIds.length == 0) {
                final long[] idsInPrefs = TwidereArrayUtils
                        .parseLongArray(mPreferences.getString(KEY_COMPOSE_ACCOUNTS, null), ',');
                final long[] intersection = TwidereArrayUtils.intersection(idsInPrefs, defaultAccountIds);
                mAccountsAdapter.setSelectedAccountIds(intersection.length > 0 ? intersection : defaultAccountIds);
            }
            mOriginalText = ParseUtils.parseString(mEditText.getText());
        }
        if (!setComposeTitle(intent)) {
            setTitle(R.string.compose);
        }

        final Menu menu = mMenuBar.getMenu();
        getMenuInflater().inflate(R.menu.menu_compose, menu);
        ThemeUtils.wrapMenuIcon(mMenuBar);

        mSendView.setOnClickListener(this);
        mSendView.setOnLongClickListener(this);
        final Intent composeExtensionsIntent = new Intent(INTENT_ACTION_EXTENSION_COMPOSE);
        Utils.addIntentToMenu(this, menu, composeExtensionsIntent, MENU_GROUP_COMPOSE_EXTENSION);
        final Intent imageExtensionsIntent = new Intent(INTENT_ACTION_EXTENSION_EDIT_IMAGE);
        final MenuItem mediaMenuItem = menu.findItem(R.id.media_menu);
        if (mediaMenuItem != null && mediaMenuItem.hasSubMenu()) {
            Utils.addIntentToMenu(this, mediaMenuItem.getSubMenu(), imageExtensionsIntent,
                    MENU_GROUP_IMAGE_EXTENSION);
        }
        setMenu();
        updateLocationState();
        updateMediaPreview();
        notifyAccountSelectionChanged();

        mTextChanged = false;
    }

    private void setupEditText() {
        final boolean sendByEnter = mPreferences.getBoolean(KEY_QUICK_SEND);
        EditTextEnterHandler.attach(mEditText, new EnterListener() {
            @Override
            public void onHitEnter() {
                updateStatus();
            }
        }, sendByEnter);
        mEditText.addTextChangedListener(new TextWatcher() {

            @Override
            public void beforeTextChanged(final CharSequence s, final int start, final int count, final int after) {

            }

            @Override
            public void onTextChanged(final CharSequence s, final int start, final int before, final int count) {
                setMenu();
                updateTextCount();
                if (s instanceof Spannable && count == 1 && before == 0) {
                    final ImageSpan[] imageSpans = ((Spannable) s).getSpans(start, start + count, ImageSpan.class);
                    if (imageSpans.length == 1) {
                        final Intent intent = new Intent(ComposeActivity.this, ImagePickerActivity.class);
                        intent.setAction(ImagePickerActivity.INTENT_ACTION_GET_IMAGE);
                        intent.setData(Uri.parse(imageSpans[0].getSource()));
                        startActivityForResult(intent, REQUEST_PICK_IMAGE);
                        ((Spannable) s).setSpan(new MarkForDeleteSpan(), start, start + count,
                                Spanned.SPAN_INCLUSIVE_INCLUSIVE);
                    }
                }
            }

            @Override
            public void afterTextChanged(final Editable s) {
                mTextChanged = s.length() == 0;
                final MarkForDeleteSpan[] deletes = s.getSpans(0, s.length(), MarkForDeleteSpan.class);
                for (MarkForDeleteSpan delete : deletes) {
                    s.delete(s.getSpanStart(delete), s.getSpanEnd(delete));
                }
            }
        });
        mEditText.setCustomSelectionActionModeCallback(this);
    }

    @Override
    protected void onTitleChanged(final CharSequence title, final int color) {
        super.onTitleChanged(title, color);
    }

    @Override
    public boolean handleKeyboardShortcutSingle(@NonNull KeyboardShortcutsHandler handler, int keyCode,
            @NonNull KeyEvent event) {
        final String action = handler.getKeyAction(CONTEXT_TAG_NAVIGATION, keyCode, event);
        if (ACTION_NAVIGATION_BACK.equals(action)) {
            if (mEditText.length() == 0 && !mTextChanged) {
                if (!mNavigateBackPressed) {
                    final SuperToast toast = SuperToast.create(this, getString(R.string.press_again_to_close),
                            Duration.SHORT);
                    toast.setOnDismissListener(new OnDismissListener() {
                        @Override
                        public void onDismiss(View view) {
                            mNavigateBackPressed = false;
                        }
                    });
                    toast.show();
                } else {
                    onBackPressed();
                }
                mNavigateBackPressed = true;
            } else {
                mTextChanged = false;
            }
            return true;
        }
        return super.handleKeyboardShortcutSingle(handler, keyCode, event);
    }

    @Override
    public boolean handleKeyboardShortcutRepeat(@NonNull KeyboardShortcutsHandler handler, int keyCode,
            int repeatCount, @NonNull KeyEvent event) {
        return super.handleKeyboardShortcutRepeat(handler, keyCode, repeatCount, event);
    }

    private void addMedia(final ParcelableMediaUpdate media) {
        mMediaPreviewAdapter.add(media);
        updateMediaPreview();
    }

    private void addMedia(final List<ParcelableMediaUpdate> media) {
        mMediaPreviewAdapter.addAll(media);
        updateMediaPreview();
    }

    private void clearMedia() {
        mMediaPreviewAdapter.clear();
        updateMediaPreview();
    }

    private Uri createTempImageUri() {
        final File file = new File(getCacheDir(), "tmp_image_" + System.currentTimeMillis());
        return Uri.fromFile(file);
    }

    private void displayNewDraftNotification(String text, Uri draftUri) {
        final NotificationManager nm = (NotificationManager) getSystemService(NOTIFICATION_SERVICE);
        final NotificationCompat.Builder builder = new NotificationCompat.Builder(this);
        builder.setTicker(getString(R.string.draft_saved));
        builder.setContentTitle(getString(R.string.draft_saved));
        if (TextUtils.isEmpty(text)) {
            builder.setContentText(getString(R.string.empty_content));
        } else {
            builder.setContentText(text);
        }
        builder.setSmallIcon(R.drawable.ic_stat_info);
        builder.setAutoCancel(true);
        final Intent draftsIntent = new Intent(this, DraftsFragment.class);
        builder.setContentIntent(
                PendingIntent.getActivity(this, 0, draftsIntent, PendingIntent.FLAG_UPDATE_CURRENT));
        final Intent serviceIntent = new Intent(this, BackgroundOperationService.class);
        serviceIntent.setAction(INTENT_ACTION_DISCARD_DRAFT);
        serviceIntent.setData(draftUri);
        final Action.Builder actionBuilder = new Action.Builder(R.drawable.ic_action_delete,
                getString(R.string.discard),
                PendingIntent.getService(this, 0, serviceIntent, PendingIntent.FLAG_UPDATE_CURRENT));
        builder.addAction(actionBuilder.build());
        nm.notify(draftUri.toString(), NOTIFICATION_ID_DRAFTS, builder.build());
    }

    private ParcelableMediaUpdate[] getMedia() {
        final List<ParcelableMediaUpdate> list = getMediaList();
        return list.toArray(new ParcelableMediaUpdate[list.size()]);
    }

    private List<ParcelableMediaUpdate> getMediaList() {
        return mMediaPreviewAdapter.getAsList();
    }

    private boolean handleDefaultIntent(final Intent intent) {
        if (intent == null)
            return false;
        final String action = intent.getAction();
        final boolean hasAccountIds;
        if (intent.hasExtra(EXTRA_ACCOUNT_IDS)) {
            mAccountsAdapter.setSelectedAccountIds(intent.getLongArrayExtra(EXTRA_ACCOUNT_IDS));
            hasAccountIds = true;
        } else if (intent.hasExtra(EXTRA_ACCOUNT_ID)) {
            mAccountsAdapter.setSelectedAccountIds(intent.getLongExtra(EXTRA_ACCOUNT_ID, -1));
            hasAccountIds = true;
        } else {
            hasAccountIds = false;
        }
        mShouldSaveAccounts = !Intent.ACTION_SEND.equals(action) && !Intent.ACTION_SEND_MULTIPLE.equals(action)
                && !hasAccountIds;
        final Uri data = intent.getData();
        final CharSequence extraSubject = intent.getCharSequenceExtra(Intent.EXTRA_SUBJECT);
        final CharSequence extraText = intent.getCharSequenceExtra(Intent.EXTRA_TEXT);
        final Uri extraStream = intent.getParcelableExtra(Intent.EXTRA_STREAM);
        //TODO handle share_screenshot extra (Bitmap)
        if (extraStream != null) {
            AsyncTaskUtils.executeTask(
                    new AddMediaTask(this, extraStream, createTempImageUri(), ParcelableMedia.TYPE_IMAGE, false));
        } else if (data != null) {
            AsyncTaskUtils.executeTask(
                    new AddMediaTask(this, data, createTempImageUri(), ParcelableMedia.TYPE_IMAGE, false));
        } else if (intent.hasExtra(EXTRA_SHARE_SCREENSHOT) && Utils.useShareScreenshot()) {
            final Bitmap bitmap = intent.getParcelableExtra(EXTRA_SHARE_SCREENSHOT);
            if (bitmap != null) {
                try {
                    AsyncTaskUtils.executeTask(
                            new AddBitmapTask(this, bitmap, createTempImageUri(), ParcelableMedia.TYPE_IMAGE));
                } catch (IOException e) {
                    // ignore
                    bitmap.recycle();
                }
            }
        }
        mEditText.setText(Utils.getShareStatus(this, extraSubject, extraText));
        final int selectionEnd = mEditText.length();
        mEditText.setSelection(selectionEnd);
        return true;
    }

    private boolean handleEditDraftIntent(final DraftItem draft) {
        if (draft == null)
            return false;
        mEditText.setText(draft.text);
        final int selectionEnd = mEditText.length();
        mEditText.setSelection(selectionEnd);
        mAccountsAdapter.setSelectedAccountIds(draft.account_ids);
        if (draft.media != null) {
            addMedia(Arrays.asList(draft.media));
        }
        mIsPossiblySensitive = draft.is_possibly_sensitive;
        mInReplyToStatusId = draft.in_reply_to_status_id;
        return true;
    }

    private boolean handleIntent(final Intent intent) {
        final String action = intent.getAction();
        if (action == null)
            return false;
        mShouldSaveAccounts = false;
        mMentionUser = intent.getParcelableExtra(EXTRA_USER);
        mInReplyToStatus = intent.getParcelableExtra(EXTRA_STATUS);
        mInReplyToStatusId = mInReplyToStatus != null ? mInReplyToStatus.id : -1;
        switch (action) {
        case INTENT_ACTION_REPLY: {
            return handleReplyIntent(mInReplyToStatus);
        }
        case INTENT_ACTION_QUOTE: {
            return handleQuoteIntent(mInReplyToStatus);
        }
        case INTENT_ACTION_EDIT_DRAFT: {
            mDraftItem = intent.getParcelableExtra(EXTRA_DRAFT);
            return handleEditDraftIntent(mDraftItem);
        }
        case INTENT_ACTION_MENTION: {
            return handleMentionIntent(mMentionUser);
        }
        case INTENT_ACTION_REPLY_MULTIPLE: {
            final String[] screenNames = intent.getStringArrayExtra(EXTRA_SCREEN_NAMES);
            final long accountId = intent.getLongExtra(EXTRA_ACCOUNT_ID, -1);
            final long inReplyToUserId = intent.getLongExtra(EXTRA_IN_REPLY_TO_ID, -1);
            return handleReplyMultipleIntent(screenNames, accountId, inReplyToUserId);
        }
        case INTENT_ACTION_COMPOSE_TAKE_PHOTO: {
            return takePhoto();
        }
        case INTENT_ACTION_COMPOSE_PICK_IMAGE: {
            return pickImage();
        }
        }
        // Unknown action or no intent extras
        return false;
    }

    private boolean handleMentionIntent(final ParcelableUser user) {
        if (user == null || user.id <= 0)
            return false;
        final String my_screen_name = Utils.getAccountScreenName(this, user.account_id);
        if (TextUtils.isEmpty(my_screen_name))
            return false;
        mEditText.setText("@" + user.screen_name + " ");
        final int selection_end = mEditText.length();
        mEditText.setSelection(selection_end);
        mAccountsAdapter.setSelectedAccountIds(user.account_id);
        return true;
    }

    private boolean handleQuoteIntent(final ParcelableStatus status) {
        if (status == null || status.id <= 0)
            return false;
        mEditText.setText(Utils.getQuoteStatus(this, status.id, status.user_screen_name, status.text_plain));
        mEditText.setSelection(0);
        mAccountsAdapter.setSelectedAccountIds(status.account_id);
        return true;
    }

    private boolean handleReplyIntent(final ParcelableStatus status) {
        if (status == null || status.id <= 0)
            return false;
        final String myScreenName = Utils.getAccountScreenName(this, status.account_id);
        if (TextUtils.isEmpty(myScreenName))
            return false;
        int selectionStart = 0;
        if (status.is_quote) {
            mEditText.append("@" + status.quoted_by_user_screen_name + " ");
            selectionStart = mEditText.length();
        }
        mEditText.append("@" + status.user_screen_name + " ");
        if (!status.is_quote) {
            selectionStart = mEditText.length();
        }
        if (status.is_retweet) {
            mEditText.append("@" + status.retweeted_by_user_screen_name + " ");
        }
        final Collection<String> mentions = new TreeSet<>(String.CASE_INSENSITIVE_ORDER);
        mentions.addAll(mExtractor.extractMentionedScreennames(status.text_plain));
        for (final String mention : mentions) {
            if (mention.equalsIgnoreCase(status.user_screen_name) || mention.equalsIgnoreCase(myScreenName)
                    || mention.equalsIgnoreCase(status.retweeted_by_user_screen_name)) {
                continue;
            }
            mEditText.append("@" + mention + " ");
        }
        final int selectionEnd = mEditText.length();
        mEditText.setSelection(selectionStart, selectionEnd);
        mAccountsAdapter.setSelectedAccountIds(status.account_id);
        return true;
    }

    private boolean handleReplyMultipleIntent(final String[] screenNames, final long accountId,
            final long inReplyToStatusId) {
        if (screenNames == null || screenNames.length == 0 || accountId <= 0)
            return false;
        final String myScreenName = Utils.getAccountScreenName(this, accountId);
        if (TextUtils.isEmpty(myScreenName))
            return false;
        for (final String screenName : screenNames) {
            if (screenName.equalsIgnoreCase(myScreenName)) {
                continue;
            }
            mEditText.append("@" + screenName + " ");
        }
        mEditText.setSelection(mEditText.length());
        mAccountsAdapter.setSelectedAccountIds(accountId);
        mInReplyToStatusId = inReplyToStatusId;
        return true;
    }

    private boolean hasMedia() {
        return !mMediaPreviewAdapter.isEmpty();
    }

    private boolean isQuote() {
        return INTENT_ACTION_QUOTE.equals(getIntent().getAction());
    }

    private boolean isQuotingProtectedStatus() {
        if (!isQuote() || mInReplyToStatus == null)
            return false;
        return mInReplyToStatus.user_is_protected && mInReplyToStatus.account_id != mInReplyToStatus.user_id;
    }

    private boolean noReplyContent(final String text) {
        if (text == null)
            return true;
        final String action = getIntent().getAction();
        final boolean is_reply = INTENT_ACTION_REPLY.equals(action) || INTENT_ACTION_REPLY_MULTIPLE.equals(action);
        return is_reply && text.equals(mOriginalText);
    }

    private void notifyAccountSelectionChanged() {
        final ParcelableAccount[] accounts = mAccountsAdapter.getSelectedAccounts();
        setSelectedAccounts(accounts);
        mEditText.setAccountId(accounts.length > 0 ? accounts[0].account_id : Utils.getDefaultAccountId(this));
        //        mAccountActionProvider.setSelectedAccounts(mAccountsAdapter.getSelectedAccounts());
    }

    @TargetApi(Build.VERSION_CODES.KITKAT)
    private boolean openDocument() {
        final Intent intent = new Intent(Intent.ACTION_GET_CONTENT);
        final String[] mimeTypes = { "image/png", "image/jpeg", "image/gif" };
        intent.setType("image/*");
        intent.putExtra(Intent.EXTRA_MIME_TYPES, mimeTypes);
        intent.addCategory(Intent.CATEGORY_OPENABLE);
        intent.addFlags(Intent.FLAG_GRANT_READ_URI_PERMISSION);
        try {
            startActivityForResult(intent, REQUEST_OPEN_DOCUMENT);
        } catch (final ActivityNotFoundException e) {
            return false;
        }
        return true;
    }

    private boolean pickImage() {
        final Intent intent = new Intent(this, ImagePickerActivity.class);
        intent.setAction(ImagePickerActivity.INTENT_ACTION_PICK_IMAGE);
        startActivityForResult(intent, REQUEST_PICK_IMAGE);
        return true;
    }

    private void saveAccountSelection() {
        if (!mShouldSaveAccounts)
            return;
        final SharedPreferences.Editor editor = mPreferences.edit();
        editor.putString(KEY_COMPOSE_ACCOUNTS,
                TwidereArrayUtils.toString(mAccountsAdapter.getSelectedAccountIds(), ',', false));
        editor.apply();
    }

    private void setAccountSelectorVisible(boolean visible) {
        mAccountSelectorContainer.setVisibility(visible ? View.VISIBLE : View.GONE);
    }

    private boolean setComposeTitle(final Intent intent) {
        final String action = intent.getAction();
        final UserColorNameManager manager = UserColorNameManager.getInstance(this);
        final boolean nameFirst = mPreferences.getBoolean(KEY_NAME_FIRST);
        if (INTENT_ACTION_REPLY.equals(action)) {
            if (mInReplyToStatus == null)
                return false;
            final String displayName = manager.getDisplayName(mInReplyToStatus.user_id, mInReplyToStatus.user_name,
                    mInReplyToStatus.user_screen_name, nameFirst, false);
            setTitle(getString(R.string.reply_to, displayName));
        } else if (INTENT_ACTION_QUOTE.equals(action)) {
            if (mInReplyToStatus == null)
                return false;
            final String displayName = manager.getDisplayName(mInReplyToStatus.user_id, mInReplyToStatus.user_name,
                    mInReplyToStatus.user_screen_name, nameFirst, false);
            setTitle(getString(R.string.quote_user, displayName));
        } else if (INTENT_ACTION_EDIT_DRAFT.equals(action)) {
            if (mDraftItem == null)
                return false;
            setTitle(R.string.edit_draft);
        } else if (INTENT_ACTION_MENTION.equals(action)) {
            if (mMentionUser == null)
                return false;
            final String displayName = manager.getDisplayName(mMentionUser.id, mMentionUser.name,
                    mMentionUser.screen_name, nameFirst, false);
            setTitle(getString(R.string.mention_user, displayName));
        } else if (INTENT_ACTION_REPLY_MULTIPLE.equals(action)) {
            setTitle(R.string.reply);
        } else if (Intent.ACTION_SEND.equals(action) || Intent.ACTION_SEND_MULTIPLE.equals(action)) {
            setTitle(R.string.share);
        } else {
            setTitle(R.string.compose);
        }
        return true;
    }

    private void setMenu() {
        if (mMenuBar == null)
            return;
        final Menu menu = mMenuBar.getMenu();
        final boolean hasMedia = hasMedia(), hasInReplyTo = mInReplyToStatus != null;

        /*
         * No media & Not reply: [Take photo][Add image][Attach location][Drafts]
         * Has media & Not reply: [Take photo][Media menu][Attach location][Drafts]
         * Is reply: [Media menu][View status][Attach location][Drafts]
         */
        MenuUtils.setMenuItemAvailability(menu, MENU_TAKE_PHOTO, !hasInReplyTo);
        MenuUtils.setMenuItemAvailability(menu, R.id.take_photo_sub_item, hasInReplyTo);
        MenuUtils.setMenuItemAvailability(menu, MENU_ADD_IMAGE, !hasMedia && !hasInReplyTo);
        MenuUtils.setMenuItemAvailability(menu, MENU_VIEW, hasInReplyTo);
        MenuUtils.setMenuItemAvailability(menu, R.id.media_menu, hasMedia || hasInReplyTo);
        MenuUtils.setMenuItemAvailability(menu, MENU_TOGGLE_SENSITIVE, hasMedia);
        MenuUtils.setMenuItemAvailability(menu, MENU_EDIT_MEDIA, hasMedia);
        MenuUtils.setMenuItemAvailability(menu, R.id.link_to_quoted_status, isQuote());

        menu.setGroupEnabled(MENU_GROUP_IMAGE_EXTENSION, hasMedia);
        menu.setGroupVisible(MENU_GROUP_IMAGE_EXTENSION, hasMedia);
        MenuUtils.setMenuItemChecked(menu, MENU_TOGGLE_SENSITIVE, hasMedia && mIsPossiblySensitive);
        MenuUtils.setMenuItemChecked(menu, R.id.link_to_quoted_status,
                mPreferences.getBoolean(KEY_LINK_TO_QUOTED_TWEET));
        ThemeUtils.resetCheatSheet(mMenuBar);
        //        mMenuBar.show();
    }

    private void setProgressVisible(final boolean visible) {
        //        mProgress.setVisibility(visible ? View.VISIBLE : View.GONE);
    }

    private void setRecentLocation(ParcelableLocation location) {
        if (location != null) {
            mLocationText.setText(location.getHumanReadableString(3));
        } else {
            mLocationText.setText(R.string.unknown_location);
        }
        mRecentLocation = location;
    }

    /**
     * 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 startLocationUpdateIfEnabled() {
        final LocationManager lm = mLocationManager;
        final boolean attachLocation = mPreferences.getBoolean(KEY_ATTACH_LOCATION);
        if (!attachLocation) {
            lm.removeUpdates(this);
            return false;
        }
        final Criteria criteria = new Criteria();
        criteria.setAccuracy(Criteria.ACCURACY_FINE);
        final String provider = lm.getBestProvider(criteria, true);
        if (provider != null) {
            mLocationText.setText(R.string.getting_location);
            lm.requestLocationUpdates(provider, 0, 0, this);
            final Location location;
            if (lm.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
                location = lm.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
            } else {
                location = lm.getLastKnownLocation(provider);
            }
            if (location != null) {
                onLocationChanged(location);
            }
        } else {
            Toast.makeText(this, R.string.cannot_get_location, Toast.LENGTH_SHORT).show();
        }
        return provider != null;
    }

    private boolean takePhoto() {
        final Intent intent = new Intent(this, ImagePickerActivity.class);
        intent.setAction(ImagePickerActivity.INTENT_ACTION_TAKE_PHOTO);
        startActivityForResult(intent, REQUEST_TAKE_PHOTO);
        return true;
    }

    private void toggleLocation() {
        final boolean attachLocation = mPreferences.getBoolean(KEY_ATTACH_LOCATION, false);
        mPreferences.edit().putBoolean(KEY_ATTACH_LOCATION, !attachLocation).apply();
        startLocationUpdateIfEnabled();
        updateLocationState();
        setMenu();
        updateTextCount();
    }

    private void updateLocationState() {
        final boolean attachLocation = mPreferences.getBoolean(KEY_ATTACH_LOCATION, false);
        if (attachLocation) {
            mLocationIcon.setColorFilter(ThemeUtils.getOptimalAccentColor(this, false, getCurrentThemeResourceId()),
                    Mode.SRC_ATOP);
        } else {
            mLocationIcon.setColorFilter(mLocationIcon.getDefaultColor(), Mode.SRC_ATOP);
            mLocationText.setText(R.string.no_location);
        }
    }

    private void updateMediaPreview() {
        final int count = mMediaPreviewAdapter.getCount();
        final Resources res = getResources();
        final int maxColumns = res.getInteger(R.integer.grid_column_image_preview);
        mMediaPreviewGrid.setNumColumns(MathUtils.clamp(count, maxColumns, 1));
    }

    private void updateStatus() {
        if (isFinishing())
            return;
        final boolean hasMedia = hasMedia();
        final String text = mEditText != null ? ParseUtils.parseString(mEditText.getText()) : null;
        final int tweetLength = mValidator.getTweetLength(text), maxLength = mValidator.getMaxTweetLength();
        if (!mStatusShortenerUsed && tweetLength > maxLength) {
            mEditText.setError(getString(R.string.error_message_status_too_long));
            final int textLength = mEditText.length();
            mEditText.setSelection(textLength - (tweetLength - maxLength), textLength);
            return;
        } else if (!hasMedia && (TextUtils.isEmpty(text) || noReplyContent(text))) {
            mEditText.setError(getString(R.string.error_message_no_content));
            return;
        } else if (mAccountsAdapter.isSelectionEmpty()) {
            mEditText.setError(getString(R.string.no_account_selected));
            return;
        }
        final boolean attachLocation = mPreferences.getBoolean(KEY_ATTACH_LOCATION, false);
        //        if (mRecentLocation == null && attachLocation) {
        //            final Location location;
        //            if (mLocationManager.isProviderEnabled(LocationManager.NETWORK_PROVIDER)) {
        //                location = mLocationManager.getLastKnownLocation(LocationManager.NETWORK_PROVIDER);
        //            } else {
        //                location = null;
        //            }
        //            if (location != null) {
        //                mRecentLocation = new ParcelableLocation(location);
        //            }
        //            setRecentLocation();
        //        }
        final long[] accountIds = mAccountsAdapter.getSelectedAccountIds();
        final boolean isQuote = isQuote();
        final ParcelableLocation statusLocation = attachLocation ? mRecentLocation : null;
        final boolean linkToQuotedTweet = mPreferences.getBoolean(KEY_LINK_TO_QUOTED_TWEET, true);
        final long inReplyToStatusId = !isQuote || linkToQuotedTweet ? mInReplyToStatusId : -1;
        final boolean isPossiblySensitive = hasMedia && mIsPossiblySensitive;
        mTwitterWrapper.updateStatusAsync(accountIds, text, statusLocation, getMedia(), inReplyToStatusId,
                isPossiblySensitive);
        if (mPreferences.getBoolean(KEY_NO_CLOSE_AFTER_TWEET_SENT, false)
                && (mInReplyToStatus == null || mInReplyToStatusId <= 0)) {
            mIsPossiblySensitive = false;
            mShouldSaveAccounts = true;
            mInReplyToStatus = null;
            mMentionUser = null;
            mDraftItem = null;
            mInReplyToStatusId = -1;
            mOriginalText = null;
            mEditText.setText(null);
            clearMedia();
            final Intent intent = new Intent(INTENT_ACTION_COMPOSE);
            setIntent(intent);
            setComposeTitle(intent);
            handleIntent(intent);
            setMenu();
            updateTextCount();
        } else {
            setResult(Activity.RESULT_OK);
            finish();
        }
    }

    private void updateTextCount() {
        if (mSendTextCountView == null || mEditText == null)
            return;
        final String textOrig = ParseUtils.parseString(mEditText.getText());
        final String text = hasMedia() && textOrig != null
                ? mImageUploaderUsed ? Utils.getImageUploadStatus(this, new String[] { FAKE_IMAGE_LINK }, textOrig)
                        : textOrig + " " + FAKE_IMAGE_LINK
                : textOrig;
        final int validatedCount = text != null ? mValidator.getTweetLength(text) : 0;
        mSendTextCountView.setTextCount(validatedCount);
    }

    static class AccountIconViewHolder extends ViewHolder implements OnClickListener {

        private final AccountIconsAdapter adapter;
        private final ShapedImageView iconView;
        private final TextView nameView;

        public AccountIconViewHolder(AccountIconsAdapter adapter, View itemView) {
            super(itemView);
            this.adapter = adapter;
            iconView = (ShapedImageView) itemView.findViewById(android.R.id.icon);
            nameView = (TextView) itemView.findViewById(android.R.id.text1);
            itemView.setOnClickListener(this);
        }

        public void showAccount(AccountIconsAdapter adapter, ParcelableAccount account, boolean isSelected) {
            itemView.setAlpha(isSelected ? 1 : 0.33f);
            final MediaLoaderWrapper loader = adapter.getImageLoader();
            loader.displayProfileImage(iconView, account.profile_image_url);
            iconView.setBorderColor(account.color);
            nameView.setText(adapter.isNameFirst() ? account.name : ("@" + account.screen_name));
        }

        @Override
        public void onClick(View v) {
            adapter.toggleSelection(getAdapterPosition());
        }

    }

    private static class AccountIconsAdapter extends Adapter<AccountIconViewHolder> {

        private final ComposeActivity mActivity;
        private final LayoutInflater mInflater;
        private final MediaLoaderWrapper mImageLoader;
        private final LongSparseArray<Boolean> mSelection;
        private final boolean mNameFirst;

        private ParcelableAccount[] mAccounts;

        public AccountIconsAdapter(ComposeActivity activity) {
            setHasStableIds(true);
            mActivity = activity;
            mInflater = activity.getLayoutInflater();
            mImageLoader = TwidereApplication.getInstance(activity).getMediaLoaderWrapper();
            mSelection = new LongSparseArray<>();
            final SharedPreferencesWrapper preferences = SharedPreferencesWrapper.getInstance(activity,
                    SHARED_PREFERENCES_NAME, Context.MODE_PRIVATE, SharedPreferenceConstants.class);
            mNameFirst = preferences.getBoolean(KEY_NAME_FIRST);
        }

        public MediaLoaderWrapper getImageLoader() {
            return mImageLoader;
        }

        @NonNull
        public long[] getSelectedAccountIds() {
            if (mAccounts == null)
                return new long[0];
            final long[] temp = new long[mAccounts.length];
            int selectedCount = 0;
            for (ParcelableAccount account : mAccounts) {
                if (mSelection.get(account.account_id, false)) {
                    temp[selectedCount++] = account.account_id;
                }
            }
            final long[] result = new long[selectedCount];
            System.arraycopy(temp, 0, result, 0, result.length);
            return result;
        }

        public void setSelectedAccountIds(long... accountIds) {
            mSelection.clear();
            if (accountIds != null) {
                for (long accountId : accountIds) {
                    mSelection.put(accountId, true);
                }
            }
            notifyDataSetChanged();
        }

        @NonNull
        public ParcelableAccount[] getSelectedAccounts() {
            if (mAccounts == null)
                return new ParcelableAccount[0];
            final ParcelableAccount[] temp = new ParcelableAccount[mAccounts.length];
            int selectedCount = 0;
            for (ParcelableAccount account : mAccounts) {
                if (mSelection.get(account.account_id, false)) {
                    temp[selectedCount++] = account;
                }
            }
            final ParcelableAccount[] result = new ParcelableAccount[selectedCount];
            System.arraycopy(temp, 0, result, 0, result.length);
            return result;
        }

        public boolean isSelectionEmpty() {
            return getSelectedAccountIds().length == 0;
        }

        @Override
        public AccountIconViewHolder onCreateViewHolder(ViewGroup parent, int viewType) {
            final View view = mInflater.inflate(R.layout.adapter_item_compose_account, parent, false);
            return new AccountIconViewHolder(this, view);
        }

        @Override
        public void onBindViewHolder(AccountIconViewHolder holder, int position) {
            final ParcelableAccount account = mAccounts[position];
            final boolean isSelected = mSelection.get(account.account_id, false);
            holder.showAccount(this, account, isSelected);
        }

        @Override
        public int getItemCount() {
            return mAccounts != null ? mAccounts.length : 0;
        }

        public void setAccounts(ParcelableAccount[] accounts) {
            mAccounts = accounts;
            notifyDataSetChanged();
        }

        private void toggleSelection(int position) {
            if (mAccounts == null)
                return;
            final long accountId = mAccounts[position].account_id;
            mSelection.put(accountId, !mSelection.get(accountId, false));
            mActivity.notifyAccountSelectionChanged();
            notifyDataSetChanged();
        }

        public boolean isNameFirst() {
            return mNameFirst;
        }
    }

    private static class AddBitmapTask extends AddMediaTask {

        private final Bitmap mBitmap;

        AddBitmapTask(ComposeActivity activity, Bitmap bitmap, Uri dst, int media_type) throws IOException {
            super(activity, Uri.fromFile(File.createTempFile("tmp_bitmap", null)), dst, media_type, true);
            mBitmap = bitmap;
        }

        @Override
        protected Boolean doInBackground(Object... params) {
            if (mBitmap == null || mBitmap.isRecycled())
                return false;
            FileOutputStream os = null;
            try {
                os = new FileOutputStream(getSrc().getPath());
                mBitmap.compress(Bitmap.CompressFormat.PNG, 0, os);
                mBitmap.recycle();
            } catch (IOException e) {
                return false;
            } finally {
                IoUtils.closeSilently(os);
            }
            return super.doInBackground(params);
        }

    }

    private static class AddMediaTask extends AsyncTask<Object, Object, Boolean> {

        private final ComposeActivity activity;
        private final int media_type;
        private final Uri src, dst;
        private final boolean delete_src;

        AddMediaTask(final ComposeActivity activity, final Uri src, final Uri dst, final int media_type,
                final boolean delete_src) {
            this.activity = activity;
            this.src = src;
            this.dst = dst;
            this.media_type = media_type;
            this.delete_src = delete_src;
        }

        @Override
        protected Boolean doInBackground(final Object... params) {
            InputStream is = null;
            OutputStream os = null;
            try {
                final ContentResolver resolver = activity.getContentResolver();
                is = resolver.openInputStream(src);
                os = resolver.openOutputStream(dst);
                Utils.copyStream(is, os);
                if (ContentResolver.SCHEME_FILE.equals(src.getScheme()) && delete_src) {
                    final File file = new File(src.getPath());
                    if (!file.delete()) {
                        Log.d(LOGTAG, String.format("Unable to delete %s", file));
                    }
                }
            } catch (final IOException e) {
                Log.w(LOGTAG, e);
                return false;
            } finally {
                Utils.closeSilently(os);
                Utils.closeSilently(is);
            }
            return true;
        }

        Uri getSrc() {
            return src;
        }

        @Override
        protected void onPostExecute(final Boolean result) {
            activity.setProgressVisible(false);
            activity.addMedia(new ParcelableMediaUpdate(dst.toString(), media_type));
            activity.setMenu();
            activity.updateTextCount();
            if (!result) {
                Toast.makeText(activity, R.string.error_occurred, Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        protected void onPreExecute() {
            activity.setProgressVisible(true);
        }
    }

    private static class DeleteImageTask extends AsyncTask<Object, Object, Boolean> {

        final ComposeActivity mActivity;
        private final ParcelableMediaUpdate[] mMedia;

        DeleteImageTask(final ComposeActivity activity, final ParcelableMediaUpdate... media) {
            this.mActivity = activity;
            this.mMedia = media;
        }

        @Override
        protected Boolean doInBackground(final Object... params) {
            if (mMedia == null)
                return false;
            try {
                for (final ParcelableMediaUpdate media : mMedia) {
                    if (media.uri == null)
                        continue;
                    final Uri uri = Uri.parse(media.uri);
                    if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
                        final File file = new File(uri.getPath());
                        if (!file.delete()) {
                            Log.d(LOGTAG, String.format("Unable to delete %s", file));
                        }
                    }
                }
            } catch (final Exception e) {
                return false;
            }
            return true;
        }

        @Override
        protected void onPostExecute(final Boolean result) {
            mActivity.setProgressVisible(false);
            mActivity.removeAllMedia(Arrays.asList(mMedia));
            mActivity.setMenu();
            if (!result) {
                Toast.makeText(mActivity, R.string.error_occurred, Toast.LENGTH_SHORT).show();
            }
        }

        @Override
        protected void onPreExecute() {
            mActivity.setProgressVisible(true);
        }
    }

    private static class DiscardTweetTask extends AsyncTask<Object, Object, Object> {

        final ComposeActivity mActivity;

        DiscardTweetTask(final ComposeActivity activity) {
            this.mActivity = activity;
        }

        @Override
        protected Object doInBackground(final Object... params) {
            for (final ParcelableMediaUpdate media : mActivity.getMediaList()) {
                if (media.uri == null)
                    continue;
                final Uri uri = Uri.parse(media.uri);
                if (ContentResolver.SCHEME_FILE.equals(uri.getScheme())) {
                    final File file = new File(uri.getPath());
                    if (!file.delete()) {
                        Log.d(LOGTAG, String.format("Unable to delete %s", file));
                    }
                }
            }
            return null;
        }

        @Override
        protected void onPostExecute(final Object result) {
            mActivity.setProgressVisible(false);
            mActivity.finish();
        }

        @Override
        protected void onPreExecute() {
            mActivity.setProgressVisible(true);
        }
    }

    private static class MediaPreviewAdapter extends DraggableArrayAdapter<ParcelableMediaUpdate> {

        private final MediaLoaderWrapper mImageLoader;

        public MediaPreviewAdapter(final Context context) {
            super(context, R.layout.grid_item_media_editor);
            mImageLoader = TwidereApplication.getInstance(context).getMediaLoaderWrapper();
        }

        public List<ParcelableMediaUpdate> getAsList() {
            return Collections.unmodifiableList(getObjects());
        }

        @Override
        public View getView(final int position, final View convertView, final ViewGroup parent) {
            final View view = super.getView(position, convertView, parent);
            final ParcelableMediaUpdate media = getItem(position);
            final ImageView image = (ImageView) view.findViewById(R.id.image);
            mImageLoader.displayPreviewImage(media.uri, image);
            return view;
        }

    }

    public static class RetweetProtectedStatusWarnFragment extends BaseSupportDialogFragment
            implements DialogInterface.OnClickListener {

        @Override
        public void onClick(final DialogInterface dialog, final int which) {
            final Activity activity = getActivity();
            switch (which) {
            case DialogInterface.BUTTON_POSITIVE: {
                if (activity instanceof ComposeActivity) {
                    ((ComposeActivity) activity).updateStatus();
                }
                break;
            }
            }

        }

        @NonNull
        @Override
        public Dialog onCreateDialog(final Bundle savedInstanceState) {
            final Context wrapped = ThemeUtils.getDialogThemedContext(getActivity());
            final AlertDialog.Builder builder = new AlertDialog.Builder(wrapped);
            builder.setMessage(R.string.quote_protected_status_warning_message);
            builder.setPositiveButton(R.string.send_anyway, this);
            builder.setNegativeButton(android.R.string.cancel, null);
            return builder.create();
        }
    }

    private static class SpacingItemDecoration extends ItemDecoration {

        private final int mSpacingSmall, mSpacingNormal;

        SpacingItemDecoration(Context context) {
            final Resources resources = context.getResources();
            mSpacingSmall = resources.getDimensionPixelSize(R.dimen.element_spacing_small);
            mSpacingNormal = resources.getDimensionPixelSize(R.dimen.element_spacing_normal);
        }

        @Override
        public void getItemOffsets(Rect outRect, View view, RecyclerView parent, State state) {
            final int pos = parent.getChildAdapterPosition(view);
            if (pos == 0) {
                outRect.set(0, mSpacingNormal, 0, 0);
            } else if (pos == parent.getAdapter().getItemCount() - 1) {
                outRect.set(0, 0, 0, mSpacingNormal);
            } else {
                outRect.set(0, 0, 0, 0);
            }
        }
    }

}