org.catnut.fragment.ProfileFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.catnut.fragment.ProfileFragment.java

Source

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

import android.app.Activity;
import android.app.Fragment;
import android.content.AsyncQueryHandler;
import android.content.Intent;
import android.content.SharedPreferences;
import android.database.Cursor;
import android.graphics.Bitmap;
import android.graphics.Typeface;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.os.Bundle;
import android.os.Handler;
import android.os.Message;
import android.provider.BaseColumns;
import android.support.v4.view.PagerAdapter;
import android.support.v4.view.ViewPager;
import android.text.Html;
import android.text.TextUtils;
import android.text.format.DateUtils;
import android.view.LayoutInflater;
import android.view.Menu;
import android.view.MenuInflater;
import android.view.MenuItem;
import android.view.View;
import android.view.ViewGroup;
import android.view.ViewStub;
import android.view.ViewTreeObserver;
import android.widget.ImageView;
import android.widget.TextView;
import android.widget.Toast;
import com.android.volley.Response;
import com.android.volley.VolleyError;
import com.squareup.picasso.Picasso;
import com.squareup.picasso.Target;
import com.viewpagerindicator.LinePageIndicator;
import org.catnut.R;
import org.catnut.api.FriendshipsAPI;
import org.catnut.api.UserAPI;
import org.catnut.core.CatnutApp;
import org.catnut.core.CatnutProvider;
import org.catnut.core.CatnutRequest;
import org.catnut.metadata.Status;
import org.catnut.metadata.User;
import org.catnut.processor.UserProcessor;
import org.catnut.support.QuickReturnScrollView;
import org.catnut.support.TweetImageSpan;
import org.catnut.support.TweetTextView;
import org.catnut.ui.ProfileActivity;
import org.catnut.ui.SingleFragmentActivity;
import org.catnut.ui.TweetActivity;
import org.catnut.util.CatnutUtils;
import org.catnut.util.Constants;
import org.catnut.util.DateTime;
import org.json.JSONObject;

/**
 * ??
 *
 * @author longkai
 */
