org.catnut.ui.ComposeTweetActivity.java Source code

Java tutorial

Introduction

Here is the source code for org.catnut.ui.ComposeTweetActivity.java

Source

/*
 * The MIT License (MIT)
 * Copyright (c) 2014 longkai
 * The software shall be used for good, not evil.
 */
package org.catnut.ui;

import android.app.ActionBar;
import android.app.Activity;
import android.app.AlertDialog;
import android.app.LoaderManager;
import android.app.ProgressDialog;
import android.content.AsyncQueryHandler;
import android.content.Context;
import android.content.DialogInterface;
import android.content.Intent;
import android.content.Loader;
import android.database.Cursor;
import android.location.Location;
import android.net.Uri;
import android.os.Bundle;
import android.os.Handler;
import android.provider.BaseColumns;
import android.provider.MediaStore;
import android.support.v4.widget.SlidingPaneLayout;
import android.text.Editable;
import android.text.TextUtils;
import android.text.TextWatcher;
import android.util.Log;
import android.view.Gravity;
import android.view.KeyEvent;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.AutoCompleteTextView;
import android.widget.EditText;
import android.widget.GridView;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.android.volley.toolbox.JsonObjectRequest;
import com.google.analytics.tracking.android.EasyTracker;
import com.squareup.picasso.Picasso;
import org.catnut.R;
import org.catnut.adapter.EmotionsAdapter;
import org.catnut.adapter.MentionSearchAdapter;
import org.catnut.api.StuffAPI;
import org.catnut.core.CatnutAPI;
import org.catnut.core.CatnutApp;
import org.catnut.core.CatnutProvider;
import org.catnut.metadata.Draft;
import org.catnut.metadata.User;
import org.catnut.metadata.WeiboAPIError;
import org.catnut.service.ComposeTweetService;
import org.catnut.support.LocationSupport;
import org.catnut.support.TweetImageSpan;
import org.catnut.support.TweetTextView;
import org.catnut.util.CatnutUtils;
import org.catnut.util.Constants;
import org.json.JSONArray;
import org.json.JSONObject;

import java.util.LinkedList;
import java.util.List;
import java.util.regex.Matcher;

/**
 * ??
 *
 * @author longkai
 */
