redesigned account selector

This commit is contained in:
Mariotaku Lee 2015-03-21 22:53:26 +08:00
parent 72aaf3a887
commit 8ad206a9c8
6 changed files with 398 additions and 93 deletions

View File

@ -25,6 +25,7 @@ import android.graphics.Color;
import android.os.Parcel; import android.os.Parcel;
import android.os.Parcelable; import android.os.Parcelable;
import android.support.annotation.NonNull; import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import org.mariotaku.querybuilder.Columns.Column; import org.mariotaku.querybuilder.Columns.Column;
import org.mariotaku.querybuilder.Expression; import org.mariotaku.querybuilder.Expression;
@ -144,24 +145,37 @@ public class ParcelableAccount implements Parcelable {
return list.toArray(new ParcelableAccount[list.size()]); return list.toArray(new ParcelableAccount[list.size()]);
} }
public static ParcelableAccount[] getAccounts(final Context context, final long[] accountIds) { @NonNull
public static ParcelableAccount[] getAccounts(@Nullable final Context context, @Nullable final long[] accountIds) {
if (context == null) return new ParcelableAccount[0]; if (context == null) return new ParcelableAccount[0];
final String where = accountIds != null ? Expression.in(new Column(Accounts.ACCOUNT_ID), final String where = accountIds != null ? Expression.in(new Column(Accounts.ACCOUNT_ID),
new RawItemArray(accountIds)).getSQL() : null; new RawItemArray(accountIds)).getSQL() : null;
final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI, final Cursor cur = ContentResolverUtils.query(context.getContentResolver(), Accounts.CONTENT_URI,
Accounts.COLUMNS_NO_CREDENTIALS, where, null, null); Accounts.COLUMNS_NO_CREDENTIALS, where, null, null);
if (cur == null) return new ParcelableAccount[0]; if (cur == null) return new ParcelableAccount[0];
return getAccounts(cur, new Indices(cur));
}
@NonNull
public static ParcelableAccount[] getAccounts(@Nullable final Cursor cursor) {
if (cursor == null) return new ParcelableAccount[0];
return getAccounts(cursor, new Indices(cursor));
}
@NonNull
public static ParcelableAccount[] getAccounts(@Nullable final Cursor cursor,@Nullable final Indices indices) {
if (cursor == null || indices == null) return new ParcelableAccount[0];
try { try {
final Indices idx = new Indices(cur); cursor.moveToFirst();
cur.moveToFirst(); final ParcelableAccount[] names = new ParcelableAccount[cursor.getCount()];
final ParcelableAccount[] names = new ParcelableAccount[cur.getCount()]; while (!cursor.isAfterLast()) {
while (!cur.isAfterLast()) { names[cursor.getPosition()] = new ParcelableAccount(cursor, indices);
names[cur.getPosition()] = new ParcelableAccount(cur, idx); cursor.moveToNext();
cur.moveToNext();
} }
return names; return names;
} finally { } finally {
cur.close(); cursor.close();
} }
} }

View File

