org.mariotaku.twidere.fragment.UserFragment.java Source code

Java tutorial

Introduction

Here is the source code for org.mariotaku.twidere.fragment.UserFragment.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.fragment;

import android.animation.ArgbEvaluator;
import android.annotation.TargetApi;
import android.app.Activity;
import android.content.ContentResolver;
import android.content.Context;
import android.content.Intent;
import android.content.res.Resources;
import android.graphics.Color;
import android.graphics.Outline;
import android.graphics.Rect;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.net.Uri;
import android.nfc.NdefMessage;
import android.nfc.NdefRecord;
import android.nfc.NfcAdapter.CreateNdefMessageCallback;
import android.nfc.NfcEvent;
import android.os.Build;
import android.os.Bundle;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.annotation.StringDef;
import android.support.annotation.UiThread;
import android.support.v4.app.Fragment;
import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.Loader;
import android.support.v4.content.res.ResourcesCompat;
import android.support.v4.view.ViewCompat;
import android.support.v4.view.ViewPager;
import android.support.v4.view.ViewPager.OnPageChangeListener;
import android.support.v4.view.WindowCompat;
import android.support.v7.app.ActionBar;
import android.support.v7.app.AppCompatActivity;
import android.support.v7.widget.Toolbar;
import android.text.SpannableStringBuilder;
import android.text.TextUtils;
import android.text.util.Linkify;
import android.util.Pair;
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.OnTouchListener;
import android.view.ViewGroup;
import android.view.Window;
import android.view.animation.AnimationUtils;
import android.widget.Button;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.TextView;

import com.afollestad.appthemeengine.ATEActivity;
import com.afollestad.appthemeengine.Config;
import com.squareup.otto.Subscribe;

import org.apache.commons.lang3.ObjectUtils;
import org.mariotaku.abstask.library.AbstractTask;
import org.mariotaku.abstask.library.TaskStarter;
import org.mariotaku.microblog.library.MicroBlog;
import org.mariotaku.microblog.library.MicroBlogException;
import org.mariotaku.microblog.library.twitter.model.FriendshipUpdate;
import org.mariotaku.microblog.library.twitter.model.Relationship;
import org.mariotaku.sqliteqb.library.Expression;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.AccountSelectorActivity;
import org.mariotaku.twidere.activity.BaseActivity;
import org.mariotaku.twidere.activity.ColorPickerDialogActivity;
import org.mariotaku.twidere.activity.LinkHandlerActivity;
import org.mariotaku.twidere.activity.UserListSelectorActivity;
import org.mariotaku.twidere.adapter.SupportTabsAdapter;
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback;
import org.mariotaku.twidere.fragment.iface.IToolBarSupportFragment;
import org.mariotaku.twidere.fragment.iface.RefreshScrollTopInterface;
import org.mariotaku.twidere.fragment.iface.SupportFragmentCallback;
import org.mariotaku.twidere.graphic.ActionBarColorDrawable;
import org.mariotaku.twidere.graphic.ActionIconDrawable;
import org.mariotaku.twidere.loader.ParcelableUserLoader;
import org.mariotaku.twidere.model.CachedRelationship;
import org.mariotaku.twidere.model.CachedRelationshipValuesCreator;
import org.mariotaku.twidere.model.ConsumerKeyType;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableCredentials;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableUser;
import org.mariotaku.twidere.model.ParcelableUserList;
import org.mariotaku.twidere.model.ParcelableUserValuesCreator;
import org.mariotaku.twidere.model.SingleResponse;
import org.mariotaku.twidere.model.SupportTabSpec;
import org.mariotaku.twidere.model.UserKey;
import org.mariotaku.twidere.model.message.FriendshipTaskEvent;
import org.mariotaku.twidere.model.message.FriendshipUpdatedEvent;
import org.mariotaku.twidere.model.message.ProfileUpdatedEvent;
import org.mariotaku.twidere.model.message.TaskStateChangedEvent;
import org.mariotaku.twidere.model.util.ParcelableAccountUtils;
import org.mariotaku.twidere.model.util.ParcelableCredentialsUtils;
import org.mariotaku.twidere.model.util.ParcelableMediaUtils;
import org.mariotaku.twidere.model.util.ParcelableStatusUtils;
import org.mariotaku.twidere.model.util.ParcelableUserUtils;
import org.mariotaku.twidere.model.util.UserKeyUtils;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedRelationships;
import org.mariotaku.twidere.provider.TwidereDataStore.CachedUsers;
import org.mariotaku.twidere.provider.TwidereDataStore.Filters;
import org.mariotaku.twidere.util.AsyncTwitterWrapper;
import org.mariotaku.twidere.util.ContentValuesCreator;
import org.mariotaku.twidere.util.DataStoreUtils;
import org.mariotaku.twidere.util.IntentUtils;
import org.mariotaku.twidere.util.InternalTwitterContentUtils;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler;
import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback;
import org.mariotaku.twidere.util.LinkCreator;
import org.mariotaku.twidere.util.MenuUtils;
import org.mariotaku.twidere.util.MicroBlogAPIFactory;
import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TwidereLinkify;
import org.mariotaku.twidere.util.TwidereLinkify.OnLinkClickListener;
import org.mariotaku.twidere.util.TwidereMathUtils;
import org.mariotaku.twidere.util.UserColorNameManager;
import org.mariotaku.twidere.util.UserColorNameManager.UserColorChangedListener;
import org.mariotaku.twidere.util.UserColorNameManager.UserNicknameChangedListener;
import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.menu.TwidereMenuInfo;
import org.mariotaku.twidere.util.support.ActivitySupport;
import org.mariotaku.twidere.util.support.ActivitySupport.TaskDescriptionCompat;
import org.mariotaku.twidere.util.support.ViewSupport;
import org.mariotaku.twidere.util.support.WindowSupport;
import org.mariotaku.twidere.view.ColorLabelRelativeLayout;
import org.mariotaku.twidere.view.ExtendedRelativeLayout;
import org.mariotaku.twidere.view.HeaderDrawerLayout;
import org.mariotaku.twidere.view.HeaderDrawerLayout.DrawerCallback;
import org.mariotaku.twidere.view.ProfileBannerImageView;
import org.mariotaku.twidere.view.ProfileBannerSpace;
import org.mariotaku.twidere.view.ShapedImageView;
import org.mariotaku.twidere.view.TabPagerIndicator;
import org.mariotaku.twidere.view.TintedStatusFrameLayout;
import org.mariotaku.twidere.view.iface.IExtendedView.OnSizeChangedListener;

import java.util.Calendar;
import java.util.Locale;

import edu.tsinghua.hotmobi.HotMobiLogger;
import edu.tsinghua.hotmobi.model.UserEvent;