public class ProfileFragment extends Fragment
        implements SharedPreferences.OnSharedPreferenceChangeListener, QuickReturnScrollView.Callbacks {

    private static final String[] PROJECTION = new String[] { BaseColumns._ID, User.remark, User.verified,
            User.screen_name, User.profile_url, User.location, User.description, User.avatar_large, User.avatar_hd,
            User.cover_image, User.favourites_count, User.friends_count, User.statuses_count, User.followers_count,
            User.following, User.verified_reason };

    private CatnutApp mApp;
    private Menu mMenu;

    private ScrollSettleHandler mScrollSettleHandler = new ScrollSettleHandler();
    private QuickReturnScrollView mQuickReturnScrollView;
    private View mQuickReturnView;
    private View mQuickReturnPlaceHolderView;
    private int mMinRawY = 0;
    private int mState = STATE_ON_SCREEN;
    private int mQuickReturnHeight;
    private int mMaxScrollY;

    private long mUid;
    private String mScreenName;
    private String mCoverUrl;
    private String mAvatarUrl;
    private String mAvatarHdUrl;
    private boolean mVerified;
    private boolean mFollowing; // ?
    private String mRemark;
    private String mDescription;
    private String mLocation;
    private String mProfileUrl;
    private String mVerifiedReason;

    private ViewPager mViewPager;
    private LinePageIndicator mIndicator;
    private View mPlaceHolder;
    private View mTweetsCount;
    private View mFollowingsCount;
    private View mFollowersCount;
    private View mTweetLayout;
    private View mRetweetLayout;

    private Typeface mTypeface;
    private float mLineSpacing = 1.0f;

    private View.OnClickListener tweetsOnclickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ProfileActivity activity = (ProfileActivity) getActivity();
            activity.flipCard(UserTimelineFragment.getFragment(mUid, mScreenName), null, true);
        }
    };

    private View.OnClickListener followersOnclickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ProfileActivity activity = (ProfileActivity) getActivity();
            activity.flipCard(TransientUsersFragment.getFragment(mScreenName, false), null, true);
        }
    };

    private View.OnClickListener followingsOnClickListener = new View.OnClickListener() {
        @Override
        public void onClick(View v) {
            ProfileActivity activity = (ProfileActivity) getActivity();
            activity.flipCard(TransientUsersFragment.getFragment(mScreenName, true), null, true);
        }
    };

    private Target profileTarget = new Target() {
        @Override
        public void onBitmapLoaded(Bitmap bitmap, Picasso.LoadedFrom from) {
            try {
                mPlaceHolder.setBackground(new BitmapDrawable(getResources(), bitmap));
            } catch (Exception e) {
                // no-op
            }
        }

        @Override
        public void onBitmapFailed(Drawable errorDrawable) {
            try {
                mPlaceHolder.setBackground(errorDrawable);
            } catch (Exception e) {
                // no-op
            }
        }

        @Override
        public void onPrepareLoad(Drawable placeHolderDrawable) {
            mPlaceHolder.setBackground(placeHolderDrawable);
        }
    };

    public static ProfileFragment getFragment(long uid, String screenName) {
        Bundle args = new Bundle();
        args.putLong(Constants.ID, uid);
        args.putString(User.screen_name, screenName);
        ProfileFragment fragment = new ProfileFragment();
        fragment.setArguments(args);
        return fragment;
    }

    @Override
    public void onAttach(Activity activity) {
        super.onAttach(activity);
        mApp = CatnutApp.getTingtingApp();
        Bundle args = getArguments();
        mUid = args.getLong(Constants.ID);
        mScreenName = args.getString(User.screen_name);
    }

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setHasOptionsMenu(true);
        SharedPreferences preferences = mApp.getPreferences();
        mTypeface = CatnutUtils.getTypeface(preferences, getString(R.string.pref_customize_tweet_font),
                getString(R.string.default_typeface));
        mLineSpacing = CatnutUtils.getLineSpacing(preferences, getString(R.string.pref_line_spacing),
                getString(R.string.default_line_spacing));
    }

    @Override
    public void onStart() {
        super.onStart();
        getActivity().getActionBar().setTitle(mScreenName);
    }

    @Override
    public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
        View view = inflater.inflate(R.layout.profile, container, false);
        mQuickReturnScrollView = (QuickReturnScrollView) view;
        mQuickReturnScrollView.setCallbacks(this);

        mViewPager = (ViewPager) view.findViewById(R.id.pager);
        mIndicator = (LinePageIndicator) view.findViewById(R.id.indicator);
        mPlaceHolder = view.findViewById(R.id.place_holder);
        mTweetsCount = view.findViewById(R.id.tweets_count);
        mFollowingsCount = view.findViewById(R.id.following_count);
        mFollowersCount = view.findViewById(R.id.followers_count);
        mTweetLayout = view.findViewById(R.id.tweet_layout);
        view.findViewById(R.id.action_tweets).setOnClickListener(tweetsOnclickListener);
        view.findViewById(R.id.action_followers).setOnClickListener(followersOnclickListener);
        view.findViewById(R.id.action_followings).setOnClickListener(followingsOnClickListener);

        mQuickReturnPlaceHolderView = view.findViewById(R.id.quick_return_place_holder);
        mQuickReturnView = view.findViewById(R.id.place_holder);
        mQuickReturnScrollView.getViewTreeObserver()
                .addOnGlobalLayoutListener(new ViewTreeObserver.OnGlobalLayoutListener() {
                    @Override
                    public void onGlobalLayout() {
                        onScrollChanged(mQuickReturnScrollView.getScrollY());
                        mMaxScrollY = mQuickReturnScrollView.computeVerticalScrollRange()
                                - mQuickReturnScrollView.getHeight();
                        mQuickReturnHeight = mQuickReturnView.getHeight();
                    }
                });
        return view;
    }

    @Override
    public void onViewCreated(View view, Bundle savedInstanceState) {
        // ??*_*?view?
        String query = CatnutUtils.buildQuery(PROJECTION, User.screen_name + "=" + CatnutUtils.quote(mScreenName),
                User.TABLE, null, null, null);
        new AsyncQueryHandler(getActivity().getContentResolver()) {
            @Override
            protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
                if (cursor.moveToNext()) {
                    injectProfile(cursor);
                } else {
                    // fall back to load from network...
                    mApp.getRequestQueue().add(new CatnutRequest(getActivity(), UserAPI.profile(mScreenName),
                            new UserProcessor.UserProfileProcessor(), new Response.Listener<JSONObject>() {
                                @Override
                                public void onResponse(JSONObject response) {
                                    injectProfile(response);
                                }
                            }, new Response.ErrorListener() {
                                @Override
                                public void onErrorResponse(VolleyError error) {
                                    Toast.makeText(getActivity(), getString(R.string.user_not_found),
                                            Toast.LENGTH_SHORT).show();
                                }
                            }));
                }
                cursor.close();
            }
        }.startQuery(0, null, CatnutProvider.parse(User.MULTIPLE), null, query, null, null);
        // ??
        if (mApp.getPreferences().getBoolean(getString(R.string.pref_show_latest_tweet), true)) {
            String queryLatestTweet = CatnutUtils.buildQuery(new String[] { Status.columnText,
                    //                     Status.thumbnail_pic,
                    Status.bmiddle_pic, Status.comments_count, Status.reposts_count, Status.retweeted_status,
                    Status.attitudes_count, Status.source, Status.created_at, },
                    new StringBuilder("uid=(select _id from ").append(User.TABLE).append(" where ")
                            .append(User.screen_name).append("=").append(CatnutUtils.quote(mScreenName)).append(")")
                            .append(" and ").append(Status.TYPE).append(" in(").append(Status.HOME).append(",")
                            .append(Status.RETWEET).append(",").append(Status.OTHERS).append(")").toString(),
                    Status.TABLE, null, BaseColumns._ID + " desc", "1");
            new AsyncQueryHandler(getActivity().getContentResolver()) {
                @Override
                protected void onQueryComplete(int token, Object cookie, Cursor cursor) {
                    if (cursor.moveToNext()) {
                        mTweetLayout.setOnClickListener(tweetsOnclickListener);
                        ViewStub viewStub = (ViewStub) mTweetLayout.findViewById(R.id.latest_tweet);
                        View tweet = viewStub.inflate();
                        mRetweetLayout = tweet.findViewById(R.id.retweet);
                        tweet.findViewById(R.id.timeline).setVisibility(View.GONE);
                        tweet.findViewById(R.id.verified).setVisibility(View.GONE);
                        tweet.findViewById(R.id.tweet_overflow).setVisibility(View.GONE);
                        CatnutUtils.setText(tweet, R.id.nick, getString(R.string.latest_statues))
                                .setTextColor(getResources().getColor(R.color.actionbar_background));
                        String tweetText = cursor.getString(cursor.getColumnIndex(Status.columnText));
                        TweetImageSpan tweetImageSpan = new TweetImageSpan(getActivity());
                        TweetTextView text = (TweetTextView) CatnutUtils.setText(tweet, R.id.text,
                                tweetImageSpan.getImageSpan(tweetText));
                        CatnutUtils.vividTweet(text, null);
                        CatnutUtils.setTypeface(text, mTypeface);
                        text.setLineSpacing(0, mLineSpacing);

                        String thumbsUrl = cursor.getString(cursor.getColumnIndex(Status.bmiddle_pic));
                        if (!TextUtils.isEmpty(thumbsUrl)) {
                            View thumbs = tweet.findViewById(R.id.thumbs);
                            Picasso.with(getActivity()).load(thumbsUrl).placeholder(R.drawable.error)
                                    .error(R.drawable.error).into((ImageView) thumbs);
                            thumbs.setVisibility(View.VISIBLE);
                        }

                        int replyCount = cursor.getInt(cursor.getColumnIndex(Status.comments_count));
                        CatnutUtils.setText(tweet, R.id.reply_count, CatnutUtils.approximate(replyCount));
                        int retweetCount = cursor.getInt(cursor.getColumnIndex(Status.reposts_count));
                        CatnutUtils.setText(tweet, R.id.reteet_count, CatnutUtils.approximate(retweetCount));
                        int favoriteCount = cursor.getInt(cursor.getColumnIndex(Status.attitudes_count));
                        CatnutUtils.setText(tweet, R.id.like_count, CatnutUtils.approximate(favoriteCount));
                        String source = cursor.getString(cursor.getColumnIndex(Status.source));
                        CatnutUtils.setText(tweet, R.id.source, Html.fromHtml(source).toString());
                        String create_at = cursor.getString(cursor.getColumnIndex(Status.created_at));
                        CatnutUtils
                                .setText(tweet, R.id.create_at,
                                        DateUtils.getRelativeTimeSpanString(DateTime.getTimeMills(create_at)))
                                .setVisibility(View.VISIBLE);
                        // retweet
                        final String jsonString = cursor.getString(cursor.getColumnIndex(Status.retweeted_status));
                        try {
                            JSONObject jsonObject = new JSONObject(jsonString);
                            TweetTextView retweet = (TweetTextView) mRetweetLayout.findViewById(R.id.retweet_text);
                            retweet.setText(jsonObject.optString(Status.text));
                            CatnutUtils.vividTweet(retweet, tweetImageSpan);
                            CatnutUtils.setTypeface(retweet, mTypeface);
                            retweet.setLineSpacing(0, mLineSpacing);
                            long mills = DateTime.getTimeMills(jsonObject.optString(Status.created_at));
                            TextView tv = (TextView) mRetweetLayout.findViewById(R.id.retweet_create_at);
                            tv.setText(DateUtils.getRelativeTimeSpanString(mills));
                            TextView retweetUserScreenName = (TextView) mRetweetLayout
                                    .findViewById(R.id.retweet_nick);
                            JSONObject user = jsonObject.optJSONObject(User.SINGLE);
                            if (user == null) {
                                retweetUserScreenName.setText(getString(R.string.unknown_user));
                            } else {
                                retweetUserScreenName.setText(user.optString(User.screen_name));
                                mRetweetLayout.setOnClickListener(new View.OnClickListener() {
                                    @Override
                                    public void onClick(View v) {
                                        Intent intent = new Intent(getActivity(), TweetActivity.class);
                                        intent.putExtra(Constants.JSON, jsonString);
                                        startActivity(intent);
                                    }
                                });
                            }
                        } catch (Exception e) {
                            mRetweetLayout.setVisibility(View.GONE);
                        }
                    }
                    cursor.close();
                }
            }.startQuery(0, null, CatnutProvider.parse(Status.MULTIPLE), null, queryLatestTweet, null, null);
        }
    }

    private void injectProfile(Cursor cursor) {
        // ?
        mUid = cursor.getLong(cursor.getColumnIndex(BaseColumns._ID));
        mAvatarUrl = cursor.getString(cursor.getColumnIndex(User.avatar_large));
        mAvatarHdUrl = cursor.getString(cursor.getColumnIndex(User.avatar_hd));
        mVerified = CatnutUtils.getBoolean(cursor, User.verified);
        mRemark = cursor.getString(cursor.getColumnIndex(User.remark));
        mDescription = cursor.getString(cursor.getColumnIndex(User.description));
        mLocation = cursor.getString(cursor.getColumnIndex(User.location));
        mProfileUrl = cursor.getString(cursor.getColumnIndex(User.profile_url));
        mCoverUrl = cursor.getString(cursor.getColumnIndex(User.cover_image));
        mVerifiedReason = cursor.getString(cursor.getColumnIndex(User.verified_reason));
        // +
        mFollowing = CatnutUtils.getBoolean(cursor, User.following);
        // menu
        buildMenu();
        // load??
        if (!TextUtils.isEmpty(mCoverUrl)) {
            Picasso.with(getActivity()).load(mCoverUrl).placeholder(R.drawable.default_fantasy)
                    .error(R.drawable.default_fantasy).into(profileTarget);
        } else {
            mPlaceHolder.setBackground(getResources().getDrawable(R.drawable.default_fantasy));
        }
        // ?
        mTweetsCount.setOnClickListener(tweetsOnclickListener);
        CatnutUtils.setText(mTweetsCount, android.R.id.text1,
                cursor.getString(cursor.getColumnIndex(User.statuses_count)));
        CatnutUtils.setText(mTweetsCount, android.R.id.text2, getString(R.string.tweets));
        // 
        mFollowersCount.setOnClickListener(followersOnclickListener);
        CatnutUtils.setText(mFollowersCount, android.R.id.text1,
                cursor.getString(cursor.getColumnIndex(User.followers_count)));
        CatnutUtils.setText(mFollowersCount, android.R.id.text2, getString(R.string.followers));
        // 
        mFollowingsCount.setOnClickListener(followingsOnClickListener);
        CatnutUtils.setText(mFollowingsCount, android.R.id.text1,
                cursor.getString(cursor.getColumnIndex(User.friends_count)));
        CatnutUtils.setText(mFollowingsCount, android.R.id.text2, getString(R.string.followings));
        // pager adapter, not fragment pager any more
        mViewPager.setAdapter(coverPager);
        mIndicator.setViewPager(mViewPager);
    }

    private void injectProfile(JSONObject json) {
        // ?
        mUid = json.optLong(Constants.ID);
        mAvatarUrl = json.optString(User.avatar_large);
        mAvatarHdUrl = json.optString(User.avatar_hd);
        mVerified = json.optBoolean(User.verified);
        mRemark = json.optString(User.remark);
        mDescription = json.optString(User.description);
        mLocation = json.optString(User.location);
        mProfileUrl = json.optString(User.profile_url);
        mCoverUrl = json.optString(User.cover_image);
        mVerifiedReason = json.optString(User.verified_reason);
        // +
        mFollowing = json.optBoolean(User.following);
        // menu
        buildMenu();
        // load??
        if (!TextUtils.isEmpty(mCoverUrl)) {
            Picasso.with(getActivity()).load(mCoverUrl).placeholder(R.drawable.default_fantasy)
                    .error(R.drawable.default_fantasy).into(profileTarget);
        } else {
            mPlaceHolder.setBackground(getResources().getDrawable(R.drawable.default_fantasy));
        }
        // ?
        mTweetsCount.setOnClickListener(tweetsOnclickListener);
        CatnutUtils.setText(mTweetsCount, android.R.id.text1, json.optString(User.statuses_count));
        CatnutUtils.setText(mTweetsCount, android.R.id.text2, getString(R.string.tweets));
        // 
        mFollowersCount.setOnClickListener(followersOnclickListener);
        CatnutUtils.setText(mFollowersCount, android.R.id.text1, json.optString(User.followers_count));
        CatnutUtils.setText(mFollowersCount, android.R.id.text2, getString(R.string.followers));
        // 
        mFollowingsCount.setOnClickListener(followingsOnClickListener);
        CatnutUtils.setText(mFollowingsCount, android.R.id.text1, json.optString(User.friends_count));

        CatnutUtils.setText(mFollowingsCount, android.R.id.text2, getString(R.string.followings));
        // pager adapter, not fragment pager any more
        mViewPager.setAdapter(coverPager);
        mIndicator.setViewPager(mViewPager);
    }

    @Override
    public void onCreateOptionsMenu(Menu menu, MenuInflater inflater) {
        mMenu = menu;
        buildMenu();
    }

    @Override
    public boolean onOptionsItemSelected(MenuItem item) {
        switch (item.getItemId()) {
        case R.id.action_follow:
            toggleFollow(true);
            break;
        case R.id.action_unfollow:
            toggleFollow(false);
            break;
        default:
            break;
        }
        return super.onOptionsItemSelected(item);
    }

    // menu?
    private void buildMenu() {
        if (mMenu != null && mAvatarUrl != null) { // ???sqlite
            // ???menu
            if (mMenu.findItem(R.id.action_follow) == null && mMenu.findItem(R.id.action_unfollow) == null) {
                if (!mFollowing) {
                    mMenu.add(Menu.NONE, R.id.action_follow, Menu.NONE, R.string.follow)
                            .setIcon(R.drawable.ic_title_follow).setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
                } else {
                    mMenu.add(Menu.NONE, R.id.action_unfollow, Menu.NONE, R.string.unfollow)
                            .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
                }
            }
        }
    }

    private void toggleFollow(final boolean follow) {
        mApp.getRequestQueue()
                .add(new CatnutRequest(getActivity(),
                        follow ? FriendshipsAPI.create(mScreenName, null) : FriendshipsAPI.destroy(mScreenName),
                        new UserProcessor.UserProfileProcessor(), new Response.Listener<JSONObject>() {
                            @Override
                            public void onResponse(JSONObject response) {
                                Toast.makeText(getActivity(),
                                        getString(follow ? R.string.follow_success : R.string.unfollow_success),
                                        Toast.LENGTH_SHORT).show();
                                if (follow) {
                                    mMenu.removeItem(R.id.action_follow);
                                    mMenu.add(Menu.NONE, R.id.action_follow, Menu.NONE, R.string.unfollow)
                                            .setShowAsAction(MenuItem.SHOW_AS_ACTION_NEVER);
                                } else {
                                    mMenu.removeItem(R.id.action_unfollow);
                                    mMenu.add(Menu.NONE, R.id.action_follow, Menu.NONE, R.string.follow)
                                            .setIcon(R.drawable.ic_title_follow)
                                            .setShowAsAction(MenuItem.SHOW_AS_ACTION_IF_ROOM);
                                }
                                mFollowing = !mFollowing;
                                // ?
                                final String str = follow ? "+1" : "-1";
                                new Thread(new Runnable() {
                                    @Override
                                    public void run() {
                                        long uid = mApp.getAccessToken().uid;
                                        String update = "update " + User.TABLE + " SET " + User.friends_count + "="
                                                + User.friends_count + str + " WHERE " + BaseColumns._ID + "="
                                                + uid;
                                        getActivity().getContentResolver().update(
                                                CatnutProvider.parse(User.MULTIPLE, uid), null, update, null);
                                    }
                                }).start();
                            }
                        }, new Response.ErrorListener() {
                            @Override
                            public void onErrorResponse(VolleyError error) {
                                Toast.makeText(getActivity(), error.getLocalizedMessage(), Toast.LENGTH_SHORT)
                                        .show();
                            }
                        }));
    }

    private final PagerAdapter coverPager = new PagerAdapter() {

        private LayoutInflater mLayoutInflater;

        @Override
        public int getCount() {
            return 2;
        }

        @Override
        public boolean isViewFromObject(View view, Object object) {
            return view == object;
        }

        @Override
        public void destroyItem(ViewGroup container, int position, Object object) {
            container.removeView((View) object);
        }

        @Override
        public Object instantiateItem(ViewGroup container, int position) {
            if (mLayoutInflater == null) {
                mLayoutInflater = LayoutInflater.from(getActivity());
            }
            switch (position) {
            case 0:
                View frontPage = mLayoutInflater.inflate(R.layout.profile_cover, container, false);
                ImageView avatar = (ImageView) frontPage.findViewById(R.id.avatar);
                Picasso.with(getActivity()).load(mAvatarUrl).placeholder(R.drawable.error).error(R.drawable.error)
                        .into(avatar);
                avatar.setOnClickListener(new View.OnClickListener() {
                    @Override
                    public void onClick(View v) {
                        Intent intent = SingleFragmentActivity.getIntent(getActivity(),
                                SingleFragmentActivity.PHOTO_VIEWER);
                        intent.putExtra(Constants.PIC, mAvatarHdUrl);
                        startActivity(intent);
                    }
                });
                TextView screenName = (TextView) frontPage.findViewById(R.id.screen_name);
                screenName.setText("@" + mScreenName);
                TextView remark = (TextView) frontPage.findViewById(R.id.remark);
                // ??id
                remark.setText(TextUtils.isEmpty(mRemark) ? mScreenName : mRemark);
                if (mVerified) {
                    frontPage.findViewById(R.id.verified).setVisibility(View.VISIBLE);
                }
                container.addView(frontPage);
                return frontPage;
            case 1:
                View introPage = mLayoutInflater.inflate(R.layout.profile_intro, container, false);
                CatnutUtils.setText(introPage, R.id.description,
                        TextUtils.isEmpty(mDescription) ? getString(R.string.no_description) : mDescription);
                CatnutUtils.setText(introPage, R.id.location, mLocation);
                if (!TextUtils.isEmpty(mVerifiedReason)) {
                    CatnutUtils.setText(introPage, R.id.verified_reason, mVerifiedReason)
                            .setVisibility(View.VISIBLE);
                }
                CatnutUtils.setText(introPage, R.id.profile_url, Constants.WEIBO_DOMAIN + mProfileUrl);
                container.addView(introPage);
                return introPage;
            default:
                return null;
            }
        }
    };

    @Override
    public void onSharedPreferenceChanged(SharedPreferences sharedPreferences, String key) {
        if (key.equals(getString(R.string.pref_show_latest_tweet))) {
            boolean show = mApp.getPreferences().getBoolean(key, true);
            // ??????????
            if (show) {
                mTweetLayout.setVisibility(View.VISIBLE);
            } else {
                mTweetLayout.setVisibility(View.GONE);
            }
        }
    }

    @Override
    public void onScrollChanged(int scrollY) {
        scrollY = Math.min(mMaxScrollY, scrollY);

        mScrollSettleHandler.onScroll(scrollY);

        int rawY = mQuickReturnPlaceHolderView.getTop() - scrollY;
        int translationY = 0;

        switch (mState) {
        case STATE_OFF_SCREEN:
            if (rawY <= mMinRawY) {
                mMinRawY = rawY;
            } else {
                mState = STATE_RETURNING;
            }
            translationY = rawY;
            break;

        case STATE_ON_SCREEN:
            if (rawY < -mQuickReturnHeight) {
                mState = STATE_OFF_SCREEN;
                mMinRawY = rawY;
            }
            translationY = rawY;
            break;

        case STATE_RETURNING:
            translationY = (rawY - mMinRawY) - mQuickReturnHeight;
            if (translationY > 0) {
                translationY = 0;
                mMinRawY = rawY - mQuickReturnHeight;
            }

            if (rawY > 0) {
                mState = STATE_ON_SCREEN;
                translationY = rawY;
            }

            if (translationY < -mQuickReturnHeight) {
                mState = STATE_OFF_SCREEN;
                mMinRawY = rawY;
            }
            break;
        }
        mQuickReturnView.animate().cancel();
        mQuickReturnView.setTranslationY(translationY + scrollY);
    }

    @Override
    public void onDownMotionEvent() {
        mScrollSettleHandler.setSettleEnabled(false);
    }

    @Override
    public void onUpOrCancelMotionEvent() {
        mScrollSettleHandler.setSettleEnabled(true);
        mScrollSettleHandler.onScroll(mQuickReturnScrollView.getScrollY());
    }

    private class ScrollSettleHandler extends Handler {
        private static final int SETTLE_DELAY_MILLIS = 100;

        private int mSettledScrollY = Integer.MIN_VALUE;
        private boolean mSettleEnabled;

        public void onScroll(int scrollY) {
            if (mSettledScrollY != scrollY) {
                // Clear any pending messages and post delayed
                removeMessages(0);
                sendEmptyMessageDelayed(0, SETTLE_DELAY_MILLIS);
                mSettledScrollY = scrollY;
            }
        }

        public void setSettleEnabled(boolean settleEnabled) {
            mSettleEnabled = settleEnabled;
        }

        @Override
        public void handleMessage(Message msg) {
            // Handle the scroll settling.
            if (STATE_RETURNING == mState && mSettleEnabled) {
                int mDestTranslationY;
                if (mSettledScrollY - mQuickReturnView.getTranslationY() > mQuickReturnHeight / 2) {
                    mState = STATE_OFF_SCREEN;
                    mDestTranslationY = Math.max(mSettledScrollY - mQuickReturnHeight,
                            mQuickReturnPlaceHolderView.getTop());
                } else {
                    mDestTranslationY = mSettledScrollY;
                }

                mMinRawY = mQuickReturnPlaceHolderView.getTop() - mQuickReturnHeight - mDestTranslationY;
                mQuickReturnView.animate().translationY(mDestTranslationY);
            }
            mSettledScrollY = Integer.MIN_VALUE; // reset
        }
    }
}