/* * Twidere - Twitter client for Android * * Copyright (C) 2012-2014 Mariotaku Lee * * 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 . */ package org.mariotaku.twidere.fragment.support; import android.animation.Animator; import android.animation.Animator.AnimatorListener; import android.animation.AnimatorSet; import android.animation.ObjectAnimator; import android.app.Activity; import android.content.ContentResolver; import android.content.ContentValues; import android.content.Context; import android.content.Intent; import android.content.SharedPreferences; import android.content.SharedPreferences.Editor; import android.content.SharedPreferences.OnSharedPreferenceChangeListener; import android.content.res.Resources; import android.database.Cursor; import android.graphics.Bitmap; import android.graphics.Matrix; import android.graphics.PorterDuff.Mode; import android.graphics.Rect; import android.graphics.RectF; import android.graphics.drawable.Drawable; import android.net.Uri; import android.os.Bundle; import android.support.annotation.NonNull; import android.support.annotation.Nullable; import android.support.v4.app.FragmentActivity; import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.content.CursorLoader; import android.support.v4.content.Loader; import android.support.v4.content.res.ResourcesCompat; import android.support.v4.util.LongSparseArray; import android.support.v4.util.Pair; import android.support.v4.view.MenuItemCompat; import android.support.v7.view.SupportMenuInflater; import android.support.v7.widget.ActionMenuView; import android.support.v7.widget.ActionMenuView.OnMenuItemClickListener; import android.support.v7.widget.FixedLinearLayoutManager; import android.support.v7.widget.LinearLayoutManager; import android.support.v7.widget.RecyclerView; import android.support.v7.widget.RecyclerView.Adapter; import android.support.v7.widget.RecyclerView.ViewHolder; import android.view.ContextThemeWrapper; import android.view.KeyEvent; import android.view.LayoutInflater; import android.view.Menu; import android.view.MenuItem; import android.view.View; import android.view.View.OnClickListener; import android.view.ViewGroup; import android.view.animation.DecelerateInterpolator; import android.widget.AdapterView; import android.widget.ImageView; import android.widget.ListAdapter; import android.widget.ListView; import android.widget.TextView; import com.commonsware.cwac.merge.MergeAdapter; import com.nostra13.universalimageloader.core.assist.FailReason; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import org.mariotaku.sqliteqb.library.Expression; import org.mariotaku.twidere.R; import org.mariotaku.twidere.activity.SettingsActivity; import org.mariotaku.twidere.activity.iface.IThemedActivity; import org.mariotaku.twidere.activity.support.ComposeActivity; import org.mariotaku.twidere.activity.support.HomeActivity; import org.mariotaku.twidere.activity.support.QuickSearchBarActivity; import org.mariotaku.twidere.adapter.ArrayAdapter; import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.menu.support.AccountToggleProvider; import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.provider.TwidereDataStore.Accounts; import org.mariotaku.twidere.util.CompareUtils; import org.mariotaku.twidere.util.DataStoreUtils; import org.mariotaku.twidere.util.KeyboardShortcutsHandler; import org.mariotaku.twidere.util.KeyboardShortcutsHandler.KeyboardShortcutCallback; import org.mariotaku.twidere.util.ListViewUtils; import org.mariotaku.twidere.util.MediaLoaderWrapper; import org.mariotaku.twidere.util.SharedPreferencesWrapper; import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.TransitionUtils; import org.mariotaku.twidere.util.TwitterContentUtils; import org.mariotaku.twidere.util.UserColorNameManager; import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.util.content.SupportFragmentReloadCursorObserver; import org.mariotaku.twidere.util.dagger.GeneralComponentHelper; import org.mariotaku.twidere.view.ShapedImageView; import java.util.ArrayList; import java.util.Collections; import java.util.List; import javax.inject.Inject; public class AccountsDashboardFragment extends BaseSupportFragment implements LoaderCallbacks, OnSharedPreferenceChangeListener, ImageLoadingListener, OnClickListener, KeyboardShortcutCallback, AdapterView.OnItemClickListener { private final Rect mSystemWindowsInsets = new Rect(); private ContentResolver mResolver; private MergeAdapter mAdapter; private AccountSelectorAdapter mAccountsAdapter; private AccountOptionsAdapter mAccountOptionsAdapter; private AppMenuAdapter mAppMenuAdapter; private ListView mListView; private View mAccountSelectorView; private RecyclerView mAccountsSelector; private ImageView mAccountProfileBannerView; private ImageView mFloatingProfileImageSnapshotView; private ShapedImageView mAccountProfileImageView; private TextView mAccountProfileNameView, mAccountProfileScreenNameView; private ActionMenuView mAccountsToggleMenu; private View mAccountProfileContainer; private View mNoAccountContainer; private Context mThemedContext; private AccountToggleProvider mAccountActionProvider; private final SupportFragmentReloadCursorObserver mReloadContentObserver = new SupportFragmentReloadCursorObserver( this, 0, this) { @Override public void onChange(boolean selfChange, @Nullable Uri uri) { final ContentResolver cr = getContentResolver(); if (cr == null) return; final Cursor c = cr.query(Accounts.CONTENT_URI, Accounts.COLUMNS, null, null, Accounts.SORT_POSITION); try { updateAccountProviderData(c); } finally { Utils.closeSilently(c); } super.onChange(selfChange, uri); } }; private boolean mSwitchAccountAnimationPlaying; public long[] getActivatedAccountIds() { if (mAccountActionProvider != null) { return mAccountActionProvider.getActivatedAccountIds(); } return DataStoreUtils.getActivatedAccountIds(getActivity()); } @Override public boolean handleKeyboardShortcutSingle(@NonNull final KeyboardShortcutsHandler handler, final int keyCode, @NonNull final KeyEvent event, int metaState) { return false; } @Override public boolean isKeyboardShortcutHandled(@NonNull KeyboardShortcutsHandler handler, int keyCode, @NonNull KeyEvent event, int metaState) { final String action = handler.getKeyAction(CONTEXT_TAG_NAVIGATION, keyCode, event, metaState); return ACTION_NAVIGATION_PREVIOUS.equals(action) || ACTION_NAVIGATION_NEXT.equals(action); } @Override public boolean handleKeyboardShortcutRepeat(@NonNull final KeyboardShortcutsHandler handler, final int keyCode, final int repeatCount, @NonNull final KeyEvent event, int metaState) { final String action = handler.getKeyAction(CONTEXT_TAG_NAVIGATION, keyCode, event, metaState); if (action == null) return false; final int offset; switch (action) { case ACTION_NAVIGATION_PREVIOUS: { offset = -1; break; } case ACTION_NAVIGATION_NEXT: { offset = 1; break; } default: { return false; } } final int firstVisiblePosition = ListViewUtils.getFirstFullyVisiblePosition(mListView); final int selectedItem = mListView.getSelectedItemPosition(); final int count = mListView.getCount(); int resultPosition; if (!mListView.isFocused() || selectedItem == ListView.INVALID_POSITION) { resultPosition = firstVisiblePosition; } else { resultPosition = selectedItem + offset; while (resultPosition >= 0 && resultPosition < count && !mAdapter.isEnabled(resultPosition)) { resultPosition += offset; } } final View focusedChild = mListView.getFocusedChild(); if (focusedChild == null) { mListView.requestChildFocus(mListView.getChildAt(0), null); } if (resultPosition >= 0 && resultPosition < count) { mListView.setSelection(resultPosition); } return true; } @Override public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { switch (requestCode) { case REQUEST_SETTINGS: { if (data == null) return; final FragmentActivity activity = getActivity(); if (data.getBooleanExtra(EXTRA_CHANGED, false) && activity instanceof IThemedActivity) { ((IThemedActivity) activity).restart(); } return; } } super.onActivityResult(requestCode, resultCode, data); } @Override public void onResume() { super.onResume(); updateDefaultAccountState(); } @Override public void onClick(View v) { switch (v.getId()) { case R.id.profile_container: { final ParcelableAccount account = mAccountsAdapter.getSelectedAccount(); if (account == null) return; final FragmentActivity activity = getActivity(); final Bundle activityOption = Utils.makeSceneTransitionOption(activity, new Pair(mAccountProfileImageView, UserFragment.TRANSITION_NAME_PROFILE_IMAGE)); Utils.openUserProfile(activity, account.account_id, account.account_id, account.screen_name, activityOption); break; } } } @Override public Loader onCreateLoader(final int id, final Bundle args) { return new CursorLoader(getActivity(), Accounts.CONTENT_URI, Accounts.COLUMNS, null, null, Accounts.SORT_POSITION); } @Override public void onLoadFinished(final Loader loader, final Cursor data) { updateAccountProviderData(data); } private void updateAccountProviderData(@Nullable final Cursor cursor) { if (cursor == null) return; final Menu menu = mAccountsToggleMenu.getMenu(); mAccountActionProvider = (AccountToggleProvider) MenuItemCompat.getActionProvider(menu.findItem(R.id.select_account)); final ParcelableAccount[] accounts = ParcelableAccount.getAccounts(cursor); if (accounts.length > 0) { mNoAccountContainer.setVisibility(View.GONE); mAccountProfileContainer.setVisibility(View.VISIBLE); } else { mNoAccountContainer.setVisibility(View.VISIBLE); mAccountProfileContainer.setVisibility(View.INVISIBLE); } long defaultId = -1; for (ParcelableAccount account : accounts) { if (account.is_activated) { defaultId = account.account_id; break; } } mAccountsAdapter.setAccounts(accounts); mAccountsAdapter.setSelectedAccountId(mPreferences.getLong(KEY_DEFAULT_ACCOUNT_ID, defaultId)); mAccountOptionsAdapter.setSelectedAccount(mAccountsAdapter.getSelectedAccount()); if (mAccountActionProvider != null) { mAccountActionProvider.setExclusive(false); mAccountActionProvider.setAccounts(accounts); } initAccountActionsAdapter(accounts); updateAccountOptionsSeparatorLabel(null); updateDefaultAccountState(); } @Override public void onLoaderReset(final Loader loader) { } @Override public void onItemClick(final AdapterView parent, final View v, final int position, final long id) { final ListAdapter adapter = mAdapter.getAdapter(position); final Object item = mAdapter.getItem(position); if (adapter instanceof AccountOptionsAdapter) { final ParcelableAccount account = mAccountsAdapter.getSelectedAccount(); if (account == null || !(item instanceof OptionItem)) return; final OptionItem option = (OptionItem) item; switch (option.id) { case R.id.search: { final Intent intent = new Intent(getActivity(), QuickSearchBarActivity.class); intent.putExtra(EXTRA_ACCOUNT_ID, account.account_id); startActivity(intent); closeAccountsDrawer(); break; } case R.id.compose: { final Intent composeIntent = new Intent(INTENT_ACTION_COMPOSE); composeIntent.setClass(getActivity(), ComposeActivity.class); composeIntent.putExtra(EXTRA_ACCOUNT_IDS, new long[]{account.account_id}); startActivity(composeIntent); break; } case R.id.favorites: { Utils.openUserFavorites(getActivity(), account.account_id, account.account_id, account.screen_name); break; } case R.id.lists: { Utils.openUserLists(getActivity(), account.account_id, account.account_id, account.screen_name); break; } case R.id.edit: { Utils.openProfileEditor(getActivity(), account.account_id); break; } } } else if (adapter instanceof AppMenuAdapter) { if (!(item instanceof OptionItem)) return; final OptionItem option = (OptionItem) item; switch (option.id) { case R.id.accounts: { Utils.openAccountsManager(getActivity()); break; } case R.id.drafts: { Utils.openDrafts(getActivity()); break; } case R.id.filters: { Utils.openFilters(getActivity()); break; } case R.id.settings: { final Intent intent = new Intent(getActivity(), SettingsActivity.class); intent.addFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT); startActivityForResult(intent, REQUEST_SETTINGS); break; } } closeAccountsDrawer(); } } @Override public void onLoadingStarted(String imageUri, View view) { } @Override public void onLoadingFailed(String imageUri, View view, FailReason failReason) { } @Override public void onLoadingComplete(String imageUri, View view, Bitmap loadedImage) { view.setTag(imageUri); } @Override public void onLoadingCancelled(String imageUri, View view) { } @Override public void onSharedPreferenceChanged(final SharedPreferences sharedPreferences, final String key) { if (KEY_DEFAULT_ACCOUNT_ID.equals(key)) { updateDefaultAccountState(); } } public void setStatusBarHeight(int height) { mAccountProfileContainer.setPadding(0, height, 0, 0); } @Override protected void fitSystemWindows(Rect insets) { mSystemWindowsInsets.set(insets); updateSystemWindowsInsets(); } private void updateSystemWindowsInsets() { if (mAccountProfileContainer == null) return; final HomeActivity activity = (HomeActivity) getActivity(); final Rect insets = mSystemWindowsInsets; if (!activity.getDefaultSystemWindowsInsets(insets)) return; final int top = Utils.getInsetsTopWithoutActionBarHeight(getActivity(), insets.top); mAccountProfileContainer.setPadding(0, top, 0, 0); } @Override public void onActivityCreated(final Bundle savedInstanceState) { super.onActivityCreated(savedInstanceState); mResolver = getContentResolver(); final View view = getView(); assert view != null; final Context context = view.getContext(); mListView.setItemsCanFocus(true); mAdapter = new MergeAdapter(); final LayoutInflater inflater = getLayoutInflater(savedInstanceState); mAccountsAdapter = new AccountSelectorAdapter(context, inflater, this); mAccountOptionsAdapter = new AccountOptionsAdapter(context); mAppMenuAdapter = new AppMenuAdapter(context); mAccountSelectorView = inflater.inflate(R.layout.header_drawer_account_selector, mListView, false); mAccountsSelector = (RecyclerView) mAccountSelectorView.findViewById(R.id.other_accounts_list); final LinearLayoutManager layoutManager = new FixedLinearLayoutManager(context); layoutManager.setOrientation(LinearLayoutManager.HORIZONTAL); layoutManager.setStackFromEnd(true); mAccountsSelector.setLayoutManager(layoutManager); mAccountsSelector.setAdapter(mAccountsAdapter); mAccountsSelector.setItemAnimator(null); mAccountProfileContainer = mAccountSelectorView.findViewById(R.id.profile_container); mNoAccountContainer = mAccountSelectorView.findViewById(R.id.no_account_container); mAccountProfileImageView = (ShapedImageView) mAccountSelectorView.findViewById(R.id.profile_image); mAccountProfileBannerView = (ImageView) mAccountSelectorView.findViewById(R.id.account_profile_banner); mFloatingProfileImageSnapshotView = (ImageView) mAccountSelectorView.findViewById(R.id.floating_profile_image_snapshot); mAccountProfileNameView = (TextView) mAccountSelectorView.findViewById(R.id.name); mAccountProfileScreenNameView = (TextView) mAccountSelectorView.findViewById(R.id.screen_name); mAccountsToggleMenu = (ActionMenuView) mAccountSelectorView.findViewById(R.id.account_dashboard_menu); final SupportMenuInflater menuInflater = new SupportMenuInflater(context); menuInflater.inflate(R.menu.action_dashboard_timeline_toggle, mAccountsToggleMenu.getMenu()); mAccountsToggleMenu.setOnMenuItemClickListener(new OnMenuItemClickListener() { @Override public boolean onMenuItemClick(MenuItem item) { if (item.getGroupId() != AccountToggleProvider.MENU_GROUP) { switch (item.getItemId()) { case R.id.compose: { final ParcelableAccount account = mAccountsAdapter.getSelectedAccount(); if (account == null) return true; final Intent composeIntent = new Intent(INTENT_ACTION_COMPOSE); composeIntent.setClass(getActivity(), ComposeActivity.class); composeIntent.putExtra(EXTRA_ACCOUNT_IDS, new long[]{account.account_id}); startActivity(composeIntent); return true; } } return false; } final ParcelableAccount[] accounts = mAccountActionProvider.getAccounts(); final ParcelableAccount account = accounts[item.getOrder()]; final ContentValues values = new ContentValues(); final boolean newActivated = !account.is_activated; mAccountActionProvider.setAccountActivated(account.account_id, newActivated); values.put(Accounts.IS_ACTIVATED, newActivated); final String where = Expression.equals(Accounts.ACCOUNT_ID, account.account_id).getSQL(); mResolver.update(Accounts.CONTENT_URI, values, where, null); return true; } }); mAccountProfileContainer.setOnClickListener(this); mAdapter.addView(mAccountSelectorView, true); mAdapter.addAdapter(mAccountOptionsAdapter); mAdapter.addView(inflater.inflate(R.layout.layout_divider_drawer, mListView, false), false); mAdapter.addAdapter(mAppMenuAdapter); mListView.setAdapter(mAdapter); mListView.setOnItemClickListener(this); mPreferences.registerOnSharedPreferenceChangeListener(this); getLoaderManager().initLoader(0, null, this); updateSystemWindowsInsets(); } @Override public View onCreateView(final LayoutInflater inflater, final ViewGroup container, final Bundle savedInstanceState) { return inflater.inflate(R.layout.fragment_accounts_dashboard, container, false); } @Override public void onBaseViewCreated(View view, Bundle savedInstanceState) { super.onBaseViewCreated(view, savedInstanceState); mListView = (ListView) view.findViewById(android.R.id.list); } @Override public void onStart() { super.onStart(); final ContentResolver resolver = getContentResolver(); resolver.registerContentObserver(Accounts.CONTENT_URI, true, mReloadContentObserver); getLoaderManager().restartLoader(0, null, this); } @Override public void onStop() { final ContentResolver resolver = getContentResolver(); resolver.unregisterContentObserver(mReloadContentObserver); super.onStop(); } void initAccountActionsAdapter(ParcelableAccount[] accounts) { mAccountOptionsAdapter.clear(); mAccountOptionsAdapter.add(new OptionItem(android.R.string.search_go, R.drawable.ic_action_search, R.id.search)); if (mPreferences.getBoolean(KEY_I_WANT_MY_STARS_BACK)) { mAccountOptionsAdapter.add(new OptionItem(R.string.favorites, R.drawable.ic_action_star, R.id.favorites)); } else { mAccountOptionsAdapter.add(new OptionItem(R.string.likes, R.drawable.ic_action_heart, R.id.favorites)); } mAccountOptionsAdapter.add(new OptionItem(R.string.lists, R.drawable.ic_action_list, R.id.lists)); } private void closeAccountsDrawer() { final Activity activity = getActivity(); if (activity instanceof HomeActivity) { ((HomeActivity) activity).closeAccountsDrawer(); } } private void getLocationOnScreen(View view, RectF rectF) { final int[] location = new int[2]; view.getLocationOnScreen(location); rectF.set(location[0], location[1], location[0] + view.getWidth(), location[1] + view.getHeight()); } @Override public Context getThemedContext() { if (mThemedContext != null) return mThemedContext; final Context context = getActivity(); final int themeResource = ThemeUtils.getDrawerThemeResource(context); return mThemedContext = new ContextThemeWrapper(context, themeResource); } private void onAccountSelected(AccountProfileImageViewHolder holder, final ParcelableAccount account) { if (mSwitchAccountAnimationPlaying) return; final ImageView snapshotView = mFloatingProfileImageSnapshotView; final ShapedImageView profileImageView = mAccountProfileImageView; final ShapedImageView clickedImageView = holder.getIconView(); // Reset snapshot view position snapshotView.setPivotX(0); snapshotView.setPivotY(0); snapshotView.setTranslationX(0); snapshotView.setTranslationY(0); final Matrix matrix = new Matrix(); final RectF sourceBounds = new RectF(), destBounds = new RectF(), snapshotBounds = new RectF(); getLocationOnScreen(clickedImageView, sourceBounds); getLocationOnScreen(profileImageView, destBounds); getLocationOnScreen(snapshotView, snapshotBounds); final float finalScale = destBounds.width() / sourceBounds.width(); final Bitmap snapshotBitmap = TransitionUtils.createViewBitmap(clickedImageView, matrix, new RectF(0, 0, sourceBounds.width(), sourceBounds.height())); final ViewGroup.LayoutParams lp = snapshotView.getLayoutParams(); lp.width = clickedImageView.getWidth(); lp.height = clickedImageView.getHeight(); snapshotView.setLayoutParams(lp); // Copied from MaterialNavigationDrawer: https://github.com/madcyph3r/AdvancedMaterialDrawer/ AnimatorSet set = new AnimatorSet(); set.play(ObjectAnimator.ofFloat(snapshotView, View.TRANSLATION_X, sourceBounds.left - snapshotBounds.left, destBounds.left - snapshotBounds.left)) .with(ObjectAnimator.ofFloat(snapshotView, View.TRANSLATION_Y, sourceBounds.top - snapshotBounds.top, destBounds.top - snapshotBounds.top)) .with(ObjectAnimator.ofFloat(snapshotView, View.SCALE_X, 1, finalScale)) .with(ObjectAnimator.ofFloat(snapshotView, View.SCALE_Y, 1, finalScale)) .with(ObjectAnimator.ofFloat(profileImageView, View.ALPHA, 1, 0)) .with(ObjectAnimator.ofFloat(clickedImageView, View.SCALE_X, 0, 1)) .with(ObjectAnimator.ofFloat(clickedImageView, View.SCALE_Y, 0, 1)); final long animationTransition = 400; set.setDuration(animationTransition); set.setInterpolator(new DecelerateInterpolator()); set.addListener(new AnimatorListener() { private Drawable clickedDrawable; private int[] clickedColors; @Override public void onAnimationStart(Animator animation) { snapshotView.setVisibility(View.VISIBLE); snapshotView.setImageBitmap(snapshotBitmap); final Drawable profileDrawable = profileImageView.getDrawable(); clickedDrawable = clickedImageView.getDrawable(); clickedColors = clickedImageView.getBorderColors(); final ParcelableAccount oldSelectedAccount = mAccountsAdapter.getSelectedAccount(); mMediaLoader.displayDashboardProfileImage(clickedImageView, oldSelectedAccount.profile_image_url, profileDrawable); // mMediaLoader.displayDashboardProfileImage(profileImageView, // account.profile_image_url, clickedDrawable); clickedImageView.setBorderColors(profileImageView.getBorderColors()); mSwitchAccountAnimationPlaying = true; } @Override public void onAnimationEnd(Animator animation) { finishAnimation(); } @Override public void onAnimationCancel(Animator animation) { finishAnimation(); } @Override public void onAnimationRepeat(Animator animation) { } private void finishAnimation() { final Editor editor = mPreferences.edit(); editor.putLong(KEY_DEFAULT_ACCOUNT_ID, account.account_id); editor.apply(); mAccountsAdapter.setSelectedAccountId(account.account_id); mAccountOptionsAdapter.setSelectedAccount(account); updateAccountOptionsSeparatorLabel(clickedDrawable); snapshotView.setVisibility(View.INVISIBLE); snapshotView.setImageDrawable(null); profileImageView.setImageDrawable(clickedDrawable); profileImageView.setBorderColors(clickedColors); profileImageView.setAlpha(1f); clickedImageView.setScaleX(1); clickedImageView.setScaleY(1); clickedImageView.setAlpha(1f); mSwitchAccountAnimationPlaying = false; } }); set.start(); } private void updateAccountOptionsSeparatorLabel(Drawable profileImageSnapshot) { final ParcelableAccount account = mAccountsAdapter.getSelectedAccount(); if (account == null) { return; } mAccountProfileNameView.setText(account.name); mAccountProfileScreenNameView.setText("@" + account.screen_name); mMediaLoader.displayDashboardProfileImage(mAccountProfileImageView, account.profile_image_url, profileImageSnapshot); mAccountProfileImageView.setBorderColors(account.color); final int bannerWidth = mAccountProfileBannerView.getWidth(); final Resources res = getResources(); final int defWidth = res.getDisplayMetrics().widthPixels; final int width = bannerWidth > 0 ? bannerWidth : defWidth; final String bannerUrl = TwitterContentUtils.getBestBannerUrl(account.profile_banner_url, width); final ImageView bannerView = mAccountProfileBannerView; if (bannerView.getDrawable() == null || !CompareUtils.objectEquals(bannerUrl, bannerView.getTag())) { mMediaLoader.displayProfileBanner(mAccountProfileBannerView, bannerUrl, this); } else { mMediaLoader.cancelDisplayTask(mAccountProfileBannerView); } } private void updateDefaultAccountState() { } public static final class AccountOptionsAdapter extends OptionItemsAdapter { private final boolean mNameFirst; private ParcelableAccount mSelectedAccount; AccountOptionsAdapter(final Context context) { super(context); mNameFirst = mPreferences.getBoolean(KEY_NAME_FIRST); } public void setSelectedAccount(ParcelableAccount account) { mSelectedAccount = account; notifyDataSetChanged(); } @Override public boolean isEnabled(final int position) { return mSelectedAccount != null; } @Override protected String getTitle(int position, OptionItem option) { final ParcelableAccount account = mSelectedAccount; if (account != null && option.id == R.id.compose) { final Context context = getContext(); final String displayName = mUserColorNameManager.getDisplayName(-1, account.name, account.screen_name, mNameFirst, false); return context.getString(R.string.tweet_from_name, displayName); } return super.getTitle(position, option); } } static class AccountProfileImageViewHolder extends ViewHolder implements OnClickListener { private final AccountSelectorAdapter adapter; private final ShapedImageView icon; public AccountProfileImageViewHolder(AccountSelectorAdapter adapter, View itemView) { super(itemView); this.adapter = adapter; itemView.setOnClickListener(this); icon = (ShapedImageView) itemView.findViewById(android.R.id.icon); } public ShapedImageView getIconView() { return icon; } @Override public void onClick(View v) { adapter.dispatchItemSelected(this); } } private static class AccountSelectorAdapter extends Adapter { private final LayoutInflater mInflater; private final MediaLoaderWrapper mImageLoader; private final AccountsDashboardFragment mFragment; private final LongSparseArray positionMap = new LongSparseArray<>(); private ParcelableAccount[] mInternalAccounts; AccountSelectorAdapter(Context context, LayoutInflater inflater, AccountsDashboardFragment fragment) { mInflater = inflater; mImageLoader = fragment.mMediaLoader; mFragment = fragment; setHasStableIds(true); } private static int indexOfAccount(List accounts, long accountId) { for (int i = 0, j = accounts.size(); i < j; i++) { if (accounts.get(i).account_id == accountId) return i; } return -1; } public ParcelableAccount getAdapterAccount(int adapterPosition) { if (mInternalAccounts == null || mInternalAccounts.length < 1) { return null; } return mInternalAccounts[adapterPosition + 1]; } @Nullable public ParcelableAccount getSelectedAccount() { if (mInternalAccounts == null || mInternalAccounts.length == 0) { return null; } return mInternalAccounts[0]; } public long getSelectedAccountId() { final ParcelableAccount selectedAccount = getSelectedAccount(); if (selectedAccount == null) return -1; return selectedAccount.account_id; } public void setSelectedAccountId(long accountId) { final ParcelableAccount selectedAccount = getSelectedAccount(); if (selectedAccount == null) return; swap(accountId, selectedAccount.account_id); } @Override public AccountProfileImageViewHolder onCreateViewHolder(ViewGroup parent, int viewType) { final View view = mInflater.inflate(R.layout.adapter_item_dashboard_account, parent, false); return new AccountProfileImageViewHolder(this, view); } @Override public void onBindViewHolder(AccountProfileImageViewHolder holder, int position) { // holder.itemView.setAlpha(c.getInt(mIndices.is_activated) == 1 ? 1 : 0.5f); final ParcelableAccount account = getAdapterAccount(position); // holder.icon.setImageDrawable(null); mImageLoader.displayDashboardProfileImage(holder.icon, account.profile_image_url, null); holder.icon.setBorderColor(account.color); } @Override public long getItemId(int position) { return getAdapterAccount(position).account_id; } @Override public int getItemCount() { if (mInternalAccounts == null || mInternalAccounts.length == 0) return 0; return mInternalAccounts.length - 1; } public void setAccounts(ParcelableAccount[] accounts) { if (accounts != null) { final ParcelableAccount[] previousAccounts = mInternalAccounts; mInternalAccounts = new ParcelableAccount[accounts.length]; int tempIdx = 0; final List tempList = new ArrayList<>(); Collections.addAll(tempList, accounts); if (previousAccounts != null) { for (ParcelableAccount previousAccount : previousAccounts) { final int idx = indexOfAccount(tempList, previousAccount.account_id); if (idx >= 0) { mInternalAccounts[tempIdx++] = tempList.remove(idx); } } } for (ParcelableAccount account : tempList) { mInternalAccounts[tempIdx++] = account; } } else { mInternalAccounts = null; } notifyDataSetChanged(); } private void dispatchItemSelected(AccountProfileImageViewHolder holder) { mFragment.onAccountSelected(holder, getAdapterAccount(holder.getAdapterPosition())); } private void swap(long fromId, long toId) { int fromIdx = -1, toIdx = -1; for (int i = 0, j = mInternalAccounts.length; i < j; i++) { final ParcelableAccount account = mInternalAccounts[i]; if (account.account_id == fromId) { fromIdx = i; } if (account.account_id == toId) { toIdx = i; } } if (fromIdx < 0 || toIdx < 0) return; final ParcelableAccount temp = mInternalAccounts[toIdx]; mInternalAccounts[toIdx] = mInternalAccounts[fromIdx]; mInternalAccounts[fromIdx] = temp; notifyDataSetChanged(); } } private static final class AppMenuAdapter extends OptionItemsAdapter { public AppMenuAdapter(final Context context) { super(context); add(new OptionItem(R.string.accounts, R.drawable.ic_action_accounts, R.id.accounts)); add(new OptionItem(R.string.drafts, R.drawable.ic_action_draft, R.id.drafts)); add(new OptionItem(R.string.filters, R.drawable.ic_action_speaker_muted, R.id.filters)); add(new OptionItem(R.string.settings, R.drawable.ic_action_settings, R.id.settings)); } } public static class OptionItem { private final int name, icon, id; OptionItem(final int name, final int icon, final int id) { this.name = name; this.icon = icon; this.id = id; } @Override public boolean equals(final Object obj) { if (this == obj) return true; if (obj == null) return false; if (!(obj instanceof OptionItem)) return false; final OptionItem other = (OptionItem) obj; if (icon != other.icon) return false; if (id != other.id) return false; return name == other.name; } @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + icon; result = prime * result + id; result = prime * result + name; return result; } @Override public String toString() { return "AccountOption{name=" + name + ", icon=" + icon + ", id=" + id + "}"; } } public static abstract class OptionItemsAdapter extends ArrayAdapter { @Inject UserColorNameManager mUserColorNameManager; @Inject SharedPreferencesWrapper mPreferences; private final int mActionIconColor; OptionItemsAdapter(final Context context) { super(context, R.layout.list_item_dashboard_menu); GeneralComponentHelper.build(context).inject(this); mActionIconColor = ThemeUtils.getThemeForegroundColor(context); } @Override public View getView(final int position, final View convertView, final ViewGroup parent) { final View view = super.getView(position, convertView, parent); final OptionItem option = getItem(position); final TextView text1 = (TextView) view.findViewById(android.R.id.text1); final ImageView icon = (ImageView) view.findViewById(android.R.id.icon); text1.setText(getTitle(position, option)); icon.setImageDrawable(ResourcesCompat.getDrawable(icon.getResources(), option.icon, null)); icon.setColorFilter(mActionIconColor, Mode.SRC_ATOP); return view; } protected String getTitle(int position, OptionItem option) { return getContext().getString(option.name); } } }