public class UserFragment extends BaseSupportFragment
        implements OnClickListener, OnLinkClickListener, OnSizeChangedListener, OnTouchListener, DrawerCallback,
        SupportFragmentCallback, SystemWindowsInsetsCallback, RefreshScrollTopInterface, OnPageChangeListener,
        KeyboardShortcutCallback, UserColorChangedListener, UserNicknameChangedListener, IToolBarSupportFragment {

    private static final ArgbEvaluator sArgbEvaluator = new ArgbEvaluator();
    private static final int LOADER_ID_USER = 1;
    private static final int LOADER_ID_FRIENDSHIP = 2;

    private static final int TAB_POSITION_STATUSES = 0;
    private static final int TAB_POSITION_MEDIA = 1;
    private static final int TAB_POSITION_FAVORITES = 2;
    private static final String TAB_TYPE_STATUSES = "statuses";
    private static final String TAB_TYPE_MEDIA = "media";
    private static final String TAB_TYPE_FAVORITES = "favorites";

    // Views
    private ShapedImageView mProfileImageView;
    private ImageView mProfileTypeView;
    private ProfileBannerImageView mProfileBannerView;
    private View mProfileBirthdayBannerView;
    private TextView mNameView, mScreenNameView, mDescriptionView, mLocationView, mURLView, mCreatedAtView,
            mHeaderErrorTextView;
    private TextView mListedCount, mFollowersCount, mFriendsCount, mGroupsCount;
    private View mDescriptionContainer, mLocationContainer, mURLContainer;
    private View mListedContainer, mFollowersContainer, mFriendsContainer, mGroupsContainer;
    private ImageView mHeaderErrorIcon;
    private ColorLabelRelativeLayout mProfileNameContainer;
    private View mProgressContainer, mHeaderErrorContainer;
    private View mCardContent;
    private ProfileBannerSpace mProfileBannerSpace;
    private HeaderDrawerLayout mHeaderDrawerLayout;
    private ViewPager mViewPager;
    private TabPagerIndicator mPagerIndicator;
    private View mWindowOverlay;
    private View mProfileBannerContainer;
    private ExtendedRelativeLayout mProfileContentContainer;
    private Button mFollowButton;
    private ProgressBar mFollowProgress;
    private View mPagesContent, mPagesErrorContainer;
    private ImageView mPagesErrorIcon;
    private TextView mPagesErrorText;
    private View mProfileNameBackground;
    private View mProfileDetailsContainer;
    private View mFollowingYouIndicator;
    private Toolbar mToolbar;
    private TintedStatusFrameLayout mTintedStatusFrameLayout;

    private ActionBarDrawable mActionBarBackground;
    private SupportTabsAdapter mPagerAdapter;

    // Data fields
    private ParcelableUser mUser;
    private ParcelableAccount mAccount;
    private UserRelationship mRelationship;
    private Locale mLocale;
    private boolean mGetUserInfoLoaderInitialized, mGetFriendShipLoaderInitialized;
    private int mBannerWidth;
    private int mCardBackgroundColor;
    private int mActionBarShadowColor;
    private int mUiColor, mPrimaryColor, mPrimaryColorDark;
    private boolean mNameFirst;
    private int mPreviousTabItemIsDark, mPreviousActionBarItemIsDark;
    private boolean mHideBirthdayView;
    private UserEvent mUserEvent;

    private final LoaderCallbacks<SingleResponse<UserRelationship>> mFriendshipLoaderCallbacks = new LoaderCallbacks<SingleResponse<UserRelationship>>() {

        @Override
        public Loader<SingleResponse<UserRelationship>> onCreateLoader(final int id, final Bundle args) {
            invalidateOptionsMenu();
            final UserKey accountKey = args.getParcelable(EXTRA_ACCOUNT_KEY);
            final ParcelableUser user = args.getParcelable(EXTRA_USER);
            if (user != null && user.key.equals(accountKey)) {
                mFollowingYouIndicator.setVisibility(View.GONE);
                mFollowButton.setVisibility(View.VISIBLE);
                mFollowProgress.setVisibility(View.VISIBLE);
            } else {
                mFollowingYouIndicator.setVisibility(View.GONE);
                mFollowButton.setVisibility(View.GONE);
                mFollowProgress.setVisibility(View.VISIBLE);
            }
            return new UserRelationshipLoader(getActivity(), accountKey, user);
        }

        @Override
        public void onLoaderReset(final Loader<SingleResponse<UserRelationship>> loader) {

        }

        @Override
        public void onLoadFinished(final Loader<SingleResponse<UserRelationship>> loader,
                final SingleResponse<UserRelationship> data) {
            mFollowProgress.setVisibility(View.GONE);
            final ParcelableUser user = getUser();
            final UserRelationship relationship = data.getData();
            displayRelationship(user, relationship);
            updateOptionsMenuVisibility();
        }

    };
    private final LoaderCallbacks<SingleResponse<ParcelableUser>> mUserInfoLoaderCallbacks = new LoaderCallbacks<SingleResponse<ParcelableUser>>() {

        @Override
        public Loader<SingleResponse<ParcelableUser>> onCreateLoader(final int id, final Bundle args) {
            final boolean omitIntentExtra = args.getBoolean(EXTRA_OMIT_INTENT_EXTRA, true);
            final UserKey accountKey = args.getParcelable(EXTRA_ACCOUNT_KEY);
            final UserKey userId = args.getParcelable(EXTRA_USER_KEY);
            final String screenName = args.getString(EXTRA_SCREEN_NAME);
            if (mUser == null && (!omitIntentExtra || !args.containsKey(EXTRA_USER))) {
                mCardContent.setVisibility(View.GONE);
                mHeaderErrorContainer.setVisibility(View.GONE);
                mProgressContainer.setVisibility(View.VISIBLE);
                mHeaderErrorTextView.setText(null);
                mHeaderErrorTextView.setVisibility(View.GONE);
            }
            final ParcelableUser user = mUser;
            final boolean loadFromCache = user == null || !user.is_cache && user.key.maybeEquals(userId);
            return new ParcelableUserLoader(getActivity(), accountKey, userId, screenName, getArguments(),
                    omitIntentExtra, loadFromCache);
        }

        @Override
        public void onLoaderReset(final Loader<SingleResponse<ParcelableUser>> loader) {

        }

        @Override
        public void onLoadFinished(final Loader<SingleResponse<ParcelableUser>> loader,
                final SingleResponse<ParcelableUser> data) {
            final FragmentActivity activity = getActivity();
            if (activity == null)
                return;
            if (data.hasData()) {
                final ParcelableUser user = data.getData();
                mCardContent.setVisibility(View.VISIBLE);
                mHeaderErrorContainer.setVisibility(View.GONE);
                mProgressContainer.setVisibility(View.GONE);
                ParcelableAccount account = data.getExtras().getParcelable(EXTRA_ACCOUNT);
                displayUser(user, account);
                if (user.is_cache) {
                    final Bundle args = new Bundle();
                    args.putParcelable(EXTRA_ACCOUNT_KEY, user.account_key);
                    args.putParcelable(EXTRA_USER_KEY, user.key);
                    args.putString(EXTRA_SCREEN_NAME, user.screen_name);
                    args.putBoolean(EXTRA_OMIT_INTENT_EXTRA, true);
                    getLoaderManager().restartLoader(LOADER_ID_USER, args, this);
                }
                updateOptionsMenuVisibility();
            } else if (mUser != null && mUser.is_cache) {
                mCardContent.setVisibility(View.VISIBLE);
                mHeaderErrorContainer.setVisibility(View.GONE);
                mProgressContainer.setVisibility(View.GONE);
                displayUser(mUser, mAccount);
                updateOptionsMenuVisibility();
            } else {
                if (data.hasException()) {
                    mHeaderErrorTextView.setText(Utils.getErrorMessage(activity, data.getException()));
                    mHeaderErrorTextView.setVisibility(View.VISIBLE);
                }
                mCardContent.setVisibility(View.GONE);
                mHeaderErrorContainer.setVisibility(View.VISIBLE);
                mProgressContainer.setVisibility(View.GONE);
                displayUser(null, null);
                updateOptionsMenuVisibility();
            }
        }

    };

    private void updateOptionsMenuVisibility() {
        setHasOptionsMenu(mUser != null && mRelationship != null);
    }

    private void displayRelationship(@Nullable final ParcelableUser user,
            @Nullable final UserRelationship userRelationship) {
        if (user == null) {
            mRelationship = null;
            return;
        }
        if (user.account_key.maybeEquals(user.key)) {
            mFollowButton.setText(R.string.edit);
            mFollowButton.setVisibility(View.VISIBLE);
            mRelationship = userRelationship;
            return;
        }
        if (userRelationship == null || !userRelationship.check(user)) {
            mRelationship = null;
            return;
        } else {
            mRelationship = userRelationship;
        }
        invalidateOptionsMenu();
        mFollowButton.setEnabled(userRelationship.blocking || !userRelationship.blocked_by);
        if (userRelationship.blocked_by) {
            mPagesErrorContainer.setVisibility(View.GONE);
            mPagesErrorText.setText(null);
            mPagesContent.setVisibility(View.VISIBLE);
        } else if (!userRelationship.following && user.is_protected) {
            mPagesErrorContainer.setVisibility(View.VISIBLE);
            mPagesErrorText.setText(R.string.user_protected_summary);
            mPagesErrorIcon.setImageResource(R.drawable.ic_info_locked);
            mPagesContent.setVisibility(View.GONE);
        } else {
            mPagesErrorContainer.setVisibility(View.GONE);
            mPagesErrorText.setText(null);
            mPagesContent.setVisibility(View.VISIBLE);
        }
        if (userRelationship.blocking) {
            mFollowButton.setText(R.string.unblock);
        } else if (userRelationship.following) {
            mFollowButton.setText(R.string.unfollow);
        } else if (user.is_follow_request_sent) {
            mFollowButton.setText(R.string.requested);
        } else {
            mFollowButton.setText(R.string.follow);
        }
        mFollowButton.setCompoundDrawablePadding(Math.round(mFollowButton.getTextSize() * 0.25f));
        mFollowingYouIndicator.setVisibility(userRelationship.followed_by ? View.VISIBLE : View.GONE);

        final CacheUserInfoTask task = new CacheUserInfoTask(getContext().getApplicationContext());
        task.setParams(Pair.create(user, userRelationship));
        TaskStarter.execute(task);
        mFollowButton.setVisibility(View.VISIBLE);
    }

    @Override
    public boolean canScroll(float dy) {
        final Fragment fragment = getCurrentVisibleFragment();
        return fragment instanceof DrawerCallback && ((DrawerCallback) fragment).canScroll(dy);
    }

    @Override
    public void cancelTouch() {
        final Fragment fragment = getCurrentVisibleFragment();
        if (fragment instanceof DrawerCallback) {
            ((DrawerCallback) fragment).cancelTouch();
        }
    }

    @Override
    public void fling(float velocity) {
        final Fragment fragment = getCurrentVisibleFragment();
        if (fragment instanceof DrawerCallback) {
            ((DrawerCallback) fragment).fling(velocity);
        }
    }

    @Override
    public boolean isScrollContent(float x, float y) {
        final View v = mViewPager;
        final int[] location = new int[2];
        v.getLocationInWindow(location);
        return x >= location[0] && x <= location[0] + v.getWidth() && y >= location[1]
                && y <= location[1] + v.getHeight();
    }

    @Override
    public void onPageScrolled(int position, float positionOffset, int positionOffsetPixels) {

    }

    @Override
    public void onPageSelected(int position) {
        updateSubtitle();
    }

    private void updateSubtitle() {
        final AppCompatActivity activity = (AppCompatActivity) getActivity();
        if (activity == null)
            return;
        final ActionBar actionBar = activity.getSupportActionBar();
        if (actionBar == null)
            return;
        final ParcelableUser user = mUser;
        if (user == null) {
            actionBar.setSubtitle(null);
            return;
        }
        final SupportTabSpec spec = mPagerAdapter.getTab(mViewPager.getCurrentItem());
        assert spec.type != null;
        switch (spec.type) {
        case TAB_TYPE_STATUSES: {
            actionBar.setSubtitle(getResources().getQuantityString(R.plurals.N_statuses, (int) user.statuses_count,
                    user.statuses_count));
            break;
        }
        case TAB_TYPE_MEDIA: {
            if (user.media_count < 0) {
                actionBar.setSubtitle(R.string.recent_media);
            } else {
                actionBar.setSubtitle(getResources().getQuantityString(R.plurals.N_media, (int) user.media_count,
                        user.media_count));
            }
            break;
        }
        case TAB_TYPE_FAVORITES: {
            if (mPreferences.getBoolean(KEY_I_WANT_MY_STARS_BACK)) {
                actionBar.setSubtitle(getResources().getQuantityString(R.plurals.N_favorites,
                        (int) user.favorites_count, user.favorites_count));
            } else {
                actionBar.setSubtitle(getResources().getQuantityString(R.plurals.N_likes,
                        (int) user.favorites_count, user.favorites_count));

            }
            break;
        }
        default: {
            actionBar.setSubtitle(null);
            break;
        }
        }
        updateTitleAlpha();
    }

    @Override
    public void onPageScrollStateChanged(int state) {

    }

    @Override
    public void scrollBy(float dy) {
        final Fragment fragment = getCurrentVisibleFragment();
        if (fragment instanceof DrawerCallback) {
            ((DrawerCallback) fragment).scrollBy(dy);
        }
    }

    @Override
    public boolean shouldLayoutHeaderBottom() {
        final HeaderDrawerLayout drawer = mHeaderDrawerLayout;
        final View card = mProfileDetailsContainer;
        if (drawer == null || card == null)
            return false;
        return card.getTop() + drawer.getHeaderTop() - drawer.getPaddingTop() <= 0;
    }

    @Override
    public void topChanged(int top) {
        final HeaderDrawerLayout drawer = mHeaderDrawerLayout;
        if (drawer == null)
            return;
        final int offset = drawer.getPaddingTop() - top;
        updateScrollOffset(offset);

        final Fragment fragment = getCurrentVisibleFragment();
        if (fragment instanceof DrawerCallback) {
            ((DrawerCallback) fragment).topChanged(top);
        }
    }

    @UiThread
    public void displayUser(final ParcelableUser user, ParcelableAccount account) {
        final FragmentActivity activity = getActivity();
        if (activity == null)
            return;
        mUser = user;
        mAccount = account;
        if (user == null || user.key == null) {
            mProfileImageView.setVisibility(View.GONE);
            mProfileTypeView.setVisibility(View.GONE);
            if (activity instanceof ATEActivity) {
                setUiColor(Config.primaryColor(activity, ((ATEActivity) activity).getATEKey()));
            }
            return;
        }
        mProfileImageView.setVisibility(View.VISIBLE);
        final Resources resources = getResources();
        final LoaderManager lm = getLoaderManager();
        lm.destroyLoader(LOADER_ID_USER);
        lm.destroyLoader(LOADER_ID_FRIENDSHIP);
        mCardContent.setVisibility(View.VISIBLE);
        mHeaderErrorContainer.setVisibility(View.GONE);
        mProgressContainer.setVisibility(View.GONE);
        mUser = user;
        mProfileImageView.setBorderColor(user.color != 0 ? user.color : Color.WHITE);
        mProfileNameContainer.drawEnd(user.account_color);
        mNameView.setText(mBidiFormatter.unicodeWrap(TextUtils.isEmpty(user.nickname) ? user.name
                : getString(R.string.name_with_nickname, user.name, user.nickname)));
        final int typeIconRes = Utils.getUserTypeIconRes(user.is_verified, user.is_protected);
        if (typeIconRes != 0) {
            mProfileTypeView.setImageResource(typeIconRes);
            mProfileTypeView.setVisibility(View.VISIBLE);
        } else {
            mProfileTypeView.setImageDrawable(null);
            mProfileTypeView.setVisibility(View.GONE);
        }
        mScreenNameView.setText(String.format("@%s", user.screen_name));
        final TwidereLinkify linkify = new TwidereLinkify(this);
        if (user.description_unescaped != null) {
            final SpannableStringBuilder text = SpannableStringBuilder.valueOf(user.description_unescaped);
            ParcelableStatusUtils.applySpans(text, user.description_spans);
            linkify.applyAllLinks(text, user.account_key, false, false);
            mDescriptionView.setText(text);
        } else {
            mDescriptionView.setText(user.description_plain);
            Linkify.addLinks(mDescriptionView, Linkify.WEB_URLS);
        }
        mDescriptionContainer.setVisibility(mDescriptionView.length() > 0 ? View.VISIBLE : View.GONE);

        mLocationContainer.setVisibility(TextUtils.isEmpty(user.location) ? View.GONE : View.VISIBLE);
        mLocationView.setText(user.location);
        mURLContainer.setVisibility(
                TextUtils.isEmpty(user.url) && TextUtils.isEmpty(user.url_expanded) ? View.GONE : View.VISIBLE);
        mURLView.setText(TextUtils.isEmpty(user.url_expanded) ? user.url : user.url_expanded);
        final String createdAt = Utils.formatToLongTimeString(activity, user.created_at);
        final float daysSinceCreation = (System.currentTimeMillis() - user.created_at) / 1000 / 60 / 60 / 24;
        final int dailyTweets = Math.round(user.statuses_count / Math.max(1, daysSinceCreation));
        mCreatedAtView.setText(resources.getQuantityString(R.plurals.created_at_with_N_tweets_per_day, dailyTweets,
                createdAt, dailyTweets));
        mListedCount.setText(Utils.getLocalizedNumber(mLocale, user.listed_count));
        final long groupsCount = user.extras != null ? user.extras.groups_count : -1;
        mGroupsCount.setText(Utils.getLocalizedNumber(mLocale, groupsCount));
        mFollowersCount.setText(Utils.getLocalizedNumber(mLocale, user.followers_count));
        mFriendsCount.setText(Utils.getLocalizedNumber(mLocale, user.friends_count));

        mListedContainer.setVisibility(user.listed_count < 0 ? View.GONE : View.VISIBLE);
        mGroupsContainer.setVisibility(groupsCount < 0 ? View.GONE : View.VISIBLE);

        mMediaLoader.displayOriginalProfileImage(mProfileImageView, user);
        if (user.color != 0) {
            setUiColor(user.color);
        } else if (user.link_color != 0) {
            setUiColor(user.link_color);
        } else if (activity instanceof ATEActivity) {
            setUiColor(Config.primaryColor(activity, ((ATEActivity) activity).getATEKey()));
        }
        final int defWidth = resources.getDisplayMetrics().widthPixels;
        final int width = mBannerWidth > 0 ? mBannerWidth : defWidth;
        final String bannerUrl = ParcelableUserUtils.getProfileBannerUrl(user);
        if (ObjectUtils.notEqual(mProfileBannerView.getTag(), bannerUrl)
                || mProfileBannerView.getDrawable() == null) {
            mProfileBannerView.setTag(bannerUrl);
            mMediaLoader.displayProfileBanner(mProfileBannerView, bannerUrl, width);
        }
        final UserRelationship relationship = mRelationship;
        if (relationship == null) {
            getFriendship();
        }
        activity.setTitle(
                UserColorNameManager.decideDisplayName(user.nickname, user.name, user.screen_name, mNameFirst));

        Calendar cal = Calendar.getInstance();
        final int currentMonth = cal.get(Calendar.MONTH), currentDay = cal.get(Calendar.DAY_OF_MONTH);
        cal.setTimeInMillis(user.created_at);
        if (cal.get(Calendar.MONTH) == currentMonth && cal.get(Calendar.DAY_OF_MONTH) == currentDay
                && !mHideBirthdayView) {
            mProfileBirthdayBannerView.setVisibility(View.VISIBLE);
        } else {
            mProfileBirthdayBannerView.setVisibility(View.GONE);
        }
        updateTitleAlpha();
        invalidateOptionsMenu();
        updateSubtitle();
    }

    @Override
    public Fragment getCurrentVisibleFragment() {
        final int currentItem = mViewPager.getCurrentItem();
        if (currentItem < 0 || currentItem >= mPagerAdapter.getCount())
            return null;
        return (Fragment) mPagerAdapter.instantiateItem(mViewPager, currentItem);
    }

    @Override
    public boolean triggerRefresh(int position) {
        return false;
    }

    @Override
    public boolean getSystemWindowsInsets(Rect insets) {
        return false;
    }

    @Nullable
    public ParcelableUser getUser() {
        return mUser;
    }

    public void getUserInfo(final UserKey accountKey, final UserKey userKey, final String screenName,
            final boolean omitIntentExtra) {
        final LoaderManager lm = getLoaderManager();
        lm.destroyLoader(LOADER_ID_USER);
        lm.destroyLoader(LOADER_ID_FRIENDSHIP);
        final Bundle args = new Bundle();
        args.putParcelable(EXTRA_ACCOUNT_KEY, accountKey);
        args.putParcelable(EXTRA_USER_KEY, userKey);
        args.putString(EXTRA_SCREEN_NAME, screenName);
        args.putBoolean(EXTRA_OMIT_INTENT_EXTRA, omitIntentExtra);
        if (!mGetUserInfoLoaderInitialized) {
            lm.initLoader(LOADER_ID_USER, args, mUserInfoLoaderCallbacks);
            mGetUserInfoLoaderInitialized = true;
        } else {
            lm.restartLoader(LOADER_ID_USER, args, mUserInfoLoaderCallbacks);
        }
        if (accountKey == null || userKey == null && screenName == null) {
            mCardContent.setVisibility(View.GONE);
            mHeaderErrorContainer.setVisibility(View.GONE);
        }
    }

    @Subscribe
    public void notifyFriendshipUpdated(FriendshipUpdatedEvent event) {
        final ParcelableUser user = getUser();
        if (user == null || !event.isAccount(user.account_key) || !event.isUser(user.key.getId()))
            return;
        getFriendship();
    }

    @Subscribe
    public void notifyFriendshipUserUpdated(FriendshipTaskEvent event) {
        final ParcelableUser user = getUser();
        if (user == null || !event.isSucceeded() || !event.isUser(user))
            return;
        getFriendship();
    }

    @Subscribe
    public void notifyProfileUpdated(ProfileUpdatedEvent event) {
        final ParcelableUser user = getUser();
        // TODO check account status
        if (user == null || !user.equals(event.user))
            return;
        displayUser(event.user, mAccount);
    }

    @Subscribe
    public void notifyTaskStateChanged(TaskStateChangedEvent event) {
        invalidateOptionsMenu();
    }

    @Override
    public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
        final ParcelableUser user = getUser();
        switch (requestCode) {
        case REQUEST_SET_COLOR: {
            if (user == null)
                return;
            if (resultCode == Activity.RESULT_OK) {
                if (data == null)
                    return;
                final int color = data.getIntExtra(EXTRA_COLOR, Color.TRANSPARENT);
                mUserColorNameManager.setUserColor(mUser.key, color);
            } else if (resultCode == ColorPickerDialogActivity.RESULT_CLEARED) {
                mUserColorNameManager.clearUserColor(mUser.key);
            }
            break;
        }
        case REQUEST_ADD_TO_LIST: {
            if (user == null)
                return;
            if (resultCode == Activity.RESULT_OK && data != null) {
                final AsyncTwitterWrapper twitter = mTwitterWrapper;
                final ParcelableUserList list = data.getParcelableExtra(EXTRA_USER_LIST);
                if (list == null || twitter == null)
                    return;
                twitter.addUserListMembersAsync(user.account_key, list.id, user);
            }
            break;
        }
        case REQUEST_SELECT_ACCOUNT: {
            if (user == null)
                return;
            if (resultCode == Activity.RESULT_OK) {
                if (data == null || !data.hasExtra(EXTRA_ID))
                    return;
                final UserKey accountKey = data.getParcelableExtra(EXTRA_ACCOUNT_KEY);
                @Referral
                final String referral = getArguments().getString(EXTRA_REFERRAL);
                IntentUtils.openUserProfile(getActivity(), accountKey, user.key, user.screen_name, null,
                        mPreferences.getBoolean(KEY_NEW_DOCUMENT_API), referral);
            }
            break;
        }
        }

    }

    @Override
    public View onCreateView(final LayoutInflater inflater, final ViewGroup container,
            final Bundle savedInstanceState) {
        return inflater.inflate(R.layout.fragment_user, container, false);
    }

    @Override
    public void onActivityCreated(final Bundle savedInstanceState) {
        super.onActivityCreated(savedInstanceState);
        final FragmentActivity activity = getActivity();
        mUserColorNameManager.registerColorChangedListener(this);
        mUserColorNameManager.registerNicknameChangedListener(this);
        mNameFirst = mPreferences.getBoolean(KEY_NAME_FIRST);
        mLocale = getResources().getConfiguration().locale;
        mCardBackgroundColor = ThemeUtils.getCardBackgroundColor(activity,
                ThemeUtils.getThemeBackgroundOption(activity), ThemeUtils.getUserThemeBackgroundAlpha(activity));
        mActionBarShadowColor = 0xA0000000;
        final Bundle args = getArguments();
        UserKey accountId = null;
        UserKey userId = null;
        String screenName = null;
        if (savedInstanceState != null) {
            args.putAll(savedInstanceState);
        } else {
            accountId = args.getParcelable(EXTRA_ACCOUNT_KEY);
            userId = args.getParcelable(EXTRA_USER_KEY);
            screenName = args.getString(EXTRA_SCREEN_NAME);
        }

        Utils.setNdefPushMessageCallback(activity, new CreateNdefMessageCallback() {

            @Override
            public NdefMessage createNdefMessage(NfcEvent event) {
                final ParcelableUser user = getUser();
                if (user == null)
                    return null;
                return new NdefMessage(
                        new NdefRecord[] { NdefRecord.createUri(LinkCreator.getUserWebLink(user)), });
            }
        });

        mTintedStatusFrameLayout.setWindowInsetsListener(new TintedStatusFrameLayout.WindowInsetsListener() {
            @Override
            public void onApplyWindowInsets(int left, int top, int right, int bottom) {
                mProfileContentContainer.setPadding(0, top, 0, 0);
                mProfileBannerSpace.setStatusBarHeight(top);

                if (mProfileBannerSpace.getToolbarHeight() == 0) {
                    int toolbarHeight = mToolbar.getMeasuredHeight();
                    if (toolbarHeight == 0) {
                        toolbarHeight = ThemeUtils.getActionBarHeight(getContext());
                    }
                    mProfileBannerSpace.setToolbarHeight(toolbarHeight);
                }
            }

        });
        mProfileContentContainer.setOnSizeChangedListener(new OnSizeChangedListener() {
            @Override
            public void onSizeChanged(View view, int w, int h, int oldw, int oldh) {
                final int toolbarHeight = mToolbar.getMeasuredHeight();
                mHeaderDrawerLayout.setPadding(0, toolbarHeight, 0, 0);
                mProfileBannerSpace.setToolbarHeight(toolbarHeight);
            }
        });

        mHeaderDrawerLayout.setDrawerCallback(this);

        mPagerAdapter = new SupportTabsAdapter(activity, getChildFragmentManager());

        mViewPager.setOffscreenPageLimit(3);
        mViewPager.setAdapter(mPagerAdapter);
        mPagerIndicator.setViewPager(mViewPager);
        mPagerIndicator.setTabDisplayOption(TabPagerIndicator.LABEL);
        mPagerIndicator.setOnPageChangeListener(this);

        mFollowButton.setOnClickListener(this);
        mProfileImageView.setOnClickListener(this);
        mProfileBannerView.setOnClickListener(this);
        mListedContainer.setOnClickListener(this);
        mGroupsContainer.setOnClickListener(this);
        mFollowersContainer.setOnClickListener(this);
        mFriendsContainer.setOnClickListener(this);
        mHeaderErrorIcon.setOnClickListener(this);
        mProfileBirthdayBannerView.setOnClickListener(this);
        mProfileBannerView.setOnSizeChangedListener(this);
        mProfileBannerSpace.setOnTouchListener(this);

        mProfileNameBackground.setBackgroundColor(mCardBackgroundColor);
        mProfileDetailsContainer.setBackgroundColor(mCardBackgroundColor);
        mPagerIndicator.setBackgroundColor(mCardBackgroundColor);

        final float actionBarElevation = ThemeUtils.getSupportActionBarElevation(activity);
        ViewCompat.setElevation(mPagerIndicator, actionBarElevation);

        setupBaseActionBar();
        setupUserPages();

        getUserInfo(accountId, userId, screenName, false);
    }

    @Override
    public void onStart() {
        super.onStart();
        mBus.register(this);

        @Referral
        final String referral = getArguments().getString(EXTRA_REFERRAL);
        final Context context = getContext();
        if (mUserEvent == null) {
            mUserEvent = UserEvent.create(context, referral);
        } else {
            mUserEvent.markStart(context);
        }
    }

    @Override
    public void onStop() {
        final Context context = getContext();
        if (mUserEvent != null && context != null && mUser != null) {
            mUserEvent.setUser(mUser);
            mUserEvent.markEnd();
            HotMobiLogger.getInstance(context).log(mUserEvent);
        }
        mBus.unregister(this);
        super.onStop();
    }

    @Override
    public void onResume() {
        super.onResume();
        setUiColor(mUiColor);
    }

    @Override
    public void onSaveInstanceState(final Bundle outState) {
        outState.putParcelable(EXTRA_USER, getUser());
        super.onSaveInstanceState(outState);
    }

    @Override
    public void onDestroyView() {
        mUser = null;
        mRelationship = null;
        final LoaderManager lm = getLoaderManager();
        lm.destroyLoader(LOADER_ID_USER);
        lm.destroyLoader(LOADER_ID_FRIENDSHIP);
        super.onDestroyView();
    }

    @Override
    public void onCreateOptionsMenu(final Menu menu, final MenuInflater inflater) {
        inflater.inflate(R.menu.menu_user_profile, menu);
    }

    @Override
    @UiThread
    public void onPrepareOptionsMenu(final Menu menu) {
        final AsyncTwitterWrapper twitter = mTwitterWrapper;
        final ParcelableUser user = getUser();
        if (twitter == null || user == null)
            return;

        final boolean isMyself = user.account_key.maybeEquals(user.key);
        final MenuItem mentionItem = menu.findItem(R.id.mention);
        if (mentionItem != null) {
            final String displayName = UserColorNameManager.decideDisplayName(user.nickname, user.name,
                    user.screen_name, mNameFirst);
            mentionItem.setTitle(getString(R.string.mention_user_name, displayName));
        }
        MenuUtils.setMenuItemAvailability(menu, R.id.mention, !isMyself);
        MenuUtils.setMenuItemAvailability(menu, R.id.incoming_friendships, isMyself);
        MenuUtils.setMenuItemAvailability(menu, R.id.saved_searches, isMyself);
        MenuUtils.setMenuItemAvailability(menu, R.id.scheduled_statuses, isMyself && MicroBlogAPIFactory
                .getOfficialKeyType(getActivity(), user.account_key) == ConsumerKeyType.TWEETDECK);
        MenuUtils.setMenuItemAvailability(menu, R.id.muted_users, isMyself);
        MenuUtils.setMenuItemAvailability(menu, R.id.blocked_users, isMyself);

        MenuUtils.setMenuItemAvailability(menu, R.id.block, !isMyself);
        MenuUtils.setMenuItemAvailability(menu, R.id.mute_user, !isMyself);
        MenuUtils.setMenuItemAvailability(menu, R.id.report_spam, !isMyself);
        MenuUtils.setMenuItemAvailability(menu, R.id.enable_retweets, !isMyself);
        if (mAccount != null) {
            MenuUtils.setMenuItemAvailability(menu, R.id.add_to_list, TextUtils
                    .equals(ParcelableAccount.Type.TWITTER, ParcelableAccountUtils.getAccountType(mAccount)));
        } else {
            MenuUtils.setMenuItemAvailability(menu, R.id.add_to_list, false);
        }

        final UserRelationship userRelationship = mRelationship;
        if (userRelationship != null) {

            final MenuItem filterItem = menu.findItem(R.id.add_to_filter);
            if (filterItem != null) {
                filterItem.setChecked(userRelationship.filtering);
            }
            if (isMyself) {
                MenuUtils.setMenuItemAvailability(menu, R.id.send_direct_message, false);
            } else {
                MenuUtils.setMenuItemAvailability(menu, R.id.send_direct_message, userRelationship.can_dm);
                MenuUtils.setMenuItemAvailability(menu, R.id.block, true);
                final MenuItem blockItem = menu.findItem(R.id.block);
                if (blockItem != null) {
                    ActionIconDrawable.setMenuHighlight(blockItem, new TwidereMenuInfo(userRelationship.blocking));
                    blockItem.setTitle(userRelationship.blocking ? R.string.unblock : R.string.block);
                }
                final MenuItem muteItem = menu.findItem(R.id.mute_user);
                if (muteItem != null) {
                    muteItem.setChecked(userRelationship.muting);
                }
                final MenuItem wantRetweetsItem = menu.findItem(R.id.enable_retweets);
                if (wantRetweetsItem != null) {
                    wantRetweetsItem.setChecked(userRelationship.retweet_enabled);
                }
            }
        } else {
            MenuUtils.setMenuItemAvailability(menu, R.id.send_direct_message, false);
        }
        final Intent intent = new Intent(INTENT_ACTION_EXTENSION_OPEN_USER);
        final Bundle extras = new Bundle();
        extras.putParcelable(EXTRA_USER, user);
        intent.putExtras(extras);
        menu.removeGroup(MENU_GROUP_USER_EXTENSION);
        MenuUtils.addIntentToMenu(getActivity(), menu, intent, MENU_GROUP_USER_EXTENSION);
        final HeaderDrawerLayout drawer = mHeaderDrawerLayout;
        if (drawer != null) {
            final int offset = drawer.getPaddingTop() - drawer.getHeaderTop();
            mPreviousActionBarItemIsDark = 0;
            mPreviousTabItemIsDark = 0;
            updateScrollOffset(offset);
        }
    }

    @Override
    public boolean onOptionsItemSelected(final MenuItem item) {
        final Context context = getContext();
        final AsyncTwitterWrapper twitter = mTwitterWrapper;
        final ParcelableUser user = getUser();
        final UserRelationship userRelationship = mRelationship;
        if (user == null || twitter == null)
            return false;
        switch (item.getItemId()) {
        case R.id.block: {
            if (userRelationship == null)
                return true;
            if (userRelationship.blocking) {
                twitter.destroyBlockAsync(user.account_key, user.key);
            } else {
                CreateUserBlockDialogFragment.show(getFragmentManager(), user);
            }
            break;
        }
        case R.id.report_spam: {
            ReportSpamDialogFragment.show(getFragmentManager(), user);
            break;
        }
        case R.id.add_to_filter: {
            if (userRelationship == null)
                return true;
            final ContentResolver cr = getContentResolver();
            if (userRelationship.filtering) {
                final String where = Expression.equalsArgs(Filters.Users.USER_KEY).getSQL();
                final String[] whereArgs = { user.key.toString() };
                cr.delete(Filters.Users.CONTENT_URI, where, whereArgs);
                Utils.showInfoMessage(getActivity(), R.string.message_user_unmuted, false);
            } else {
                cr.insert(Filters.Users.CONTENT_URI, ContentValuesCreator.createFilteredUser(user));
                Utils.showInfoMessage(getActivity(), R.string.message_user_muted, false);
            }
            getFriendship();
            break;
        }
        case R.id.mute_user: {
            if (userRelationship == null)
                return true;
            if (userRelationship.muting) {
                twitter.destroyMuteAsync(user.account_key, user.key);
            } else {
                CreateUserMuteDialogFragment.show(getFragmentManager(), user);
            }
            break;
        }
        case R.id.mention: {
            final Intent intent = new Intent(INTENT_ACTION_MENTION);
            final Bundle bundle = new Bundle();
            bundle.putParcelable(EXTRA_USER, user);
            intent.putExtras(bundle);
            startActivity(intent);
            break;
        }
        case R.id.send_direct_message: {
            final Uri.Builder builder = new Uri.Builder();
            builder.scheme(SCHEME_TWIDERE);
            builder.authority(AUTHORITY_DIRECT_MESSAGES_CONVERSATION);
            builder.appendQueryParameter(QUERY_PARAM_ACCOUNT_KEY, user.account_key.toString());
            builder.appendQueryParameter(QUERY_PARAM_USER_KEY, user.key.toString());
            final Intent intent = new Intent(Intent.ACTION_VIEW, builder.build());
            intent.putExtra(EXTRA_ACCOUNT,
                    ParcelableCredentialsUtils.getCredentials(getActivity(), user.account_key));
            intent.putExtra(EXTRA_USER, user);
            startActivity(intent);
            break;
        }
        case R.id.set_color: {
            final Intent intent = new Intent(getActivity(), ColorPickerDialogActivity.class);
            intent.putExtra(EXTRA_COLOR, mUserColorNameManager.getUserColor(user.key));
            intent.putExtra(EXTRA_ALPHA_SLIDER, false);
            intent.putExtra(EXTRA_CLEAR_BUTTON, true);
            startActivityForResult(intent, REQUEST_SET_COLOR);
            break;
        }
        case R.id.clear_nickname: {
            mUserColorNameManager.clearUserNickname(user.key);
            break;
        }
        case R.id.set_nickname: {
            final String nick = mUserColorNameManager.getUserNickname(user.key);
            SetUserNicknameDialogFragment.show(getFragmentManager(), user.key, nick);
            break;
        }
        case R.id.add_to_list: {
            final Intent intent = new Intent(INTENT_ACTION_SELECT_USER_LIST);
            intent.setClass(getActivity(), UserListSelectorActivity.class);
            intent.putExtra(EXTRA_ACCOUNT_KEY, user.account_key);
            intent.putExtra(EXTRA_SCREEN_NAME,
                    DataStoreUtils.getAccountScreenName(getActivity(), user.account_key));
            startActivityForResult(intent, REQUEST_ADD_TO_LIST);
            break;
        }
        case R.id.open_with_account: {
            final Intent intent = new Intent(INTENT_ACTION_SELECT_ACCOUNT);
            intent.setClass(getActivity(), AccountSelectorActivity.class);
            intent.putExtra(EXTRA_SINGLE_SELECTION, true);
            intent.putExtra(EXTRA_ACCOUNT_HOST, user.key.getHost());
            startActivityForResult(intent, REQUEST_SELECT_ACCOUNT);
            break;
        }
        case R.id.follow: {
            if (userRelationship == null)
                return true;
            final boolean updatingRelationship = twitter.isUpdatingRelationship(user.account_key, user.key);
            if (!updatingRelationship) {
                if (userRelationship.following) {
                    DestroyFriendshipDialogFragment.show(getFragmentManager(), user);
                } else {
                    twitter.createFriendshipAsync(user.account_key, user.key);
                }
            }
            return true;
        }
        case R.id.enable_retweets: {
            final boolean newState = !item.isChecked();
            final FriendshipUpdate update = new FriendshipUpdate();
            update.retweets(newState);
            twitter.updateFriendship(user.account_key, user.key.getId(), update);
            item.setChecked(newState);
            return true;
        }
        case R.id.muted_users: {
            IntentUtils.openMutesUsers(getActivity(), user.account_key);
            return true;
        }
        case R.id.blocked_users: {
            IntentUtils.openUserBlocks(getActivity(), user.account_key);
            return true;
        }
        case R.id.incoming_friendships: {
            IntentUtils.openIncomingFriendships(getActivity(), user.account_key);
            return true;
        }
        case R.id.user_mentions: {
            IntentUtils.openUserMentions(context, user.account_key, user.screen_name);
            return true;
        }
        case R.id.saved_searches: {
            IntentUtils.openSavedSearches(context, user.account_key);
            return true;
        }
        case R.id.scheduled_statuses: {
            IntentUtils.openScheduledStatuses(context, user.account_key);
            return true;
        }
        case R.id.open_in_browser: {
            final Uri uri = LinkCreator.getUserWebLink(user);
            final Intent intent = new Intent(Intent.ACTION_VIEW, uri);
            intent.addCategory(Intent.CATEGORY_BROWSABLE);
            intent.setPackage(IntentUtils.getDefaultBrowserPackage(context, uri, true));
            if (intent.resolveActivity(context.getPackageManager()) != null) {
                startActivity(intent);
            }
            return true;
        }
        default: {
            final Intent intent = item.getIntent();
            if (intent != null && intent.resolveActivity(context.getPackageManager()) != null) {
                startActivity(intent);
            }
            break;
        }
        }
        return true;
    }

    @Override
    public void onViewCreated(final View view, final Bundle savedInstanceState) {
        super.onViewCreated(view, savedInstanceState);
        mTintedStatusFrameLayout = (TintedStatusFrameLayout) view;
        mToolbar = (Toolbar) view.findViewById(R.id.toolbar);
        mHeaderDrawerLayout = (HeaderDrawerLayout) view.findViewById(R.id.user_profile_drawer);
        final View headerView = mHeaderDrawerLayout.getHeader();
        final View contentView = mHeaderDrawerLayout.getContent();
        mCardContent = headerView.findViewById(R.id.card_content);
        mHeaderErrorContainer = headerView.findViewById(R.id.error_container);
        mHeaderErrorTextView = (TextView) headerView.findViewById(R.id.error_text);
        mHeaderErrorIcon = (ImageView) headerView.findViewById(R.id.error_icon);
        mProgressContainer = headerView.findViewById(R.id.progress_container);
        mProfileBannerView = (ProfileBannerImageView) view.findViewById(R.id.profile_banner);
        mProfileBirthdayBannerView = view.findViewById(R.id.profile_birthday_banner);
        mProfileBannerContainer = view.findViewById(R.id.profile_banner_container);
        mProfileContentContainer = (ExtendedRelativeLayout) view.findViewById(R.id.profile_content_container);
        mNameView = (TextView) headerView.findViewById(R.id.name);
        mScreenNameView = (TextView) headerView.findViewById(R.id.screen_name);
        mDescriptionView = (TextView) headerView.findViewById(R.id.description);
        mLocationView = (TextView) headerView.findViewById(R.id.location);
        mURLView = (TextView) headerView.findViewById(R.id.url);
        mCreatedAtView = (TextView) headerView.findViewById(R.id.created_at);
        mListedContainer = headerView.findViewById(R.id.listed_container);
        mGroupsContainer = headerView.findViewById(R.id.groups_container);
        mListedCount = (TextView) headerView.findViewById(R.id.listed_count);
        mGroupsCount = (TextView) headerView.findViewById(R.id.groups_count);
        mFollowersContainer = headerView.findViewById(R.id.followers_container);
        mFollowersCount = (TextView) headerView.findViewById(R.id.followers_count);
        mFriendsContainer = headerView.findViewById(R.id.friends_container);
        mFriendsCount = (TextView) headerView.findViewById(R.id.friends_count);
        mProfileNameContainer = (ColorLabelRelativeLayout) headerView.findViewById(R.id.profile_name_container);
        mProfileImageView = (ShapedImageView) headerView.findViewById(R.id.profile_image);
        mProfileTypeView = (ImageView) headerView.findViewById(R.id.profile_type);
        mDescriptionContainer = headerView.findViewById(R.id.description_container);
        mLocationContainer = headerView.findViewById(R.id.location_container);
        mURLContainer = headerView.findViewById(R.id.url_container);
        mProfileBannerSpace = (ProfileBannerSpace) headerView.findViewById(R.id.profile_banner_space);
        mViewPager = (ViewPager) contentView.findViewById(R.id.view_pager);
        mPagerIndicator = (TabPagerIndicator) contentView.findViewById(R.id.toolbar_tabs);
        mFollowButton = (Button) headerView.findViewById(R.id.follow);
        mFollowProgress = (ProgressBar) headerView.findViewById(R.id.follow_progress);
        mWindowOverlay = view.findViewById(R.id.window_overlay);
        mPagesContent = view.findViewById(R.id.pages_content);
        mPagesErrorContainer = view.findViewById(R.id.pages_error_container);
        mPagesErrorIcon = (ImageView) view.findViewById(R.id.pages_error_icon);
        mPagesErrorText = (TextView) view.findViewById(R.id.pages_error_text);
        mProfileNameBackground = view.findViewById(R.id.profile_name_background);
        mProfileDetailsContainer = view.findViewById(R.id.profile_details_container);
        mFollowingYouIndicator = view.findViewById(R.id.following_you_indicator);

        final Object host = getHost();
        if (host instanceof AppCompatActivity) {
            ((AppCompatActivity) host).setSupportActionBar(mToolbar);
        }

    }

    @Override
    public boolean handleKeyboardShortcutSingle(@NonNull KeyboardShortcutsHandler handler, int keyCode,
            @NonNull KeyEvent event, int metaState) {
        if (handleFragmentKeyboardShortcutSingle(handler, keyCode, event, metaState))
            return true;
        final String action = handler.getKeyAction(CONTEXT_TAG_NAVIGATION, keyCode, event, metaState);
        if (action != null) {
            switch (action) {
            case ACTION_NAVIGATION_PREVIOUS_TAB: {
                final int previous = mViewPager.getCurrentItem() - 1;
                if (previous >= 0 && previous < mPagerAdapter.getCount()) {
                    mViewPager.setCurrentItem(previous, true);
                }
                return true;
            }
            case ACTION_NAVIGATION_NEXT_TAB: {
                final int next = mViewPager.getCurrentItem() + 1;
                if (next >= 0 && next < mPagerAdapter.getCount()) {
                    mViewPager.setCurrentItem(next, true);
                }
                return true;
            }
            }
        }
        return handler.handleKey(getActivity(), null, keyCode, event, metaState);
    }

    @Override
    public boolean isKeyboardShortcutHandled(@NonNull KeyboardShortcutsHandler handler, int keyCode,
            @NonNull KeyEvent event, int metaState) {
        if (isFragmentKeyboardShortcutHandled(handler, keyCode, event, metaState))
            return true;
        final String action = handler.getKeyAction(CONTEXT_TAG_NAVIGATION, keyCode, event, metaState);
        if (action != null) {
            switch (action) {
            case ACTION_NAVIGATION_PREVIOUS_TAB:
            case ACTION_NAVIGATION_NEXT_TAB:
                return true;
            }
        }
        return false;
    }

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

    private boolean handleFragmentKeyboardShortcutRepeat(@NonNull KeyboardShortcutsHandler handler, int keyCode,
            int repeatCount, @NonNull KeyEvent event, int metaState) {
        final Fragment fragment = getKeyboardShortcutRecipient();
        if (fragment instanceof KeyboardShortcutCallback) {
            return ((KeyboardShortcutCallback) fragment).handleKeyboardShortcutRepeat(handler, keyCode, repeatCount,
                    event, metaState);
        }
        return false;
    }

    private boolean handleFragmentKeyboardShortcutSingle(@NonNull KeyboardShortcutsHandler handler, int keyCode,
            @NonNull KeyEvent event, int metaState) {
        final Fragment fragment = getKeyboardShortcutRecipient();
        if (fragment instanceof KeyboardShortcutCallback) {
            return ((KeyboardShortcutCallback) fragment).handleKeyboardShortcutSingle(handler, keyCode, event,
                    metaState);
        }
        return false;
    }

    private boolean isFragmentKeyboardShortcutHandled(@NonNull KeyboardShortcutsHandler handler, int keyCode,
            @NonNull KeyEvent event, int metaState) {
        final Fragment fragment = getKeyboardShortcutRecipient();
        if (fragment instanceof KeyboardShortcutCallback) {
            return ((KeyboardShortcutCallback) fragment).isKeyboardShortcutHandled(handler, keyCode, event,
                    metaState);
        }
        return false;
    }

    private Fragment getKeyboardShortcutRecipient() {
        return getCurrentVisibleFragment();
    }

    @Override
    protected void fitSystemWindows(Rect insets) {
    }

    @Override
    public boolean setupWindow(FragmentActivity activity) {
        if (activity instanceof AppCompatActivity) {
            ((AppCompatActivity) activity).supportRequestWindowFeature(Window.FEATURE_NO_TITLE);
            ((AppCompatActivity) activity).supportRequestWindowFeature(WindowCompat.FEATURE_ACTION_MODE_OVERLAY);
        }
        WindowSupport.setStatusBarColor(activity.getWindow(), Color.TRANSPARENT);
        return true;
    }

    @Override
    public void onClick(final View view) {
        final FragmentActivity activity = getActivity();
        final ParcelableUser user = getUser();
        if (activity == null || user == null)
            return;
        switch (view.getId()) {
        case R.id.error_container: {
            getUserInfo(true);
            break;
        }
        case R.id.follow: {
            if (user.account_key.maybeEquals(user.key)) {
                IntentUtils.openProfileEditor(getActivity(), user.account_key);
                break;
            }
            final UserRelationship userRelationship = mRelationship;
            final AsyncTwitterWrapper twitter = mTwitterWrapper;
            if (userRelationship == null || twitter == null)
                return;
            if (userRelationship.blocking) {
                twitter.destroyBlockAsync(user.account_key, user.key);
            } else if (userRelationship.following) {
                DestroyFriendshipDialogFragment.show(getFragmentManager(), user);
            } else {
                twitter.createFriendshipAsync(user.account_key, user.key);
            }
            break;
        }
        case R.id.profile_image: {
            final String url = Utils.getOriginalTwitterProfileImage(user.profile_image_url);
            ParcelableMedia profileImage = ParcelableMediaUtils.image(url);
            profileImage.type = ParcelableMedia.Type.IMAGE;
            final ParcelableMedia[] media = { profileImage };
            IntentUtils.openMedia(activity, user.account_key, false, null, media, null,
                    mPreferences.getBoolean(KEY_NEW_DOCUMENT_API));
            break;
        }
        case R.id.profile_banner: {
            final String bannerUrl = ParcelableUserUtils.getProfileBannerUrl(user);
            if (bannerUrl == null)
                return;
            final String url = InternalTwitterContentUtils.getBestBannerUrl(bannerUrl, Integer.MAX_VALUE);
            ParcelableMedia profileBanner = ParcelableMediaUtils.image(url);
            profileBanner.type = ParcelableMedia.Type.IMAGE;
            final ParcelableMedia[] media = { profileBanner };
            IntentUtils.openMedia(activity, user.account_key, false, null, media, null,
                    mPreferences.getBoolean(KEY_NEW_DOCUMENT_API));
            break;
        }
        case R.id.listed_container: {
            IntentUtils.openUserLists(getActivity(), user.account_key, user.key, user.screen_name);
            break;
        }
        case R.id.groups_container: {
            IntentUtils.openUserGroups(getActivity(), user.account_key, user.key, user.screen_name);
            break;
        }
        case R.id.followers_container: {
            IntentUtils.openUserFollowers(getActivity(), user.account_key, user.key, user.screen_name);
            break;
        }
        case R.id.friends_container: {
            IntentUtils.openUserFriends(getActivity(), user.account_key, user.key, user.screen_name);
            break;
        }
        case R.id.name_container: {
            if (user.account_key.equals(user.key))
                return;
            IntentUtils.openProfileEditor(getActivity(), user.account_key);
            break;
        }
        case R.id.profile_birthday_banner: {
            mHideBirthdayView = true;
            mProfileBirthdayBannerView
                    .startAnimation(AnimationUtils.loadAnimation(getActivity(), android.R.anim.fade_out));
            mProfileBirthdayBannerView.setVisibility(View.GONE);
            break;
        }
        }

    }

    @Override
    public boolean onLinkClick(final String link, final String orig, final UserKey accountKey, final long extraId,
            final int type, final boolean sensitive, int start, int end) {
        final ParcelableUser user = getUser();
        if (user == null)
            return false;
        switch (type) {
        case TwidereLinkify.LINK_TYPE_MENTION: {
            IntentUtils.openUserProfile(getActivity(), user.account_key, null, link, null,
                    mPreferences.getBoolean(KEY_NEW_DOCUMENT_API), Referral.USER_MENTION);
            return true;
        }
        case TwidereLinkify.LINK_TYPE_HASHTAG: {
            IntentUtils.openTweetSearch(getActivity(), user.account_key, "#" + link);
            return true;
        }
        case TwidereLinkify.LINK_TYPE_LINK_IN_TEXT:
        case TwidereLinkify.LINK_TYPE_ENTITY_URL: {
            final Uri uri = Uri.parse(link);
            final Intent intent;
            if (uri.getScheme() != null) {
                intent = new Intent(Intent.ACTION_VIEW, uri);
            } else {
                intent = new Intent(Intent.ACTION_VIEW, uri.buildUpon().scheme("http").build());
            }
            startActivity(intent);
            return true;
        }
        case TwidereLinkify.LINK_TYPE_LIST: {
            if (link == null)
                break;
            final String[] mentionList = link.split("/");
            if (mentionList.length != 2) {
                break;
            }
            return true;
        }
        }
        return false;
    }

    @Override
    public void onUserNicknameChanged(@NonNull UserKey userId, String nick) {
        if (mUser == null || !mUser.key.equals(userId))
            return;
        displayUser(mUser, mAccount);
    }

    @Override
    public void onUserColorChanged(@NonNull UserKey userId, int color) {
        if (mUser == null || !mUser.key.equals(userId))
            return;
        displayUser(mUser, mAccount);
    }

    @Override
    public void onSizeChanged(final View view, final int w, final int h, final int oldw, final int oldh) {
        mBannerWidth = w;
        if (w != oldw || h != oldh) {
            requestFitSystemWindows();
        }
    }

    @Override
    public boolean onTouch(final View v, final MotionEvent event) {
        if (mProfileBirthdayBannerView.getVisibility() == View.VISIBLE) {
            return mProfileBirthdayBannerView.dispatchTouchEvent(event);
        }
        return mProfileBannerView.dispatchTouchEvent(event);
    }

    @Override
    public boolean scrollToStart() {
        final Fragment fragment = getCurrentVisibleFragment();
        if (!(fragment instanceof RefreshScrollTopInterface))
            return false;
        ((RefreshScrollTopInterface) fragment).scrollToStart();
        return true;
    }

    @Override
    public boolean triggerRefresh() {
        final Fragment fragment = getCurrentVisibleFragment();
        if (!(fragment instanceof RefreshScrollTopInterface))
            return false;
        ((RefreshScrollTopInterface) fragment).triggerRefresh();
        return true;
    }

    private void getFriendship() {
        final ParcelableUser user = getUser();
        if (user == null)
            return;
        mRelationship = null;
        final LoaderManager lm = getLoaderManager();
        lm.destroyLoader(LOADER_ID_FRIENDSHIP);
        final Bundle args = new Bundle();
        args.putParcelable(EXTRA_ACCOUNT_KEY, user.account_key);
        args.putParcelable(EXTRA_USER, user);
        if (!mGetFriendShipLoaderInitialized) {
            lm.initLoader(LOADER_ID_FRIENDSHIP, args, mFriendshipLoaderCallbacks);
            mGetFriendShipLoaderInitialized = true;
        } else {
            lm.restartLoader(LOADER_ID_FRIENDSHIP, args, mFriendshipLoaderCallbacks);
        }
    }

    private void getUserInfo(final boolean omitIntentExtra) {
        final ParcelableUser user = mUser;
        if (user == null)
            return;
        getUserInfo(user.account_key, user.key, user.screen_name, omitIntentExtra);
    }

    private void setUiColor(int color) {
        mUiColor = color;
        mPreviousActionBarItemIsDark = 0;
        mPreviousTabItemIsDark = 0;
        if (mActionBarBackground == null) {
            setupBaseActionBar();
        }
        final BaseActivity activity = (BaseActivity) getActivity();
        if (Config.coloredActionBar(activity, activity.getATEKey())) {
            mPrimaryColor = color;
            mPrimaryColorDark = ThemeUtils.computeDarkColor(color);
        } else {
            mPrimaryColor = Config.primaryColor(activity, activity.getATEKey());
            mPrimaryColorDark = Color.BLACK;
        }
        if (mActionBarBackground != null) {
            mActionBarBackground.setColor(mPrimaryColor);
        }
        int taskColor;
        if (Config.coloredActionBar(activity, activity.getATEKey())) {
            taskColor = color;
        } else {
            taskColor = Config.toolbarColor(activity, activity.getATEKey(), mToolbar);
        }
        if (mUser != null) {
            final String name = mUserColorNameManager.getDisplayName(mUser, mNameFirst);
            ActivitySupport.setTaskDescription(activity, new TaskDescriptionCompat(name, null, taskColor));
        } else {
            ActivitySupport.setTaskDescription(activity, new TaskDescriptionCompat(null, null, taskColor));
        }
        final int optimalAccentColor = ThemeUtils.getOptimalAccentColor(color,
                mDescriptionView.getCurrentTextColor());
        mDescriptionView.setLinkTextColor(optimalAccentColor);
        mLocationView.setLinkTextColor(optimalAccentColor);
        mURLView.setLinkTextColor(optimalAccentColor);
        mProfileBannerView.setBackgroundColor(color);

        mPagerIndicator.setBackgroundColor(mPrimaryColor);

        final HeaderDrawerLayout drawer = mHeaderDrawerLayout;
        if (drawer != null) {
            final int offset = drawer.getPaddingTop() - drawer.getHeaderTop();
            updateScrollOffset(offset);
        }
    }

    private void setupBaseActionBar() {
        final FragmentActivity activity = getActivity();
        if (!(activity instanceof LinkHandlerActivity))
            return;
        final LinkHandlerActivity linkHandler = (LinkHandlerActivity) activity;
        final ActionBar actionBar = linkHandler.getSupportActionBar();
        if (actionBar == null)
            return;
        final Drawable shadow = ResourcesCompat.getDrawable(activity.getResources(),
                R.drawable.shadow_user_banner_action_bar, null);
        mActionBarBackground = new ActionBarDrawable(shadow);
        if (!ThemeUtils.isWindowFloating(linkHandler)
                && ThemeUtils.isTransparentBackground(linkHandler.getCurrentThemeBackgroundOption())) {
            //            mActionBarBackground.setAlpha(ThemeUtils.getActionBarAlpha(linkHandler.getCurrentThemeBackgroundAlpha()));
            mProfileBannerView.setAlpha(linkHandler.getCurrentThemeBackgroundAlpha() / 255f);
        }
        actionBar.setBackgroundDrawable(mActionBarBackground);
    }

    private void setupUserPages() {
        final Bundle args = getArguments(), tabArgs = new Bundle();
        final ParcelableUser user = args.getParcelable(EXTRA_USER);
        if (user != null) {
            tabArgs.putParcelable(EXTRA_ACCOUNT_KEY, user.account_key);
            tabArgs.putParcelable(EXTRA_USER_KEY, user.key);
            tabArgs.putString(EXTRA_SCREEN_NAME, user.screen_name);
        } else {
            tabArgs.putParcelable(EXTRA_ACCOUNT_KEY, args.getParcelable(EXTRA_ACCOUNT_KEY));
            tabArgs.putParcelable(EXTRA_USER_KEY, args.getParcelable(EXTRA_USER_KEY));
            tabArgs.putString(EXTRA_SCREEN_NAME, args.getString(EXTRA_SCREEN_NAME));
        }
        mPagerAdapter.addTab(UserTimelineFragment.class, tabArgs, getString(R.string.statuses),
                R.drawable.ic_action_quote, TAB_TYPE_STATUSES, TAB_POSITION_STATUSES, null);
        mPagerAdapter.addTab(UserMediaTimelineFragment.class, tabArgs, getString(R.string.media),
                R.drawable.ic_action_gallery, TAB_TYPE_MEDIA, TAB_POSITION_MEDIA, null);
        if (mPreferences.getBoolean(KEY_I_WANT_MY_STARS_BACK)) {
            mPagerAdapter.addTab(UserFavoritesFragment.class, tabArgs, getString(R.string.favorites),
                    R.drawable.ic_action_star, TAB_TYPE_FAVORITES, TAB_POSITION_FAVORITES, null);
        } else {
            mPagerAdapter.addTab(UserFavoritesFragment.class, tabArgs, getString(R.string.likes),
                    R.drawable.ic_action_heart, TAB_TYPE_FAVORITES, TAB_POSITION_FAVORITES, null);
        }
    }

    private void updateScrollOffset(int offset) {
        final View space = mProfileBannerSpace;
        final ProfileBannerImageView profileBannerView = mProfileBannerView;
        final View profileBirthdayBannerView = mProfileBirthdayBannerView;
        final View profileBannerContainer = mProfileBannerContainer;
        final int spaceHeight = space.getHeight();
        final float factor = TwidereMathUtils.clamp(spaceHeight == 0 ? 0 : (offset / (float) spaceHeight), 0, 1);
        profileBannerContainer.setTranslationY(-offset);
        profileBannerView.setTranslationY(offset / 2);
        profileBirthdayBannerView.setTranslationY(offset / 2);

        final BaseActivity activity = (BaseActivity) getActivity();

        final int statusBarColor = (int) sArgbEvaluator.evaluate(factor, 0xA0000000,
                ThemeUtils.computeDarkColor(mPrimaryColorDark));
        final Window window = activity.getWindow();
        mTintedStatusFrameLayout.setStatusBarColor(statusBarColor);
        ThemeUtils.setLightStatusBar(window, ThemeUtils.isLightColor(statusBarColor));
        int stackedTabColor = mPrimaryColor;

        final float profileContentHeight = mProfileNameContainer.getHeight() + mProfileDetailsContainer.getHeight();
        final float tabOutlineAlphaFactor;
        if ((offset - spaceHeight) > 0) {
            tabOutlineAlphaFactor = 1f
                    - TwidereMathUtils.clamp((offset - spaceHeight) / profileContentHeight, 0, 1);
        } else {
            tabOutlineAlphaFactor = 1f;
        }

        if (mActionBarBackground != null) {
            mActionBarBackground.setFactor(factor);
            mActionBarBackground.setOutlineAlphaFactor(tabOutlineAlphaFactor);
        }

        if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
            mWindowOverlay.setAlpha(factor * tabOutlineAlphaFactor);
            //            setCompatToolbarOverlayAlpha(activity, factor * tabOutlineAlphaFactor);
        }

        final int currentTabColor = (Integer) sArgbEvaluator.evaluate(tabOutlineAlphaFactor, stackedTabColor,
                mCardBackgroundColor);

        final Drawable tabBackground = mPagerIndicator.getBackground();
        ((ColorDrawable) tabBackground).setColor(currentTabColor);
        final boolean tabItemIsDark = ThemeUtils.isLightColor(currentTabColor);

        if (mPreviousTabItemIsDark == 0 || (tabItemIsDark ? 1 : -1) != mPreviousTabItemIsDark) {
            final int tabContrastColor = ThemeUtils.getColorDependent(currentTabColor);
            mPagerIndicator.setIconColor(tabContrastColor);
            mPagerIndicator.setLabelColor(tabContrastColor);
            if (Config.coloredActionBar(activity, activity.getATEKey())) {
                mPagerIndicator.setStripColor(tabContrastColor);
            } else {
                mPagerIndicator.setStripColor(ThemeUtils.getOptimalAccentColor(mUiColor, tabContrastColor));
            }
            mPagerIndicator.updateAppearance();
        }
        mPreviousTabItemIsDark = tabItemIsDark ? 1 : -1;

        final int currentActionBarColor = (Integer) sArgbEvaluator.evaluate(factor, mActionBarShadowColor,
                stackedTabColor);
        final boolean actionItemIsDark = ThemeUtils.isLightColor(currentActionBarColor);
        if (mPreviousActionBarItemIsDark == 0 || (actionItemIsDark ? 1 : -1) != mPreviousActionBarItemIsDark) {
            ThemeUtils.applyToolbarItemColor(activity, mToolbar, currentActionBarColor);
        }
        mPreviousActionBarItemIsDark = actionItemIsDark ? 1 : -1;

        updateTitleAlpha();
    }

    private void updateTitleAlpha() {
        final int[] location = new int[2];
        mNameView.getLocationInWindow(location);
        final float nameShowingRatio = (mHeaderDrawerLayout.getPaddingTop() - location[1])
                / (float) mNameView.getHeight();
        final float textAlpha = TwidereMathUtils.clamp(nameShowingRatio, 0, 1);
        final Toolbar actionBarView = mToolbar;
        if (actionBarView != null) {
            final TextView titleView = ViewSupport.findViewByText(actionBarView, actionBarView.getTitle());
            if (titleView != null) {
                titleView.setAlpha(textAlpha);
            }
            final TextView subtitleView = ViewSupport.findViewByText(actionBarView, actionBarView.getSubtitle());
            if (subtitleView != null) {
                subtitleView.setAlpha(textAlpha);
            }
        }
    }

    @Override
    public Toolbar getToolbar() {
        return mToolbar;
    }

    @Override
    public float getControlBarOffset() {
        return 0;
    }

    @Override
    public void setControlBarOffset(float offset) {

    }

    @Override
    public int getControlBarHeight() {
        return 0;
    }

    @StringDef({ Referral.SEARCH_RESULT, Referral.USER_MENTION, Referral.STATUS, Referral.TIMELINE_STATUS,
            Referral.DIRECT, Referral.EXTERNAL, Referral.SELF_PROFILE })
    public @interface Referral {

        String SEARCH_RESULT = "search_result";
        String USER_MENTION = "user_mention";
        String STATUS = "status";
        String TIMELINE_STATUS = "timeline_status";
        String DIRECT = "direct";
        String EXTERNAL = "external";
        String SELF_PROFILE = "self_profile";
    }

    private static class ActionBarDrawable extends LayerDrawable {

        private final Drawable mShadowDrawable;
        private final ColorDrawable mColorDrawable;

        private float mFactor;
        private int mColor;
        private int mAlpha;
        private float mOutlineAlphaFactor;

        public ActionBarDrawable(Drawable shadow) {
            super(new Drawable[] { shadow, ActionBarColorDrawable.create(true) });
            mShadowDrawable = getDrawable(0);
            mColorDrawable = (ColorDrawable) getDrawable(1);
            setAlpha(0xFF);
            setOutlineAlphaFactor(1);
        }

        public int getColor() {
            return mColor;
        }

        public void setColor(int color) {
            mColor = color;
            mColorDrawable.setColor(color);
            setFactor(mFactor);
        }

        @TargetApi(Build.VERSION_CODES.LOLLIPOP)
        @Override
        public void getOutline(@NonNull Outline outline) {
            mColorDrawable.getOutline(outline);
            outline.setAlpha(mFactor * mOutlineAlphaFactor * 0.99f);
        }

        @Override
        public void setAlpha(int alpha) {
            mAlpha = alpha;
            setFactor(mFactor);
        }

        @Override
        public int getIntrinsicWidth() {
            return mColorDrawable.getIntrinsicWidth();
        }

        @Override
        public int getIntrinsicHeight() {
            return mColorDrawable.getIntrinsicHeight();
        }

        public void setFactor(float f) {
            mFactor = f;
            final int shadowAlpha = Math.round(mAlpha * TwidereMathUtils.clamp(1 - f, 0, 1));
            mShadowDrawable.setAlpha(shadowAlpha);
            final boolean hasColor = mColor != 0;
            final int colorAlpha = hasColor ? Math.round(mAlpha * TwidereMathUtils.clamp(f, 0, 1)) : 0;
            mColorDrawable.setAlpha(colorAlpha);
            invalidateSelf();
        }

        public void setOutlineAlphaFactor(float f) {
            mOutlineAlphaFactor = f;
            invalidateSelf();
        }

    }

    static class UserRelationshipLoader extends AsyncTaskLoader<SingleResponse<UserRelationship>> {

        private final Context context;
        @Nullable
        private final UserKey mAccountKey;
        @Nullable
        private final ParcelableUser mUser;

        public UserRelationshipLoader(final Context context, @Nullable final UserKey accountKey,
                @Nullable final ParcelableUser user) {
            super(context);
            this.context = context;
            this.mAccountKey = accountKey;
            this.mUser = user;
        }

        @Override
        public SingleResponse<UserRelationship> loadInBackground() {
            if (mAccountKey == null || mUser == null) {
                return SingleResponse.getInstance(new MicroBlogException("Null parameters"));
            }
            final UserKey userKey = mUser.key;
            final boolean isFiltering = DataStoreUtils.isFilteringUser(context, userKey);
            if (mAccountKey.equals(mUser.key)) {
                return SingleResponse.getInstance(new UserRelationship(mAccountKey, userKey, null, isFiltering));
            }
            final ParcelableCredentials credentials = ParcelableCredentialsUtils.getCredentials(context,
                    mAccountKey);
            if (credentials == null) {
                return SingleResponse.getInstance(new MicroBlogException("No Account"));
            }
            if (MicroBlogAPIFactory.isStatusNetCredentials(credentials)) {
                if (!UserKeyUtils.isSameHost(mAccountKey, mUser.key)) {
                    return SingleResponse.getInstance(new UserRelationship(mUser, isFiltering));
                }
            }
            final MicroBlog twitter = MicroBlogAPIFactory.getInstance(context, mAccountKey, false);
            if (twitter == null) {
                return SingleResponse.getInstance(new MicroBlogException("No Account"));
            }
            try {
                final Relationship relationship = twitter.showFriendship(mUser.key.getId());
                if (relationship.isSourceBlockingTarget() || relationship.isSourceBlockedByTarget()) {
                    Utils.setLastSeen(context, userKey, -1);
                } else {
                    Utils.setLastSeen(context, userKey, System.currentTimeMillis());
                }
                Utils.updateRelationship(context, mAccountKey, userKey, relationship);
                return SingleResponse
                        .getInstance(new UserRelationship(mAccountKey, userKey, relationship, isFiltering));
            } catch (final MicroBlogException e) {
                return SingleResponse.getInstance(e);
            }
        }

        @Override
        protected void onStartLoading() {
            forceLoad();
        }
    }

    static class UserRelationship extends CachedRelationship {
        boolean filtering;
        boolean can_dm;

        public UserRelationship(@NonNull UserKey accountKey, @NonNull UserKey userKey,
                @Nullable Relationship relationship, boolean filtering) {
            super(relationship, accountKey, userKey);
            this.filtering = filtering;
            if (relationship != null) {
                this.can_dm = relationship.canSourceDMTarget();
            }
        }

        public UserRelationship(@NonNull ParcelableUser user, boolean filtering) {
            this.account_key = user.account_key;
            this.user_key = user.key;
            this.filtering = filtering;
            if (user.extras != null) {
                this.following = user.is_following;
                this.followed_by = user.extras.statusnet_followed_by;
                this.blocking = user.extras.statusnet_blocking;
                this.blocked_by = user.extras.statusnet_blocked_by;
                this.can_dm = user.extras.statusnet_followed_by;
            }
        }

        public boolean check(@NonNull ParcelableUser user) {
            if (!account_key.equals(user.account_key)) {
                return false;
            }
            return (user.extras != null && TextUtils.equals(user_key.getId(), user.extras.unique_id))
                    || TextUtils.equals(user_key.getId(), user.key.getId());
        }

    }

    private static class CacheUserInfoTask
            extends AbstractTask<Pair<ParcelableUser, ? extends CachedRelationship>, Object, Object> {
        private final Context context;

        public CacheUserInfoTask(Context context) {
            this.context = context;
        }

        @Override
        public Object doLongOperation(Pair<ParcelableUser, ? extends CachedRelationship> args) {
            final ContentResolver resolver = context.getContentResolver();
            final ParcelableUser user = args.first;
            resolver.insert(CachedUsers.CONTENT_URI, ParcelableUserValuesCreator.create(user));
            resolver.insert(CachedRelationships.CONTENT_URI, CachedRelationshipValuesCreator.create(args.second));
            return null;
        }
    }
}