@ -19,6 +19,10 @@
package org.mariotaku.twidere.fragment.support; 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.app.Activity;
import android.content.ContentResolver; import android.content.ContentResolver;
import android.content.ContentValues; import android.content.ContentValues;
@ -29,13 +33,18 @@ import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.content.res.Resources; import android.content.res.Resources;
import android.database.Cursor; import android.database.Cursor;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.Matrix;
import android.graphics.PorterDuff.Mode; import android.graphics.PorterDuff.Mode;
import android.graphics.Rect; import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.Drawable;
import android.os.Bundle; import android.os.Bundle;
import android.support.v4.app.FragmentActivity; import android.support.v4.app.FragmentActivity;
import android.support.v4.app.LoaderManager.LoaderCallbacks; import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.CursorLoader; import android.support.v4.content.CursorLoader;
import android.support.v4.content.Loader; 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.util.Pair;
import android.support.v4.view.MenuItemCompat; import android.support.v4.view.MenuItemCompat;
import android.support.v7.internal.view.SupportMenuInflater; import android.support.v7.internal.view.SupportMenuInflater;
@ -52,6 +61,7 @@ import android.view.MenuItem;
import android.view.View; import android.view.View;
import android.view.View.OnClickListener; import android.view.View.OnClickListener;
import android.view.ViewGroup; import android.view.ViewGroup;
import android.view.animation.DecelerateInterpolator;
import android.widget.CompoundButton; import android.widget.CompoundButton;
import android.widget.CompoundButton.OnCheckedChangeListener; import android.widget.CompoundButton.OnCheckedChangeListener;
import android.widget.ImageView; import android.widget.ImageView;
@ -78,17 +88,19 @@ import org.mariotaku.twidere.adapter.ArrayAdapter;
import org.mariotaku.twidere.app.TwidereApplication; import org.mariotaku.twidere.app.TwidereApplication;
import org.mariotaku.twidere.menu.SupportAccountActionProvider; import org.mariotaku.twidere.menu.SupportAccountActionProvider;
import org.mariotaku.twidere.model.ParcelableAccount; import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableAccount.Indices;
import org.mariotaku.twidere.provider.TwidereDataStore.Accounts; import org.mariotaku.twidere.provider.TwidereDataStore.Accounts;
import org.mariotaku.twidere.util.CompareUtils; import org.mariotaku.twidere.util.CompareUtils;
import org.mariotaku.twidere.util.ImageLoaderWrapper; import org.mariotaku.twidere.util.ImageLoaderWrapper;
import org.mariotaku.twidere.util.ThemeUtils; import org.mariotaku.twidere.util.ThemeUtils;
import org.mariotaku.twidere.util.TransitionUtils;
import org.mariotaku.twidere.util.Utils; import org.mariotaku.twidere.util.Utils;
import org.mariotaku.twidere.util.content.SupportFragmentReloadCursorObserver; import org.mariotaku.twidere.util.content.SupportFragmentReloadCursorObserver;
import org.mariotaku.twidere.view.ShapedImageView; import org.mariotaku.twidere.view.ShapedImageView;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.HashSet; import java.util.HashSet;
import java.util.List;
import java.util.Set; import java.util.Set;
import static org.mariotaku.twidere.util.Utils.openUserFavorites; import static org.mariotaku.twidere.util.Utils.openUserFavorites;
@ -114,6 +126,7 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
private View mAccountSelectorView; private View mAccountSelectorView;
private RecyclerView mAccountsSelector; private RecyclerView mAccountsSelector;
private ImageView mAccountProfileBannerView; private ImageView mAccountProfileBannerView;
private ImageView mFloatingProfileImageSnapshotView;
private ShapedImageView mAccountProfileImageView; private ShapedImageView mAccountProfileImageView;
private TextView mAccountProfileNameView, mAccountProfileScreenNameView; private TextView mAccountProfileNameView, mAccountProfileScreenNameView;
private ActionMenuView mAccountsToggleMenu; private ActionMenuView mAccountsToggleMenu;
@ -122,6 +135,7 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
private Context mThemedContext; private Context mThemedContext;
private ImageLoaderWrapper mImageLoader; private ImageLoaderWrapper mImageLoader;
private SupportAccountActionProvider mAccountActionProvider; private SupportAccountActionProvider mAccountActionProvider;
private boolean mSwitchAccountAnimationPlaying;
@Override @Override
public void onActivityResult(final int requestCode, final int resultCode, final Intent data) { public void onActivityResult(final int requestCode, final int resultCode, final Intent data) {
@ -180,38 +194,30 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
final Menu menu = mAccountsToggleMenu.getMenu(); final Menu menu = mAccountsToggleMenu.getMenu();
mAccountActionProvider = (SupportAccountActionProvider) MenuItemCompat.getActionProvider(menu.findItem(MENU_SELECT_ACCOUNT)); mAccountActionProvider = (SupportAccountActionProvider) MenuItemCompat.getActionProvider(menu.findItem(MENU_SELECT_ACCOUNT));
mAccountActionProvider.setExclusive(false); mAccountActionProvider.setExclusive(false);
final ArrayList<ParcelableAccount> accounts = new ArrayList<>(); final ParcelableAccount[] accounts = ParcelableAccount.getAccounts(data);
final Set<Long> activatedIds = new HashSet<>(); final Set<Long> activatedIds = new HashSet<>();
if (data != null) { long defaultId = -1;
data.moveToFirst(); for (ParcelableAccount account : accounts) {
final Indices indices = new Indices(data); if (account.is_activated) {
long defaultId = -1; if (defaultId < 0) {
while (!data.isAfterLast()) { defaultId = account.account_id;
final ParcelableAccount account = new ParcelableAccount(data, indices);
accounts.add(account);
if (account.is_activated) {
if (defaultId < 0) {
defaultId = account.account_id;
}
activatedIds.add(account.account_id);
} }
data.moveToNext(); activatedIds.add(account.account_id);
}
if (mAccountsAdapter.getSelectedAccountId() <= 0) {
mAccountsAdapter.setSelectedAccountId(mPreferences.getLong(KEY_DEFAULT_ACCOUNT_ID, defaultId));
} }
} }
mAccountActionProvider.setAccounts(accounts.toArray(new ParcelableAccount[accounts.size()])); mAccountsAdapter.setAccounts(accounts);
if (mAccountsAdapter.getSelectedAccountId() <= 0) {
mAccountsAdapter.setSelectedAccountId(mPreferences.getLong(KEY_DEFAULT_ACCOUNT_ID, defaultId));
}
mAccountActionProvider.setAccounts(accounts);
mAccountActionProvider.setSelectedAccountIds(ArrayUtils.toPrimitive(activatedIds.toArray(new Long[activatedIds.size()]))); mAccountActionProvider.setSelectedAccountIds(ArrayUtils.toPrimitive(activatedIds.toArray(new Long[activatedIds.size()])));
mAccountsAdapter.changeCursor(data); updateAccountOptionsSeparatorLabel(null);
updateAccountOptionsSeparatorLabel();
updateDefaultAccountState(); updateDefaultAccountState();
} }
@Override @Override
public void onLoaderReset(final Loader<Cursor> loader) { public void onLoaderReset(final Loader<Cursor> loader) {
mAccountsAdapter.changeCursor(null);
} }
@Override @Override
@ -345,9 +351,11 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
layoutManager.setStackFromEnd(true); layoutManager.setStackFromEnd(true);
mAccountsSelector.setLayoutManager(layoutManager); mAccountsSelector.setLayoutManager(layoutManager);
mAccountsSelector.setAdapter(mAccountsAdapter); mAccountsSelector.setAdapter(mAccountsAdapter);
mAccountsSelector.setItemAnimator(null);
mAccountProfileContainer = mAccountSelectorView.findViewById(R.id.profile_container); mAccountProfileContainer = mAccountSelectorView.findViewById(R.id.profile_container);
mAccountProfileImageView = (ShapedImageView) mAccountSelectorView.findViewById(R.id.profile_image); mAccountProfileImageView = (ShapedImageView) mAccountSelectorView.findViewById(R.id.profile_image);
mAccountProfileBannerView = (ImageView) mAccountSelectorView.findViewById(R.id.account_profile_banner); 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); mAccountProfileNameView = (TextView) mAccountSelectorView.findViewById(R.id.name);
mAccountProfileScreenNameView = (TextView) mAccountSelectorView.findViewById(R.id.screen_name); mAccountProfileScreenNameView = (TextView) mAccountSelectorView.findViewById(R.id.screen_name);
mAccountsToggleMenu = (ActionMenuView) mAccountSelectorView.findViewById(R.id.toggle_menu); mAccountsToggleMenu = (ActionMenuView) mAccountSelectorView.findViewById(R.id.toggle_menu);
@ -412,19 +420,102 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
return mThemedContext = new ContextThemeWrapper(context, themeResource); return mThemedContext = new ContextThemeWrapper(context, themeResource);
} }
private void onAccountSelected(ParcelableAccount account) { private void onAccountSelected(AccountProfileImageViewHolder holder, final ParcelableAccount account) {
mAccountsAdapter.setSelectedAccountId(account.account_id); if (mSwitchAccountAnimationPlaying) return;
updateAccountOptionsSeparatorLabel(); final ImageView snapshotView = mFloatingProfileImageSnapshotView;
final ShapedImageView profileImageView = mAccountProfileImageView;
final ShapedImageView clickedImageView = holder.getIconView();
final Matrix matrix = new Matrix();
final Rect tempRect = new Rect();
clickedImageView.getGlobalVisibleRect(tempRect);
final RectF sourceBounds = new RectF(tempRect);
profileImageView.getGlobalVisibleRect(tempRect);
final RectF destBounds = new RectF(tempRect);
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();
snapshotView.setPivotX(0);
snapshotView.setPivotY(0);
snapshotView.setX(sourceBounds.left);
snapshotView.setY(sourceBounds.top);
set.play(ObjectAnimator.ofFloat(snapshotView, View.X, sourceBounds.left, destBounds.left))
.with(ObjectAnimator.ofFloat(snapshotView, View.Y, sourceBounds.top, destBounds.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();
mImageLoader.displayDashboardProfileImage(clickedImageView,
oldSelectedAccount.profile_image_url, profileDrawable);
// mImageLoader.displayDashboardProfileImage(profileImageView,
// account.profile_image_url, clickedDrawable);
clickedImageView.setBorderColors(profileImageView.getBorderColors());
mSwitchAccountAnimationPlaying = true;
}
@Override
public void onAnimationEnd(Animator animation) {
finishAnimation();
}
private void finishAnimation() {
mAccountsAdapter.setSelectedAccountId(account.account_id);
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;
}
@Override
public void onAnimationCancel(Animator animation) {
finishAnimation();
}
@Override
public void onAnimationRepeat(Animator animation) {
}
});
set.start();
} }
private void updateAccountOptionsSeparatorLabel() { private void updateAccountOptionsSeparatorLabel(Drawable profileImageSnapshot) {
final ParcelableAccount account = mAccountsAdapter.getSelectedAccount(); final ParcelableAccount account = mAccountsAdapter.getSelectedAccount();
if (account == null) { if (account == null) {
return; return;
} }
mAccountProfileNameView.setText(account.name); mAccountProfileNameView.setText(account.name);
mAccountProfileScreenNameView.setText("@" + account.screen_name); mAccountProfileScreenNameView.setText("@" + account.screen_name);
mImageLoader.displayProfileImage(mAccountProfileImageView, account.profile_image_url); mImageLoader.displayDashboardProfileImage(mAccountProfileImageView,
account.profile_image_url, profileImageSnapshot);
mAccountProfileImageView.setBorderColors(account.color); mAccountProfileImageView.setBorderColors(account.color);
final int bannerWidth = mAccountProfileBannerView.getWidth(); final int bannerWidth = mAccountProfileBannerView.getWidth();
final Resources res = getResources(); final Resources res = getResources();
@ -483,9 +574,13 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
icon = (ShapedImageView) itemView.findViewById(android.R.id.icon); icon = (ShapedImageView) itemView.findViewById(android.R.id.icon);
} }
public ShapedImageView getIconView() {
return icon;
}
@Override @Override
public void onClick(View v) { public void onClick(View v) {
adapter.dispatchItemSelected(getPosition()); adapter.dispatchItemSelected(this);
} }
} }
@ -494,62 +589,95 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
private final LayoutInflater mInflater; private final LayoutInflater mInflater;
private final ImageLoaderWrapper mImageLoader; private final ImageLoaderWrapper mImageLoader;
private final AccountsDashboardFragment mFragment; private final AccountsDashboardFragment mFragment;
private Cursor mCursor; private ParcelableAccount[] mAccounts;
private Indices mIndices; private ParcelableAccount[] mInternalAccounts;
private long mSelectedAccountId;
private int mSelectedAccountIndex;
AccountSelectorAdapter(Context context, AccountsDashboardFragment fragment) { AccountSelectorAdapter(Context context, AccountsDashboardFragment fragment) {
mInflater = LayoutInflater.from(context); mInflater = LayoutInflater.from(context);
mImageLoader = TwidereApplication.getInstance(context).getImageLoaderWrapper(); mImageLoader = TwidereApplication.getInstance(context).getImageLoaderWrapper();
mFragment = fragment; mFragment = fragment;
setHasStableIds(true);
} }
public void changeCursor(Cursor cursor) { private static int indexOfAccount(List<ParcelableAccount> accounts, long accountId) {
mCursor = cursor; for (int i = 0, j = accounts.size(); i < j; i++) {
if (cursor != null) { if (accounts.get(i).account_id == accountId) return i;
mIndices = new Indices(cursor); }
return -1;
}
public void setAccounts(ParcelableAccount[] accounts) {
mAccounts = accounts;
if (accounts != null) {
final ParcelableAccount[] previousAccounts = mInternalAccounts;
mInternalAccounts = new ParcelableAccount[accounts.length];
int tempIdx = 0;
final List<ParcelableAccount> tempList = Arrays.asList(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;
} }
updateSelectedAccountIndex();
notifyDataSetChanged(); notifyDataSetChanged();
} }
private void updateSelectedAccountIndex() {
final Cursor c = mCursor; public ParcelableAccount getAdapterAccount(int adapterPosition) {
final Indices i = mIndices; if (mInternalAccounts == null || mInternalAccounts.length < 1) {
mSelectedAccountIndex = -1; return null;
if (c != null && i != null && c.moveToFirst()) { }
while (!c.isAfterLast()) { return mInternalAccounts[adapterPosition + 1];
if (c.getLong(mIndices.account_id) == mSelectedAccountId) { }
mSelectedAccountIndex = c.getPosition();
break; private final LongSparseArray<Long> positionMap = new LongSparseArray<>();
}
c.moveToNext(); 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();
} }
public ParcelableAccount getSelectedAccount() { public ParcelableAccount getSelectedAccount() {
final Cursor c = mCursor; if (mInternalAccounts == null || mInternalAccounts.length < 0) {
final Indices i = mIndices; return null;
if (c != null && i != null && c.moveToFirst()) {
while (!c.isAfterLast()) {
if (c.getLong(mIndices.account_id) == mSelectedAccountId)
return new ParcelableAccount(c, mIndices);
c.moveToNext();
}
} }
return null; return mInternalAccounts[0];
} }
public long getSelectedAccountId() { public long getSelectedAccountId() {
return mSelectedAccountId; return getSelectedAccount().account_id;
} }
public void setSelectedAccountId(long accountId) { public void setSelectedAccountId(long accountId) {
mSelectedAccountId = accountId; final ParcelableAccount selectedAccount = getSelectedAccount();
updateSelectedAccountIndex(); if (selectedAccount == null) return;
notifyDataSetChanged(); swap(accountId, selectedAccount.account_id);
}
@Override
public long getItemId(int position) {
return getAdapterAccount(position).account_id;
} }
@Override @Override
@ -560,31 +688,22 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
@Override @Override
public void onBindViewHolder(AccountProfileImageViewHolder holder, int position) { public void onBindViewHolder(AccountProfileImageViewHolder holder, int position) {
final Cursor c = mCursor; // holder.itemView.setAlpha(c.getInt(mIndices.is_activated) == 1 ? 1 : 0.5f);
if (mSelectedAccountIndex != -1 && position >= mSelectedAccountIndex) { final ParcelableAccount account = getAdapterAccount(position);
c.moveToPosition(position + 1); mImageLoader.cancelDisplayTask(holder.icon);
} else { // holder.icon.setImageDrawable(null);
c.moveToPosition(position); mImageLoader.displayDashboardProfileImage(holder.icon, account.profile_image_url, null);
} holder.icon.setBorderColor(account.color);
holder.itemView.setAlpha(c.getInt(mIndices.is_activated) == 1 ? 1 : 0.5f);
mImageLoader.displayProfileImage(holder.icon, c.getString(mIndices.profile_image_url));
holder.icon.setBorderColor(c.getInt(mIndices.color));
} }
@Override @Override
public int getItemCount() { public int getItemCount() {
if (mCursor == null) return 0; if (mInternalAccounts == null || mInternalAccounts.length == 0) return 0;
return Math.max(mCursor.getCount() - 1, 0); return mInternalAccounts.length - 1;
} }
private void dispatchItemSelected(int position) { private void dispatchItemSelected(AccountProfileImageViewHolder holder) {
final Cursor c = mCursor; mFragment.onAccountSelected(holder, getAdapterAccount(holder.getAdapterPosition()));
if (mSelectedAccountIndex != -1 && position >= mSelectedAccountIndex) {
c.moveToPosition(position + 1);
} else {
c.moveToPosition(position);
}
mFragment.onAccountSelected(new ParcelableAccount(c, mIndices));
} }
} }
@ -643,7 +762,7 @@ public class AccountsDashboardFragment extends BaseSupportListFragment implement
final TextView text1 = (TextView) view.findViewById(android.R.id.text1); final TextView text1 = (TextView) view.findViewById(android.R.id.text1);
final ImageView icon = (ImageView) view.findViewById(android.R.id.icon); final ImageView icon = (ImageView) view.findViewById(android.R.id.icon);
text1.setText(option.name); text1.setText(option.name);
icon.setImageDrawable(icon.getResources().getDrawable(option.icon)); icon.setImageDrawable(ResourcesCompat.getDrawable(icon.getResources(), option.icon, null));
icon.setColorFilter(mActionIconColor, Mode.SRC_ATOP); icon.setColorFilter(mActionIconColor, Mode.SRC_ATOP);
return view; return view;
} }