public class ComposeTweetActivity extends Activity implements TextWatcher, AdapterView.OnItemClickListener,
        View.OnClickListener, LoaderManager.LoaderCallbacks<Cursor>, MenuItem.OnActionExpandListener {

    public static final String TAG = "ComposeTweetActivity";
    private static final int GALLERY = 1;
    private static final int CAMERA = 2;

    private Handler mHandler = new Handler();
    // app specifics
    private CatnutApp mApp;
    private EasyTracker mTracker;

    //   private boolean mKeyboardShown = false; // ?

    // customized actionbar widgets
    private View mCustomizedBar;
    private TextView mTextCounter;
    private View mSender; // ???

    // widgets
    private SlidingPaneLayout mSlidingPaneLayout;
    private GridView mEmotions;
    private ActionBar mActionBar;
    private GridView mPhotos; // 
    private List<Uri> mUris;
    private ArrayAdapter<Uri> mAdapter;

    private Uri mTmpUri; // ??

    // str
    private String mTitle;
    private String mEmotionTitle;

    private ImageView mAvatar;
    private TextView mScreenName;
    private EditText mText;

    // @ search
    private String mCurKeywords;
    private MenuItem mMentionItem;
    private AutoCompleteTextView mAutoCompleteTextView;
    private InputMethodManager mInputMethodManager;
    private MentionSearchAdapter mMentionSearchAdapter;

    // listeners
    private Response.Listener<JSONObject> listener;
    private Response.ErrorListener errorListener;

    // location
    private LocationSupport.LocationResult mLocationResult; // lazy, if user require.
    private View mLocationMarker;
    private double mLongitude;
    private double mLatitude;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.compose);
        mApp = CatnutApp.getTingtingApp();
        mActionBar = getActionBar();

        mTitle = getString(R.string.compose);
        mEmotionTitle = getString(R.string.add_emotions);

        injectLayout();
        injectActionBar();
        injectListener();

        handleOuterShare();

        mActionBar.setIcon(R.drawable.ic_title_compose);
        mActionBar.setTitle(mTitle);
        mActionBar.setDisplayHomeAsUpEnabled(true);
        mActionBar.setHomeButtonEnabled(true);

        mInputMethodManager = (InputMethodManager) getSystemService(INPUT_METHOD_SERVICE);

        if (mApp.getPreferences().getBoolean(getString(R.string.pref_enable_analytics), true)) {
            mTracker = EasyTracker.getInstance(this);
        }
    }

    // ??
    private void handleOuterShare() {
        Intent intent = getIntent();
        String action = intent.getAction();
        String mimeType = intent.getType();
        if (!TextUtils.isEmpty(action)) {
            if (!TextUtils.isEmpty(mimeType)) {
                if (mimeType.equals(getString(R.string.mime_text_plain))) {
                    mText.setText(intent.getStringExtra(Intent.EXTRA_TEXT));
                    setResult(RESULT_OK); // no result return back
                } else if (mimeType.startsWith("image/")) {
                    mText.setText(intent.getStringExtra(Intent.EXTRA_TEXT));
                    if (mUris == null) {
                        initGallery();
                        // ??binary data??uri
                        // ??
                        mUris.add((Uri) intent.getExtras().get(Intent.EXTRA_STREAM));
                        mAdapter.notifyDataSetChanged();
                        setResult(RESULT_OK);
                    }
                } else {
                    setResult(RESULT_CANCELED);
                }
            } else {
                setResult(RESULT_CANCELED);
            }
        }
    }

    @Override
    protected void onStart() {
        super.onStart();
        if (mTracker != null) {
            mTracker.activityStart(this);
        }
    }

    @Override
    protected void onStop() {
        super.onStop();
        if (mTracker != null) {
            mTracker.activityStop(this);
        }
        mApp.getRequestQueue().cancelAll(TAG);
    }

    @Override
    public boolean onCreateOptionsMenu(Menu menu) {
        getMenuInflater().inflate(R.menu.compose, menu);
        mMentionItem = menu.findItem(R.id.action_mention);
        mMentionItem.setOnActionExpandListener(this);
        return true;
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        if (!mSlidingPaneLayout.isOpen()) {
            mSlidingPaneLayout.openPane();
        }
        switch (item.getItemId()) {
        case R.id.pref:
            startActivity(SingleFragmentActivity.getIntent(this, SingleFragmentActivity.PREF));
            break;
        case R.id.action_gallery:
            // todo: ????Orz
            if (mUris != null && mUris.size() > 0) {
                Toast.makeText(this, getString(R.string.only_one_pic_hint), Toast.LENGTH_LONG).show();
            } else {
                startActivityForResult(new Intent(Intent.ACTION_PICK).setType("image/*"), 1);
            }
            break;
        case R.id.action_shorten:
            shorten();
            break;
        case R.id.action_camera:
            // same as above
            if (mUris != null && mUris.size() > 0) {
                break;
            }
            Intent intent = new Intent(MediaStore.ACTION_IMAGE_CAPTURE);
            if (intent.resolveActivity(getPackageManager()) != null) {
                mTmpUri = CatnutUtils.createImageFile();
                if (mTmpUri != null) {
                    intent.putExtra(MediaStore.EXTRA_OUTPUT, mTmpUri);
                }
                startActivityForResult(intent, CAMERA);
            } else {
                Toast.makeText(this, getString(R.string.device_not_support), Toast.LENGTH_SHORT).show();
            }
            break;
        case R.id.action_discovery:
            int cursor = mText.getSelectionStart();
            mText.getText().append("##");
            mText.setSelection(cursor + 1);
            mText.requestFocus();
            break;
        default:
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    @Override
    public void onClick(View v) {
        switch (v.getId()) {
        case R.id.action_send:
            sendTweet();
            break;
        case R.id.action_geo:
            if (mLocationMarker.getVisibility() == View.VISIBLE) {
                invalidateLocation();
            } else {
                requireLocation().getLocation(this, mLocationResult);
            }
            break;
        case R.id.location_marker:
            invalidateLocation();
            break;
        case R.id.clear:
            mAutoCompleteTextView.setText(null);
            break;
        default:
            break;
        }
    }

    @Override
    public boolean onKeyDown(int keyCode, KeyEvent event) {
        switch (keyCode) {
        case KeyEvent.KEYCODE_BACK:
            if (CatnutUtils.hasLength(mText) || (mUris != null && mUris.size() > 0)) {
                abort();
                return true; // deal it
            }
            break;
        }
        return super.onKeyDown(keyCode, event);
    }

    // ?
    private void abort() {
        new AlertDialog.Builder(this).setMessage(getString(R.string.abort_existing_tweet_alert))
                .setNegativeButton(android.R.string.no, null)
                .setPositiveButton(android.R.string.ok, new DialogInterface.OnClickListener() {
                    @Override
                    public void onClick(DialogInterface dialog, int which) {
                        navigateUpTo(getIntent());
                    }
                }).show();
    }

    private void injectLayout() {
        // for panel
        mSlidingPaneLayout = (SlidingPaneLayout) findViewById(R.id.sliding_pane_layout);
        mEmotions = (GridView) findViewById(R.id.emotions);
        mEmotions.setAdapter(new EmotionsAdapter(this));
        mEmotions.setOnItemClickListener(this);
        mSlidingPaneLayout.setPanelSlideListener(new SliderListener());
        mSlidingPaneLayout.openPane();
        mSlidingPaneLayout.getViewTreeObserver().addOnGlobalLayoutListener(new FirstLayoutListener());
        // for tweet
        mAvatar = (ImageView) findViewById(R.id.avatar);
        mScreenName = (TextView) findViewById(R.id.screen_name);
        mText = (EditText) findViewById(R.id.text);
        mLocationMarker = findViewById(R.id.location_marker);
        // set data to layout...
        new AsyncQueryHandler(getContentResolver()) {
            @Override
            protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
                if (cursor.moveToNext()) {
                    Picasso.with(ComposeTweetActivity.this)
                            .load(cursor.getString(cursor.getColumnIndex(User.avatar_large)))
                            .placeholder(R.drawable.error).error(R.drawable.error).into(mAvatar);
                    mScreenName.setText("@" + cursor.getString(cursor.getColumnIndex(User.screen_name)));
                }
                cursor.close();
            }
        }.startQuery(0, null, CatnutProvider.parse(User.MULTIPLE, mApp.getAccessToken().uid),
                new String[] { User.avatar_large, User.screen_name }, null, null, null);
        // other stuffs...
        mText.addTextChangedListener(this);
    }

    private void injectActionBar() {
        mActionBar.setDisplayShowCustomEnabled(true);
        mCustomizedBar = LayoutInflater.from(this).inflate(R.layout.customized_actionbar, null);
        mTextCounter = (TextView) mCustomizedBar.findViewById(R.id.text_counter);
        mSender = mCustomizedBar.findViewById(R.id.action_send);
        mActionBar.setCustomView(mCustomizedBar, new ActionBar.LayoutParams(ViewGroup.LayoutParams.WRAP_CONTENT,
                ViewGroup.LayoutParams.MATCH_PARENT, Gravity.END));
    }

    private void injectListener() {
        listener = new Response.Listener<JSONObject>() {
            @Override
            public void onResponse(JSONObject response) {
                // delete posted text and thumbs
                mText.setText(null);
                if (mUris != null) {
                    mUris.clear();
                    mAdapter.notifyDataSetChanged();
                }
                Toast.makeText(ComposeTweetActivity.this, R.string.post_success, Toast.LENGTH_SHORT).show();
            }
        };
        errorListener = new Response.ErrorListener() {
            @Override
            public void onErrorResponse(VolleyError error) {
                Log.e(TAG, "post tweet error!", error);
                WeiboAPIError weiboAPIError = WeiboAPIError.fromVolleyError(error);
                Toast.makeText(ComposeTweetActivity.this, weiboAPIError.error, Toast.LENGTH_LONG).show();
            }
        };
        mCustomizedBar.findViewById(R.id.action_geo).setOnClickListener(this);
        mCustomizedBar.findViewById(R.id.action_send).setOnClickListener(this);
        mLocationMarker.setOnClickListener(this);
    }

    private LocationSupport requireLocation() {
        Toast.makeText(this, getString(R.string.locating), Toast.LENGTH_SHORT).show();
        if (mLocationResult == null) {
            mLocationResult = new LocationSupport.LocationResult() {
                @Override
                public void gotLocation(Location location) {
                    final boolean ok;
                    if (location == null) {
                        ok = false;
                    } else {
                        mLatitude = location.getLatitude();
                        mLongitude = location.getLongitude();
                        ok = true;
                    }
                    mHandler.post(new Runnable() {
                        @Override
                        public void run() {
                            findViewById(R.id.location_marker).setVisibility(ok ? View.VISIBLE : View.GONE);
                            Toast.makeText(ComposeTweetActivity.this,
                                    ok ? R.string.locate_success : R.string.sorry_cannot_locate, Toast.LENGTH_SHORT)
                                    .show();
                        }
                    });
                }
            };
        }
        return new LocationSupport();
    }

    // ??
    private void invalidateLocation() {
        mLongitude = 0;
        mLatitude = 0;
        mLocationMarker.setVisibility(View.GONE);
    }

    @Override
    protected void onActivityResult(int requestCode, int resultCode, Intent data) {
        if (resultCode == RESULT_OK) { // for the moment, just check ok
            if (mPhotos == null) {
                initGallery();
            }
            switch (requestCode) {
            case GALLERY:
                if (data != null) {
                    mUris.add(data.getData());
                    mAdapter.notifyDataSetChanged();
                }
                break;
            case CAMERA:
                mUris.add(mTmpUri);
                mAdapter.notifyDataSetChanged();
                // reset
                mTmpUri = null;
                break;
            default:
                break;
            }
        }
    }

    private void initGallery() {
        ViewStub viewStub = (ViewStub) findViewById(R.id.view_stub);
        mPhotos = (GridView) viewStub.inflate();
        mUris = new LinkedList<Uri>();
        mAdapter = new ThumbsAdapter(this, mUris);
        mPhotos.setAdapter(mAdapter);
        // 
        mPhotos.setOnItemLongClickListener(new AdapterView.OnItemLongClickListener() {
            @Override
            public boolean onItemLongClick(AdapterView<?> parent, View view, int position, long id) {
                mUris.remove(position);
                mAdapter.notifyDataSetChanged();
                return true;
            }
        });
        // ?
        mPhotos.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Intent intent = new Intent(Intent.ACTION_VIEW);
                intent.setDataAndType(mUris.get(0), getString(R.string.mime_image));
                startActivity(intent);
            }
        });
    }

    @Override
    public void beforeTextChanged(CharSequence s, int start, int count, int after) {
        // no-op
    }

    @Override
    public void onTextChanged(CharSequence s, int start, int before, int count) {
        // no-op
    }

    @Override
    public void afterTextChanged(Editable s) {
        int count = 140 - mText.length();
        mTextCounter.setText(String.valueOf(count));
        if (count >= 10) {
            // def color
            mTextCounter.setTextColor(getResources().getColor(android.R.color.white));
        } else if (count <= 0) { // in fact, never lt 0
            mTextCounter.setTextColor(getResources().getColor(android.R.color.holo_red_dark));
        } else {
            mTextCounter.setTextColor(getResources().getColor(android.R.color.holo_orange_dark));
        }
    }

    private void sendTweet() {
        if (!CatnutUtils.hasLength(mText)) {
            Toast.makeText(this, R.string.require_not_empty, Toast.LENGTH_SHORT).show();
            return; // stop here
        }
        Toast.makeText(this, getString(R.string.background_sending), Toast.LENGTH_SHORT).show();
        Intent intent = new Intent(this, ComposeTweetService.class);
        Draft draft = new Draft();
        draft.status = mText.getText().toString();
        draft._long = (float) mLongitude;
        draft.lat = (float) mLatitude;
        if (mUris != null && mUris.size() > 0) { // 
            draft.pic = mUris.get(0);
            // ?
            mUris.clear();
            mAdapter.notifyDataSetChanged();
        }
        intent.putExtra(Draft.DRAFT, draft);
        mText.setText(null);
        invalidateLocation();
        // ??
        startService(intent);
    }

    @Override
    public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
        // ?
        int cursor = mText.getSelectionStart();
        mText.getText().insert(cursor, CatnutUtils.text2Emotion(this, TweetImageSpan.EMOTION_KEYS[position],
                getResources().getInteger(R.integer.icon_bound_px)));
        // focus
        mText.requestFocus();
    }

    @Override
    public Loader<Cursor> onCreateLoader(int id, Bundle args) {
        Log.d(TAG, mCurKeywords);
        StringBuilder where = new StringBuilder();
        where.append(User.screen_name).append(" like ").append(CatnutUtils.like(mCurKeywords)).append(" or ")
                .append(User.remark).append(" like ").append(CatnutUtils.like(mCurKeywords));
        return CatnutUtils.getCursorLoader(this, CatnutProvider.parse(User.MULTIPLE),
                new String[] { User.screen_name, User.remark, BaseColumns._ID, }, where.toString(), null,
                User.TABLE, null, null, null);
    }

    @Override
    public void onLoadFinished(Loader<Cursor> loader, Cursor data) {
        mMentionSearchAdapter.swapCursor(data);
    }

    @Override
    public void onLoaderReset(Loader<Cursor> loader) {
        mMentionSearchAdapter.swapCursor(null);
    }

    @Override
    public boolean onMenuItemActionExpand(MenuItem item) {
        if (mAutoCompleteTextView == null) {
            mAutoCompleteTextView = (AutoCompleteTextView) item.getActionView().findViewById(R.id.mention_search);
            mAutoCompleteTextView.setThreshold(1); // ??
            mMentionSearchAdapter = new MentionSearchAdapter(ComposeTweetActivity.this);
            mAutoCompleteTextView.setAdapter(mMentionSearchAdapter);
        }
        //      Toast.makeText(ComposeTweetActivity.this, getString(R.string.mention_helper_toast), Toast.LENGTH_SHORT).show();
        final View clear = item.getActionView().findViewById(R.id.clear);
        clear.setOnClickListener(ComposeTweetActivity.this);
        mAutoCompleteTextView.addTextChangedListener(new TextWatcher() {
            @Override
            public void beforeTextChanged(CharSequence s, int start, int count, int after) {
                // no-op
            }

            @Override
            public void onTextChanged(CharSequence s, int start, int before, int count) {
                // no-op
            }

            @Override
            public void afterTextChanged(Editable s) {
                String text = s.toString();
                if (text.length() > 0) {
                    clear.setVisibility(View.VISIBLE);
                } else {
                    clear.setVisibility(View.GONE);
                }
                String key = text.trim();
                if (!TextUtils.isEmpty(key) && !key.equals(mCurKeywords)) {
                    mCurKeywords = key;
                    getLoaderManager().restartLoader(0, null, ComposeTweetActivity.this);
                }
            }
        });
        mAutoCompleteTextView.setOnItemClickListener(new AdapterView.OnItemClickListener() {
            @Override
            public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
                Cursor cursor = (Cursor) mMentionSearchAdapter.getItem(position);
                mText.getText().append(getString(R.string.mention_text,
                        cursor.getString(cursor.getColumnIndex(User.screen_name))));
                mText.setSelection(mText.length());
            }
        });
        mText.clearFocus();
        mAutoCompleteTextView.requestFocus();
        mAutoCompleteTextView.post(new Runnable() {
            @Override
            public void run() {
                mInputMethodManager.showSoftInput(mAutoCompleteTextView, InputMethodManager.SHOW_FORCED);
            }
        });
        return true;
    }

    // ?
    private void shorten() {
        String text = mText.getText().toString();
        final Matcher matcher = TweetTextView.WEB_URL.matcher(text);
        String urls = "";
        while (matcher.find()) {
            urls += matcher.group() + ""; // ?
        }
        // http request
        if (!TextUtils.isEmpty(urls)) {
            final ProgressDialog dialog = ProgressDialog.show(this, null, getString(R.string.converting), true,
                    false);
            CatnutAPI api = StuffAPI.shorten(urls.split(""));
            mApp.getRequestQueue()
                    .add(new JsonObjectRequest(api.method, api.uri, null, new Response.Listener<JSONObject>() {
                        @Override
                        public void onResponse(JSONObject response) {
                            matcher.reset(); // ?
                            JSONArray _urls = response.optJSONArray("urls");
                            StringBuffer sb = new StringBuffer();
                            int i = 0;
                            try {
                                while (matcher.find()) {
                                    matcher.appendReplacement(sb, _urls.optJSONObject(i).optString("url_short"));
                                    i++;
                                }
                                matcher.appendTail(sb);
                                mText.setText(sb);
                                mText.setSelection(mText.length());
                            } catch (Exception ex) {
                                Log.e(TAG, "replace shorten url error!", ex);
                            }
                            dialog.dismiss();
                        }
                    }, new Response.ErrorListener() {
                        @Override
                        public void onErrorResponse(VolleyError error) {
                            dialog.dismiss();
                            WeiboAPIError weiboAPIError = WeiboAPIError.fromVolleyError(error);
                            Toast.makeText(ComposeTweetActivity.this, weiboAPIError.error, Toast.LENGTH_SHORT)
                                    .show();
                        }
                    }));
        } else {
            Toast.makeText(this, getString(R.string.no_links_hint), Toast.LENGTH_SHORT).show();
        }
    }

    @Override
    public boolean onMenuItemActionCollapse(MenuItem item) {
        mInputMethodManager.hideSoftInputFromInputMethod(mAutoCompleteTextView.getWindowToken(), 0);
        mAutoCompleteTextView.setText(null);
        mText.requestFocus();
        return true;
    }

    private class SliderListener extends SlidingPaneLayout.SimplePanelSlideListener {
        @Override
        public void onPanelOpened(View panel) {
            ComposeTweetActivity.this.onPanelOpened();
        }

        @Override
        public void onPanelClosed(View panel) {
            ComposeTweetActivity.this.onPanelClosed();
        }
    }

    private class FirstLayoutListener implements ViewTreeObserver.OnGlobalLayoutListener {
        @Override
        public void onGlobalLayout() {
            onFirstLayout();
            mSlidingPaneLayout.getViewTreeObserver().removeOnGlobalLayoutListener(this);
        }
    }

    private void onPanelClosed() {
        mActionBar.setDisplayHomeAsUpEnabled(true);
        mActionBar.setHomeButtonEnabled(true);
        mActionBar.setTitle(mEmotionTitle);
    }

    private void onPanelOpened() {
        mActionBar.setHomeButtonEnabled(false);
        mActionBar.setDisplayHomeAsUpEnabled(false);
        mActionBar.setTitle(mTitle);
        mText.requestFocus();
    }

    private void onFirstLayout() {
        if (mSlidingPaneLayout.isSlideable() && !mSlidingPaneLayout.isOpen()) {
            onPanelClosed();
        } else {
            onPanelOpened();
        }
    }

    private class ThumbsAdapter extends ArrayAdapter<Uri> {

        private int mScreenSize;

        public ThumbsAdapter(Context context, List<Uri> uris) {
            super(context, 0, uris);
            mScreenSize = CatnutUtils.getScreenWidth(context);
        }

        @Override
        public View getView(int position, View convertView, ViewGroup parent) {
            final ImageView photo;
            if (convertView == null) {
                photo = new ImageView(ComposeTweetActivity.this);
                photo.setAdjustViewBounds(true);
                //            photo.setScaleType(ImageView.ScaleType.CENTER_CROP);
            } else {
                photo = (ImageView) convertView;
            }
            // load image efficiently
            Picasso.with(getContext()).load(getItem(position))
                    .resize(mScreenSize, (int) (Constants.GOLDEN_RATIO * mScreenSize)).centerCrop().into(photo);
            return photo;
        }
    }
}