View File

@ -20,9 +20,11 @@
package org.mariotaku.twidere.util; package org.mariotaku.twidere.util;
import android.graphics.Bitmap; import android.graphics.Bitmap;
import android.graphics.drawable.Drawable;
import android.widget.ImageView; import android.widget.ImageView;
import com.nostra13.universalimageloader.core.DisplayImageOptions; import com.nostra13.universalimageloader.core.DisplayImageOptions;
import com.nostra13.universalimageloader.core.DisplayImageOptions.Builder;
import com.nostra13.universalimageloader.core.ImageLoader; import com.nostra13.universalimageloader.core.ImageLoader;
import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer; import com.nostra13.universalimageloader.core.display.FadeInBitmapDisplayer;
import com.nostra13.universalimageloader.core.listener.ImageLoadingListener; import com.nostra13.universalimageloader.core.listener.ImageLoadingListener;
@ -38,6 +40,7 @@ public class ImageLoaderWrapper implements Constants {
private final ImageLoader mImageLoader; private final ImageLoader mImageLoader;
private final DisplayImageOptions mProfileImageDisplayOptions; private final DisplayImageOptions mProfileImageDisplayOptions;
private final DisplayImageOptions mDashboardProfileImageDisplayOptions;
private final DisplayImageOptions mOvalProfileImageDisplayOptions; private final DisplayImageOptions mOvalProfileImageDisplayOptions;
private final DisplayImageOptions mImageDisplayOptions, mBannerDisplayOptions; private final DisplayImageOptions mImageDisplayOptions, mBannerDisplayOptions;
@ -72,11 +75,17 @@ public class ImageLoaderWrapper implements Constants {
bannerOptsBuilder.cacheOnDisk(true); bannerOptsBuilder.cacheOnDisk(true);
bannerOptsBuilder.bitmapConfig(Bitmap.Config.RGB_565); bannerOptsBuilder.bitmapConfig(Bitmap.Config.RGB_565);
bannerOptsBuilder.displayer(new FadeInBitmapDisplayer(200, true, true, true)); bannerOptsBuilder.displayer(new FadeInBitmapDisplayer(200, true, true, true));
final DisplayImageOptions.Builder dashboardProfileOptsBuilder = new DisplayImageOptions.Builder();
// dashboardProfileOptsBuilder.showImageOnLoading(android.R.color.transparent);
dashboardProfileOptsBuilder.cacheInMemory(true);
dashboardProfileOptsBuilder.cacheOnDisk(true);
dashboardProfileOptsBuilder.bitmapConfig(Bitmap.Config.RGB_565);
mProfileImageDisplayOptions = profileOptsBuilder.build(); mProfileImageDisplayOptions = profileOptsBuilder.build();
mOvalProfileImageDisplayOptions = ovalProfileOptsBuilder.build(); mOvalProfileImageDisplayOptions = ovalProfileOptsBuilder.build();
mImageDisplayOptions = imageOptsBuilder.build(); mImageDisplayOptions = imageOptsBuilder.build();
mBannerDisplayOptions = bannerOptsBuilder.build(); mBannerDisplayOptions = bannerOptsBuilder.build();
mDashboardProfileImageDisplayOptions = dashboardProfileOptsBuilder.build();
} }
public void clearFileCache() { public void clearFileCache() {
@ -120,6 +129,18 @@ public class ImageLoaderWrapper implements Constants {
mImageLoader.displayImage(url, view, mProfileImageDisplayOptions); mImageLoader.displayImage(url, view, mProfileImageDisplayOptions);
} }
public void displayDashboardProfileImage(final ImageView view, final String url, Drawable drawableOnLoading) {
if (drawableOnLoading != null) {
final Builder builder = new Builder();
builder.cloneFrom(mDashboardProfileImageDisplayOptions);
builder.showImageOnLoading(drawableOnLoading);
builder.showImageOnFail(drawableOnLoading);
mImageLoader.displayImage(url, view, builder.build());
return;
}
mImageLoader.displayImage(url, view, mDashboardProfileImageDisplayOptions);
}
public void displayImage(final ImageView view, final String url, DisplayImageOptions options) { public void displayImage(final ImageView view, final String url, DisplayImageOptions options) {
mImageLoader.displayImage(url, view, options); mImageLoader.displayImage(url, view, options);

View File

@ -0,0 +1,135 @@
/*
* Twidere - Twitter client for Android
*
* Copyright (C) 2012-2015 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.util;
import android.animation.Animator;
import android.animation.AnimatorSet;
import android.animation.TypeEvaluator;
import android.graphics.Bitmap;
import android.graphics.Canvas;
import android.graphics.Matrix;
import android.graphics.Rect;
import android.graphics.RectF;
import android.graphics.drawable.BitmapDrawable;
import android.graphics.drawable.Drawable;
import android.view.View;
/**
* Static utility methods for Transitions.
*
* @hide
*/
public class TransitionUtils {
private static int MAX_IMAGE_SIZE = (1024 * 1024);
static Animator mergeAnimators(Animator animator1, Animator animator2) {
if (animator1 == null) {
return animator2;
} else if (animator2 == null) {
return animator1;
} else {
AnimatorSet animatorSet = new AnimatorSet();
animatorSet.playTogether(animator1, animator2);
return animatorSet;
}
}
/**
* Get a copy of bitmap of given drawable, return null if intrinsic size is zero
*/
public static Bitmap createDrawableBitmap(Drawable drawable) {
int width = drawable.getIntrinsicWidth();
int height = drawable.getIntrinsicHeight();
if (width <= 0 || height <= 0) {
return null;
}
float scale = Math.min(1f, ((float) MAX_IMAGE_SIZE) / (width * height));
if (drawable instanceof BitmapDrawable && scale == 1f) {
// return same bitmap if scale down not needed
return ((BitmapDrawable) drawable).getBitmap();
}
int bitmapWidth = (int) (width * scale);
int bitmapHeight = (int) (height * scale);
Bitmap bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
Rect existingBounds = drawable.getBounds();
int left = existingBounds.left;
int top = existingBounds.top;
int right = existingBounds.right;
int bottom = existingBounds.bottom;
drawable.setBounds(0, 0, bitmapWidth, bitmapHeight);
drawable.draw(canvas);
drawable.setBounds(left, top, right, bottom);
return bitmap;
}
/**
* Creates a Bitmap of the given view, using the Matrix matrix to transform to the local
* coordinates. <code>matrix</code> will be modified during the bitmap creation.
* <p/>
* <p>If the bitmap is large, it will be scaled uniformly down to at most 1MB size.</p>
*
* @param view The view to create a bitmap for.
* @param matrix The matrix converting the view local coordinates to the coordinates that
* the bitmap will be displayed in. <code>matrix</code> will be modified before
* returning.
* @param bounds The bounds of the bitmap in the destination coordinate system (where the
* view should be presented. Typically, this is matrix.mapRect(viewBounds);
* @return A bitmap of the given view or null if bounds has no width or height.
*/
public static Bitmap createViewBitmap(View view, Matrix matrix, RectF bounds) {
Bitmap bitmap = null;
int bitmapWidth = Math.round(bounds.width());
int bitmapHeight = Math.round(bounds.height());
if (bitmapWidth > 0 && bitmapHeight > 0) {
float scale = Math.min(1f, ((float) MAX_IMAGE_SIZE) / (bitmapWidth * bitmapHeight));
bitmapWidth *= scale;
bitmapHeight *= scale;
matrix.postTranslate(-bounds.left, -bounds.top);
matrix.postScale(scale, scale);
bitmap = Bitmap.createBitmap(bitmapWidth, bitmapHeight, Bitmap.Config.ARGB_8888);
Canvas canvas = new Canvas(bitmap);
canvas.concat(matrix);
view.draw(canvas);
}
return bitmap;
}
public static class MatrixEvaluator implements TypeEvaluator<Matrix> {
float[] mTempStartValues = new float[9];
float[] mTempEndValues = new float[9];
Matrix mTempMatrix = new Matrix();
@Override
public Matrix evaluate(float fraction, Matrix startValue, Matrix endValue) {
startValue.getValues(mTempStartValues);
endValue.getValues(mTempEndValues);
for (int i = 0; i < 9; i++) {
float diff = mTempEndValues[i] - mTempStartValues[i];
mTempEndValues[i] = mTempStartValues[i] + (fraction * diff);
}
mTempMatrix.setValues(mTempEndValues);
return mTempMatrix;
}
}
}

View File

@ -228,6 +228,10 @@ public class ShapedImageView extends ImageView {
} }
public int[] getBorderColors() {
return mBorderColors;
}
@ShapeStyle @ShapeStyle
public int getStyle() { public int getStyle() {
return mStyle; return mStyle;

View File

@ -23,7 +23,9 @@
xmlns:app="http://schemas.android.com/apk/res-auto" xmlns:app="http://schemas.android.com/apk/res-auto"
xmlns:tools="http://schemas.android.com/tools" xmlns:tools="http://schemas.android.com/tools"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content"> android:layout_height="wrap_content"
tools:layout_height="160dp"
tools:layout_width="280dp">
<ImageView <ImageView
android:id="@+id/account_profile_banner" android:id="@+id/account_profile_banner"
@ -33,7 +35,8 @@
android:layout_alignTop="@id/profile_container" android:layout_alignTop="@id/profile_container"
android:alpha="0.5" android:alpha="0.5"
android:contentDescription="@string/profile_banner" android:contentDescription="@string/profile_banner"
android:scaleType="centerCrop"/> android:scaleType="centerCrop"
tools:src="@drawable/nyan_stars_background"/>
<FrameLayout <FrameLayout
android:id="@+id/profile_container" android:id="@+id/profile_container"
@ -58,7 +61,7 @@
android:layout_marginTop="@dimen/element_spacing_mlarge" android:layout_marginTop="@dimen/element_spacing_mlarge"
app:sivBorder="true" app:sivBorder="true"
app:sivBorderWidth="2dp" app:sivBorderWidth="2dp"
tools:src="@drawable/profile_image_nyan_sakamoto"/> tools:src="@mipmap/ic_launcher"/>
<android.support.v7.widget.RecyclerView <android.support.v7.widget.RecyclerView
android:id="@+id/other_accounts_list" android:id="@+id/other_accounts_list"
@ -76,7 +79,8 @@
android:layout_alignParentBottom="true" android:layout_alignParentBottom="true"
android:layout_below="@id/profile_image" android:layout_below="@id/profile_image"
android:gravity="center_vertical" android:gravity="center_vertical"
android:orientation="horizontal"> android:orientation="horizontal"
android:baselineAligned="false">
<LinearLayout <LinearLayout
android:layout_width="0dp" android:layout_width="0dp"
@ -117,6 +121,14 @@
</LinearLayout> </LinearLayout>
</RelativeLayout> </RelativeLayout>
<ImageView
android:id="@+id/floating_profile_image_snapshot"
android:layout_width="wrap_content"
android:layout_height="wrap_content"
android:scaleType="fitCenter"
android:visibility="invisible"
tools:ignore="ContentDescription"/>
</FrameLayout> </FrameLayout>
</RelativeLayout> </RelativeLayout>