added some eye candies :)

This commit is contained in:
Mariotaku Lee 2016-02-19 00:21:04 +08:00
parent c05ba531c0
commit e645cbbba5
18 changed files with 1132 additions and 54 deletions

View File

@ -32,6 +32,7 @@ import com.commonsware.cwac.layouts.AspectLockedFrameLayout;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter;
import org.mariotaku.twidere.graphic.LikeAnimationDrawable;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.model.util.ParcelableMediaUtils;
@ -91,11 +92,7 @@ public class StaggeredGridParcelableStatusesAdapter extends AbsParcelableStatuse
final ParcelableMedia[] media = status.media;
if (media == null || media.length < 1) return;
final ParcelableMedia firstMedia = media[0];
if (status.text_plain.codePointCount(0, status.text_plain.length()) == firstMedia.end) {
mediaTextView.setText(status.text_unescaped.substring(0, firstMedia.start));
} else {
mediaTextView.setText(status.text_unescaped);
}
mediaTextView.setText(status.text_unescaped);
aspectRatioSource.setSize(firstMedia.width, firstMedia.height);
mediaImageContainer.setTag(firstMedia);
mediaImageContainer.requestLayout();
@ -153,6 +150,11 @@ public class StaggeredGridParcelableStatusesAdapter extends AbsParcelableStatuse
}
@Override
public void playLikeAnimation(LikeAnimationDrawable.OnLikedListener listener) {
}
public void setOnClickListeners() {
setStatusClickListener(adapter.getStatusClickListener());
}

View File

@ -47,6 +47,7 @@ import org.mariotaku.twidere.adapter.AbsActivitiesAdapter;
import org.mariotaku.twidere.adapter.decorator.DividerItemDecoration;
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition;
import org.mariotaku.twidere.annotation.ReadPositionTag;
import org.mariotaku.twidere.fragment.support.AbsStatusesFragment.DefaultOnLikedListener;
import org.mariotaku.twidere.loader.iface.IExtendedLoader;
import org.mariotaku.twidere.model.ParcelableActivity;
import org.mariotaku.twidere.model.ParcelableMedia;
@ -155,11 +156,11 @@ public abstract class AbsActivitiesFragment<Data> extends AbsContentListRecycler
if (recyclerView == null || layoutManager == null) return false;
final View focusedChild = RecyclerViewUtils.findRecyclerViewChild(recyclerView,
layoutManager.getFocusedChild());
int position = -1;
int position = RecyclerView.NO_POSITION;
if (focusedChild != null && focusedChild.getParent() == recyclerView) {
position = recyclerView.getChildLayoutPosition(focusedChild);
}
if (position != -1) {
if (position != RecyclerView.NO_POSITION) {
final ParcelableActivity activity = getAdapter().getActivity(position);
if (activity == null) return false;
if (keyCode == KeyEvent.KEYCODE_ENTER) {
@ -188,7 +189,9 @@ public abstract class AbsActivitiesFragment<Data> extends AbsContentListRecycler
if (status.is_favorite) {
twitter.destroyFavoriteAsync(activity.account_id, status.id);
} else {
twitter.createFavoriteAsync(activity.account_id, status.id);
final IStatusViewHolder holder = (IStatusViewHolder)
recyclerView.findViewHolderForLayoutPosition(position);
holder.playLikeAnimation(new DefaultOnLikedListener(twitter, status));
}
return true;
}
@ -362,7 +365,7 @@ public abstract class AbsActivitiesFragment<Data> extends AbsContentListRecycler
if (status.is_favorite) {
twitter.destroyFavoriteAsync(status.account_id, status.id);
} else {
twitter.createFavoriteAsync(status.account_id, status.id);
holder.playLikeAnimation(new DefaultOnLikedListener(twitter, status));
}
break;
}

View File

@ -47,6 +47,7 @@ import org.mariotaku.twidere.adapter.AbsStatusesAdapter;
import org.mariotaku.twidere.adapter.iface.ILoadMoreSupportAdapter.IndicatorPosition;
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter.StatusAdapterListener;
import org.mariotaku.twidere.annotation.ReadPositionTag;
import org.mariotaku.twidere.graphic.LikeAnimationDrawable;
import org.mariotaku.twidere.loader.iface.IExtendedLoader;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableStatus;
@ -184,7 +185,9 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentListRecyclerVi
if (status.is_favorite) {
twitter.destroyFavoriteAsync(status.account_id, status.id);
} else {
twitter.createFavoriteAsync(status.account_id, status.id);
final IStatusViewHolder holder = (IStatusViewHolder)
recyclerView.findViewHolderForLayoutPosition(position);
holder.playLikeAnimation(new DefaultOnLikedListener(twitter, status));
}
return true;
}
@ -351,7 +354,7 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentListRecyclerVi
if (status.is_favorite) {
twitter.destroyFavoriteAsync(status.account_id, status.id);
} else {
twitter.createFavoriteAsync(status.account_id, status.id);
holder.playLikeAnimation(new DefaultOnLikedListener(twitter, status));
}
break;
}
@ -547,6 +550,23 @@ public abstract class AbsStatusesFragment<Data> extends AbsContentListRecyclerVi
return Utils.getReadPositionTagWithAccounts(getReadPositionTag(), getAccountIds());
}
public static final class DefaultOnLikedListener implements LikeAnimationDrawable.OnLikedListener {
private final ParcelableStatus mStatus;
private final AsyncTwitterWrapper mTwitter;
public DefaultOnLikedListener(final AsyncTwitterWrapper twitter, final ParcelableStatus status) {
mStatus = status;
mTwitter = twitter;
}
@Override
public boolean onLiked() {
if (mStatus.is_favorite) return false;
mTwitter.createFavoriteAsync(mStatus.account_id, mStatus.id);
return true;
}
}
protected final class StatusesBusCallback {
protected StatusesBusCallback() {

View File

@ -47,7 +47,10 @@ import android.support.v4.app.FragmentManagerAccessor;
import android.support.v4.app.FragmentTransaction;
import android.support.v4.app.LoaderManager.LoaderCallbacks;
import android.support.v4.content.AsyncTaskLoader;
import android.support.v4.content.ContextCompat;
import android.support.v4.content.Loader;
import android.support.v4.view.ActionProvider;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.ActionMenuView;
import android.support.v7.widget.CardView;
import android.support.v7.widget.FixedLinearLayoutManager;
@ -97,6 +100,7 @@ import org.mariotaku.twidere.api.twitter.model.TranslationResult;
import org.mariotaku.twidere.constant.IntentConstants;
import org.mariotaku.twidere.loader.support.ConversationLoader;
import org.mariotaku.twidere.loader.support.ParcelableStatusLoader;
import org.mariotaku.twidere.menu.support.FavoriteItemProvider;
import org.mariotaku.twidere.model.ParcelableActivity;
import org.mariotaku.twidere.model.ParcelableActivityCursorIndices;
import org.mariotaku.twidere.model.ParcelableActivityValuesCreator;
@ -1231,7 +1235,22 @@ public class StatusFragment extends BaseSupportFragment implements LoaderCallbac
final StatusFragment fragment = adapter.getFragment();
final FragmentActivity activity = fragment.getActivity();
final MenuInflater inflater = activity.getMenuInflater();
inflater.inflate(R.menu.menu_detail_status, menuBar.getMenu());
final Menu menu = menuBar.getMenu();
inflater.inflate(R.menu.menu_detail_status, menu);
final MenuItem favoriteItem = menu.findItem(R.id.favorite);
final ActionProvider provider = MenuItemCompat.getActionProvider(favoriteItem);
if (provider instanceof FavoriteItemProvider) {
final int defaultColor = ThemeUtils.getActionIconColor(activity);
final FavoriteItemProvider itemProvider = (FavoriteItemProvider) provider;
itemProvider.setDefaultColor(defaultColor);
final int favoriteHighlight = ContextCompat.getColor(activity, R.color.highlight_favorite);
final int likeHighlight = ContextCompat.getColor(activity, R.color.highlight_like);
final boolean useStar = adapter.shouldUseStarsForLikes();
itemProvider.setActivatedColor(useStar ? favoriteHighlight : likeHighlight);
itemProvider.setIcon(useStar ? R.drawable.ic_action_star : R.drawable.ic_action_heart);
itemProvider.setUseStar(useStar);
itemProvider.init(menuBar, favoriteItem);
}
ThemeUtils.wrapMenuIcon(menuBar, MENU_GROUP_STATUS_SHARE);
mediaPreviewLoad.setOnClickListener(this);
profileContainer.setOnClickListener(this);

View File

@ -0,0 +1,804 @@
package org.mariotaku.twidere.graphic;
import android.animation.Animator;
import android.animation.AnimatorListenerAdapter;
import android.animation.AnimatorSet;
import android.animation.ArgbEvaluator;
import android.animation.ObjectAnimator;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Color;
import android.graphics.ColorFilter;
import android.graphics.Paint;
import android.graphics.PixelFormat;
import android.graphics.PorterDuff;
import android.graphics.Rect;
import android.graphics.drawable.Animatable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.LayerDrawable;
import android.support.annotation.IntDef;
import android.support.v4.content.ContextCompat;
import android.util.Property;
import android.view.Gravity;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.DecelerateInterpolator;
import org.mariotaku.twidere.graphic.iface.DoNotWrapDrawable;
import java.lang.ref.WeakReference;
/**
* Created by mariotaku on 15/11/4.
*/
public class LikeAnimationDrawable extends LayerDrawable implements Animatable, DoNotWrapDrawable {
private static final Property<IconLayer, Float> ICON_SCALE = new Property<IconLayer, Float>(Float.class, "icon_scale") {
@Override
public void set(IconLayer object, Float value) {
object.setScale(value);
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public Float get(IconLayer object) {
return object.getScale();
}
};
private static final Property<Layer, Float> LAYER_PROGRESS = new Property<Layer, Float>(Float.class, "layer_progress") {
@Override
public void set(Layer object, Float value) {
object.setProgress(value);
}
@Override
public boolean isReadOnly() {
return false;
}
@Override
public Float get(Layer object) {
return object.getProgress();
}
};
private final int mDefaultColor, mLikeColor;
@Style
private final int mStyle;
private long mDuration = 500;
private AnimatorSet mCurrentAnimator;
private WeakReference<OnLikedListener> mListenerRef;
public LikeAnimationDrawable(final Context context, final int likeIcon, final int defaultColor,
final int likeColor, @Style final int style) {
super(createLayers(context, likeIcon, defaultColor, style));
mDefaultColor = defaultColor;
mLikeColor = likeColor;
mStyle = style;
}
@Override
public void start() {
if (mCurrentAnimator != null) return;
final AnimatorSet animatorSet = new AnimatorSet();
final AbsLayer particleLayer = getParticleShineLayer();
final AbsLayer circleLayer = getCircleLayer();
final IconLayer iconLayer = getIconLayer();
switch (mStyle) {
case Style.LIKE: {
setupLikeAnimation(animatorSet, particleLayer, circleLayer, iconLayer);
break;
}
case Style.FAVORITE: {
setupFavoriteAnimation(animatorSet, particleLayer, circleLayer, iconLayer);
break;
}
}
animatorSet.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
resetState();
}
@Override
public void onAnimationCancel(Animator animation) {
mCurrentAnimator = null;
}
@Override
public void onAnimationEnd(Animator animation) {
mCurrentAnimator = null;
if (mListenerRef == null) return;
final OnLikedListener listener = mListenerRef.get();
if (listener == null) return;
if (!listener.onLiked()) {
resetState();
}
}
private void resetState() {
iconLayer.setColorFilter(mDefaultColor, PorterDuff.Mode.SRC_ATOP);
particleLayer.setProgress(-1);
}
});
animatorSet.start();
mCurrentAnimator = animatorSet;
}
private void setupFavoriteAnimation(final AnimatorSet animatorSet, final Layer particleLayer,
final Layer circleLayer, final IconLayer iconLayer) {
setupLikeAnimation(animatorSet, particleLayer, circleLayer, iconLayer);
}
private void setupLikeAnimation(final AnimatorSet animatorSet, final Layer particleLayer,
final Layer circleLayer, final IconLayer iconLayer) {
final long scaleDownDuration = Math.round(1f / 24f * mDuration);
final long ovalExpandDuration = Math.round(4f / 24f * mDuration);
final long iconExpandOffset = Math.round(6f / 24f * mDuration);
final long iconExpandDuration = Math.round(8f / 24f * mDuration);
final long iconNormalDuration = Math.round(4f / 24f * mDuration);
final long particleExpandDuration = Math.round(12f / 24f * mDuration);
final long circleExplodeDuration = Math.round(5f / 24f * mDuration);
final ObjectAnimator iconScaleDown = ObjectAnimator.ofFloat(iconLayer, ICON_SCALE, 1, 0);
iconScaleDown.setDuration(scaleDownDuration);
iconScaleDown.setInterpolator(new AccelerateInterpolator(2));
iconScaleDown.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
iconLayer.setColorFilter(mDefaultColor, PorterDuff.Mode.SRC_ATOP);
}
});
final ObjectAnimator ovalExpand = ObjectAnimator.ofFloat(circleLayer, LAYER_PROGRESS, 0, 0.5f);
ovalExpand.setDuration(ovalExpandDuration);
final ObjectAnimator iconExpand = ObjectAnimator.ofFloat(iconLayer, ICON_SCALE, 0, 1.25f);
iconExpand.setDuration(iconExpandDuration);
iconExpand.addListener(new AnimatorListenerAdapter() {
@Override
public void onAnimationStart(Animator animation) {
iconLayer.setColorFilter(mLikeColor, PorterDuff.Mode.SRC_ATOP);
}
});
final ObjectAnimator particleExplode = ObjectAnimator.ofFloat(particleLayer, LAYER_PROGRESS, 0, 0.5f);
particleExplode.setDuration(iconExpandDuration);
final ObjectAnimator iconNormal = ObjectAnimator.ofFloat(iconLayer, ICON_SCALE, 1.25f, 1);
iconNormal.setDuration(iconNormalDuration);
final ObjectAnimator circleExplode = ObjectAnimator.ofFloat(circleLayer, LAYER_PROGRESS, 0.5f, 0.95f, 0.95f, 1);
circleExplode.setDuration(circleExplodeDuration);
circleExplode.setInterpolator(new DecelerateInterpolator());
final ObjectAnimator particleFade = ObjectAnimator.ofFloat(particleLayer, LAYER_PROGRESS, 0.5f, 1);
particleFade.setDuration(particleExpandDuration);
animatorSet.play(iconScaleDown);
animatorSet.play(ovalExpand).after(iconScaleDown);
animatorSet.play(iconExpand).after(iconExpandOffset);
animatorSet.play(particleExplode).after(iconExpandOffset);
animatorSet.play(circleExplode).after(iconExpandOffset);
animatorSet.play(iconNormal).after(iconExpand);
animatorSet.play(particleFade).after(iconExpand);
}
private IconLayer getIconLayer() {
return (IconLayer) getDrawable(2);
}
private AbsLayer getCircleLayer() {
return (AbsLayer) getDrawable(0);
}
private AbsLayer getParticleShineLayer() {
return (AbsLayer) getDrawable(1);
}
@Override
public void stop() {
if (mCurrentAnimator == null) return;
mCurrentAnimator.cancel();
}
@Override
public boolean isRunning() {
return mCurrentAnimator != null && mCurrentAnimator.isRunning();
}
public long getDuration() {
return mDuration;
}
public void setDuration(long duration) {
mDuration = duration;
}
public void setOnLikedListener(OnLikedListener listener) {
mListenerRef = new WeakReference<>(listener);
}
@Override
public void setColorFilter(ColorFilter colorFilter) {
getIconLayer().setColorFilter(colorFilter);
}
private static Drawable[] createLayers(final Context context, final int likeIcon, int defaultColor, int style) {
final IconLayer iconDrawable = new IconLayer(ContextCompat.getDrawable(context, likeIcon));
iconDrawable.setColorFilter(defaultColor, PorterDuff.Mode.SRC_ATOP);
final AbsLayer particleLayer;
final Palette palette;
switch (style) {
case Style.FAVORITE: {
palette = new FavoritePalette();
particleLayer = new ShineLayer(iconDrawable.getIntrinsicWidth(),
iconDrawable.getIntrinsicHeight(), palette);
break;
}
case Style.LIKE: {
palette = new LikePalette();
particleLayer = new ParticleLayer(iconDrawable.getIntrinsicWidth(),
iconDrawable.getIntrinsicHeight(), palette);
break;
}
default: {
throw new IllegalArgumentException();
}
}
particleLayer.setProgress(-1);
final Drawable circleLayer = new CircleLayer(iconDrawable.getIntrinsicWidth(), iconDrawable.getIntrinsicHeight(), palette);
return new Drawable[]{circleLayer, particleLayer, iconDrawable};
}
private interface Layer {
float getProgress();
void setProgress(float progress);
}
public interface OnLikedListener {
boolean onLiked();
}
public interface Palette {
int getParticleColor(int count, int index, float progress);
int getCircleColor(float progress);
}
@IntDef({Style.LIKE, Style.FAVORITE})
public @interface Style {
int LIKE = 1;
int FAVORITE = 2;
}
private static class ShineLayer extends AbsLayer {
private static final int PARTICLES_PIVOTS_COUNT = 5;
private final Paint mPaint;
private int mFullRadius;
private float mLineWidth;
public ShineLayer(final int intrinsicWidth, final int intrinsicHeight, final Palette palette) {
super(intrinsicWidth, intrinsicHeight, palette);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeCap(Paint.Cap.ROUND);
setProgress(-1);
}
@Override
protected ConstantState createConstantState(final int intrinsicWidth,
final int intrinsicHeight,
final Palette palette) {
return new AbsLayerState() {
@Override
public Drawable newDrawable() {
return new ShineLayer(intrinsicWidth, intrinsicHeight, palette);
}
};
}
private void calculateLineStartEnd(float[] startEnd, float progress) {
}
@Override
public void draw(Canvas canvas) {
final float progress = getProgress();
if (progress < 0) return;
final int particleColor = palette.getParticleColor(0, 0, progress);
final Rect bounds = getBounds();
mPaint.setColor(particleColor);
mPaint.setStrokeWidth(mLineWidth);
final float[] startEnd = new float[2];
mPaint.setAlpha(0xFF);
if (progress < 0.25f) {
calcPhase1(startEnd, progress);
} else if (progress < 0.5f) {
calcPhase2(startEnd, progress);
} else if (progress < 0.75f) {
calcPhase3(startEnd, progress);
} else {
calcPhase4(startEnd, progress);
mPaint.setAlpha(Math.round(0xFF * (1 - (progress - 0.75f) * 4)));
}
for (int i = 0; i < PARTICLES_PIVOTS_COUNT; i++) {
final double degree = 360.0 / PARTICLES_PIVOTS_COUNT * i;
final double mainParticleAngle = Math.toRadians(degree + 18);
final float startX = (float) (bounds.centerX() + startEnd[0] * Math.cos(mainParticleAngle));
final float startY = (float) (bounds.centerY() + startEnd[0] * Math.sin(mainParticleAngle));
final float stopX = (float) (bounds.centerX() + startEnd[1] * Math.cos(mainParticleAngle));
final float stopY = (float) (bounds.centerY() + startEnd[1] * Math.sin(mainParticleAngle));
if (startEnd[1] - startEnd[0] <= 0) {
canvas.drawPoint(startX, startY, mPaint);
} else {
canvas.drawLine(startX, startY, stopX, stopY, mPaint);
}
}
}
private void calcPhase4(float[] startEnd, float progress) {
calcPhase3(startEnd, 0.75f);
}
private void calcPhase3(float[] startEnd, float progress) {
calcPhase2(startEnd, 0.5f);
final float length = (startEnd[1] - startEnd[0]) * (1 - (progress - 0.5f) * 4);
startEnd[0] = startEnd[1] - length;
}
private void calcPhase2(float[] startEnd, float progress) {
calcPhase1(startEnd, 0.25f);
final float length = startEnd[1] - startEnd[0];
final float initialStart = startEnd[0];
startEnd[0] = initialStart + mFullRadius / 3 * (progress - 0.25f) * 4;
startEnd[1] = startEnd[0] + length;
}
private void calcPhase1(float[] startEnd, float progress) {
// Start point: 1/4 of icon radius
startEnd[0] = mFullRadius / 3;
startEnd[1] = startEnd[0] + (mFullRadius / 4 * progress * 4);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mFullRadius = Math.min(bounds.width(), bounds.height()) / 2;
mLineWidth = mFullRadius / 10f;
}
}
private static class ParticleLayer extends AbsLayer {
private static final int PARTICLES_PIVOTS_COUNT = 7;
private final Paint mPaint;
private float mFullRadius;
private float mParticleSize;
public ParticleLayer(final int intrinsicWidth, final int intrinsicHeight,
final Palette palette) {
super(intrinsicWidth, intrinsicHeight, palette);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
mPaint.setStrokeCap(Paint.Cap.ROUND);
setProgress(-1);
}
@Override
protected ConstantState createConstantState(final int intrinsicWidth,
final int intrinsicHeight,
final Palette palette) {
return new AbsLayerState() {
@Override
public Drawable newDrawable() {
return new ParticleLayer(intrinsicWidth, intrinsicHeight, palette);
}
};
}
@Override
public void draw(final Canvas canvas) {
final float progress = getProgress();
if (progress < 0) return;
final Rect bounds = getBounds();
final float expandSpinProgress = Math.min(0.5f, progress);
final float currentRadius = mFullRadius + (mFullRadius * expandSpinProgress);
final float distance = mParticleSize + (mParticleSize * progress);
final float mainStrokeWidth, subStrokeWidth;
if (progress < 0.5) {
// Scale factor: [1, 0.5)
mainStrokeWidth = mParticleSize * (1 - progress);
// Scale factor: [1, 1.25)
subStrokeWidth = mParticleSize * (1 + progress / 2);
} else {
mainStrokeWidth = mParticleSize * (1 - progress);
subStrokeWidth = mParticleSize * 1.25f * (1 - (progress - 0.5f) * 2);
}
for (int i = 0; i < PARTICLES_PIVOTS_COUNT; i++) {
final double degree = 360.0 / PARTICLES_PIVOTS_COUNT * i;
final int color = palette.getParticleColor(PARTICLES_PIVOTS_COUNT, i, progress);
final double mainParticleAngle = Math.toRadians(degree - 115);
final float mainParticleX = (float) (bounds.centerX() + currentRadius * Math.cos(mainParticleAngle));
final float mainParticleY = (float) (bounds.centerY() + currentRadius * Math.sin(mainParticleAngle));
mPaint.setColor(color);
mPaint.setStrokeWidth(mainStrokeWidth);
if (mainStrokeWidth > 0) {
canvas.drawPoint(mainParticleX, mainParticleY, mPaint);
}
final double particleAngle = Math.toRadians(90.0 * -expandSpinProgress + degree + 15);
final float subParticleX = (float) (mainParticleX + distance * Math.cos(particleAngle));
final float subParticleY = (float) (mainParticleY + distance * Math.sin(particleAngle));
mPaint.setAlpha(Math.round(255f * (1 - progress / 2f)));
mPaint.setStrokeWidth(subStrokeWidth);
if (subStrokeWidth > 0) {
canvas.drawPoint(subParticleX, subParticleY, mPaint);
}
}
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mFullRadius = Math.min(bounds.width(), bounds.height()) / 2;
mParticleSize = mFullRadius / 4f;
}
}
private static class CircleLayer extends AbsLayer {
private final Paint mPaint;
private int mFullRadius;
public CircleLayer(final int intrinsicWidth, final int intrinsicHeight,
final Palette palette) {
super(intrinsicWidth, intrinsicHeight, palette);
mPaint = new Paint(Paint.ANTI_ALIAS_FLAG);
}
@Override
protected ConstantState createConstantState(final int intrinsicWidth,
final int intrinsicHeight,
final Palette palette) {
return new AbsLayerState() {
@Override
public Drawable newDrawable() {
return new CircleLayer(intrinsicWidth, intrinsicHeight, palette);
}
};
}
@Override
public void draw(final Canvas canvas) {
final float progress = getProgress();
final Rect bounds = getBounds();
final float radius;
if (progress < 0.5f) {
mPaint.setStyle(Paint.Style.FILL);
final float sizeProgress = Math.min(1, progress * 2);
radius = sizeProgress * mFullRadius;
} else {
mPaint.setStyle(Paint.Style.STROKE);
final float innerLeftRatio = 1 - (progress - 0.5f) * 2f;
final float strokeWidth = mFullRadius * innerLeftRatio;
mPaint.setStrokeWidth(strokeWidth);
radius = mFullRadius - strokeWidth / 2;
if (strokeWidth <= 0) return;
}
mPaint.setColor(palette.getCircleColor(progress));
canvas.drawCircle(bounds.centerX(), bounds.centerY(), radius, mPaint);
}
@Override
protected void onBoundsChange(Rect bounds) {
super.onBoundsChange(bounds);
mFullRadius = Math.min(bounds.width(), bounds.height()) / 2;
}
}
private static abstract class AbsLayer extends Drawable implements Layer {
protected final int intrinsicWidth;
protected final int intrinsicHeight;
protected final Palette palette;
private float mProgress;
private ConstantState mState;
public AbsLayer(final int intrinsicWidth, final int intrinsicHeight, final Palette palette) {
this.intrinsicWidth = intrinsicWidth;
this.intrinsicHeight = intrinsicHeight;
this.palette = palette;
mState = createConstantState(intrinsicWidth, intrinsicHeight, palette);
}
protected abstract ConstantState createConstantState(int intrinsicWidth, int intrinsicHeight, final Palette palette);
@Override
public void setAlpha(final int alpha) {
}
@Override
public final float getProgress() {
return mProgress;
}
@Override
public final void setProgress(float progress) {
mProgress = progress;
invalidateSelf();
}
@Override
public void setColorFilter(final ColorFilter colorFilter) {
}
@Override
public final int getIntrinsicHeight() {
return intrinsicHeight;
}
@Override
public final int getIntrinsicWidth() {
return intrinsicWidth;
}
@Override
public int getOpacity() {
return PixelFormat.TRANSLUCENT;
}
@Override
public ConstantState getConstantState() {
return mState;
}
static abstract class AbsLayerState extends ConstantState {
@Override
public int getChangingConfigurations() {
return 0;
}
}
}
private static class FavoritePalette implements Palette {
private final ArgbEvaluator evaluator = new ArgbEvaluator();
@Override
public int getParticleColor(int count, int index, float progress) {
return (Integer) evaluator.evaluate(progress, 0xFFFF7020, 0xFFFD9050);
}
@Override
public int getCircleColor(float progress) {
return (Integer) evaluator.evaluate(progress, 0xFFFF9C00, 0xFFFFB024);
}
}
private static class LikePalette implements Palette {
private final ArgbEvaluator evaluator = new ArgbEvaluator();
private final float[] hsv = new float[3];
@Override
public int getParticleColor(int count, int index, float progress) {
final double degree = 360.0 / count * index;
hsv[0] = (float) degree;
hsv[1] = 0.4f;
hsv[2] = 1f;
return Color.HSVToColor(hsv);
}
@Override
public int getCircleColor(float progress) {
return (Integer) evaluator.evaluate(progress, 0xFFDE4689, 0xFFCD8FF5);
}
}
static class IconLayer extends Drawable implements Callback {
private final Drawable mDrawable;
private final Rect mTmpRect = new Rect();
private float mScale;
private boolean mMutated;
private ConstantState mState;
public IconLayer(Drawable drawable) {
if (drawable == null) throw new NullPointerException();
mState = new ScaleConstantState(drawable);
mDrawable = drawable;
drawable.setCallback(this);
setScale(1);
}
/**
* Returns the drawable scaled by this ScaleDrawable.
*/
public Drawable getDrawable() {
return mDrawable;
}
// overrides from Drawable.Callback
@Override
public void invalidateDrawable(Drawable who) {
if (getCallback() != null) {
getCallback().invalidateDrawable(this);
}
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
if (getCallback() != null) {
getCallback().scheduleDrawable(this, what, when);
}
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
if (getCallback() != null) {
getCallback().unscheduleDrawable(this, what);
}
}
// overrides from Drawable
@Override
public void draw(Canvas canvas) {
if (mScale <= 0) return;
mDrawable.draw(canvas);
}
@Override
public int getChangingConfigurations() {
return super.getChangingConfigurations()
| mDrawable.getChangingConfigurations();
}
@Override
public boolean getPadding(Rect padding) {
// XXX need to adjust padding!
return mDrawable.getPadding(padding);
}
@Override
public boolean setVisible(boolean visible, boolean restart) {
mDrawable.setVisible(visible, restart);
return super.setVisible(visible, restart);
}
@Override
public void setAlpha(int alpha) {
mDrawable.setAlpha(alpha);
}
@Override
public void setColorFilter(ColorFilter cf) {
mDrawable.setColorFilter(cf);
}
@Override
public int getOpacity() {
return mDrawable.getOpacity();
}
@Override
public boolean isStateful() {
return mDrawable.isStateful();
}
@Override
protected boolean onStateChange(int[] state) {
boolean changed = mDrawable.setState(state);
onBoundsChange(getBounds());
return changed;
}
@Override
protected boolean onLevelChange(int level) {
mDrawable.setLevel(level);
onBoundsChange(getBounds());
invalidateSelf();
return true;
}
@Override
protected void onBoundsChange(Rect bounds) {
updateBounds(bounds);
}
@Override
public int getIntrinsicWidth() {
return mDrawable.getIntrinsicWidth();
}
@Override
public int getIntrinsicHeight() {
return mDrawable.getIntrinsicHeight();
}
@Override
public Drawable mutate() {
if (!mMutated && super.mutate() == this) {
mDrawable.mutate();
mMutated = true;
}
return this;
}
public float getScale() {
return mScale;
}
public void setScale(float scale) {
mScale = scale;
updateBounds(getBounds());
}
@Override
public ConstantState getConstantState() {
return mState;
}
static class ScaleConstantState extends ConstantState {
private final Drawable mIcon;
public ScaleConstantState(Drawable icon) {
mIcon = icon;
}
@Override
public Drawable newDrawable() {
return new IconLayer(mIcon.mutate());
}
@Override
public int getChangingConfigurations() {
return mIcon.getChangingConfigurations();
}
}
private void updateBounds(Rect bounds) {
final Rect r = mTmpRect;
final int w = Math.round(mDrawable.getIntrinsicWidth() * mScale);
final int h = Math.round(mDrawable.getIntrinsicHeight() * mScale);
Gravity.apply(Gravity.CENTER, w, h, bounds, r);
if (w > 0 && h > 0) {
mDrawable.setBounds(r.left, r.top, r.right, r.bottom);
}
invalidateSelf();
}
}
}

View File

@ -0,0 +1,7 @@
package org.mariotaku.twidere.graphic.iface;
/**
* Created by mariotaku on 16/2/18.
*/
public interface DoNotWrapDrawable {
}

View File

@ -0,0 +1,99 @@
package org.mariotaku.twidere.menu.support;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.v4.view.ActionProvider;
import android.support.v4.view.MenuItemCompat;
import android.support.v7.widget.ActionMenuView;
import android.view.MenuItem;
import android.view.View;
import org.mariotaku.twidere.graphic.LikeAnimationDrawable;
import org.mariotaku.twidere.graphic.LikeAnimationDrawable.Style;
import java.lang.ref.WeakReference;
/**
* Created by mariotaku on 16/2/18.
*/
public class FavoriteItemProvider extends ActionProvider {
private int mDefaultColor, mActivatedColor;
private boolean mUseStar;
private int mIcon;
/**
* Creates a new instance.
*
* @param context Context for accessing resources.
*/
public FavoriteItemProvider(Context context) {
super(context);
}
@Override
public View onCreateActionView() {
return null;
}
public void setUseStar(boolean useStar) {
mUseStar = useStar;
}
public void setDefaultColor(int defaultColor) {
mDefaultColor = defaultColor;
}
public void setActivatedColor(int activatedColor) {
mActivatedColor = activatedColor;
}
public void invokeItem(MenuItem item, LikeAnimationDrawable.OnLikedListener listener) {
if (MenuItemCompat.getActionProvider(item) != this) throw new IllegalArgumentException();
final Drawable icon = item.getIcon();
if (icon instanceof LikeAnimationDrawable) {
((LikeAnimationDrawable) icon).setOnLikedListener(listener);
((LikeAnimationDrawable) icon).start();
}
}
public void setIcon(int icon) {
mIcon = icon;
}
public void init(final ActionMenuView menuBar, MenuItem item) {
if (MenuItemCompat.getActionProvider(item) != this) throw new IllegalArgumentException();
final LikeAnimationDrawable drawable = new LikeAnimationDrawable(getContext(), mIcon,
mDefaultColor, mActivatedColor, mUseStar ? Style.FAVORITE : Style.LIKE);
drawable.setCallback(new ViewCallback(menuBar));
item.setIcon(drawable);
}
private static class ViewCallback implements Drawable.Callback {
private final WeakReference<View> mViewRef;
public ViewCallback(View view) {
mViewRef = new WeakReference<>(view);
}
@Override
public void invalidateDrawable(Drawable who) {
final View view = mViewRef.get();
if (view == null) return;
view.invalidate();
}
@Override
public void scheduleDrawable(Drawable who, Runnable what, long when) {
final View view = mViewRef.get();
if (view == null) return;
view.postDelayed(what, when);
}
@Override
public void unscheduleDrawable(Drawable who, Runnable what) {
final View view = mViewRef.get();
if (view == null) return;
view.post(what);
}
}
}

View File

@ -24,6 +24,7 @@ import android.content.SharedPreferences;
import android.content.SharedPreferences.OnSharedPreferenceChangeListener;
import android.preference.Preference;
import android.support.annotation.NonNull;
import android.support.v7.widget.RecyclerView;
import android.util.AttributeSet;
import android.view.View;
import android.view.ViewGroup;
@ -31,7 +32,9 @@ import android.view.ViewGroup;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.DummyStatusHolderAdapter;
import org.mariotaku.twidere.graphic.LikeAnimationDrawable;
import org.mariotaku.twidere.view.holder.StatusViewHolder;
import org.mariotaku.twidere.view.holder.iface.IStatusViewHolder;
public class CardPreviewPreference extends Preference implements Constants, OnSharedPreferenceChangeListener {
@ -87,6 +90,19 @@ public class CardPreviewPreference extends Preference implements Constants, OnSh
mCompactModeChanged = false;
mHolder.setupViewOptions();
mHolder.displaySampleStatus();
mHolder.setStatusClickListener(new IStatusViewHolder.SimpleStatusClickListener() {
@Override
public void onItemActionClick(RecyclerView.ViewHolder holder, int id, int position) {
if (id == R.id.favorite_count) {
((StatusViewHolder) holder).playLikeAnimation(new LikeAnimationDrawable.OnLikedListener() {
@Override
public boolean onLiked() {
return false;
}
});
}
}
});
super.onBindView(view);
}

View File

@ -945,8 +945,6 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
protected void onPreExecute() {
super.onPreExecute();
mCreatingFavoriteIds.put(account_id, status_id);
bus.post(new StatusListChangedEvent());
}
@ -957,14 +955,11 @@ public class AsyncTwitterWrapper extends TwitterWrapper {
final ParcelableStatus status = result.getData();
// BEGIN HotMobi
final TweetEvent event = TweetEvent.create(getContext(), status, TimelineType.OTHER);
event.setAction(TweetEvent.Action.FAVORITE);
HotMobiLogger.getInstance(getContext()).log(account_id, event);
// END HotMobi
bus.post(new FavoriteCreatedEvent(status));
Utils.showOkMessage(mContext, R.string.status_favorited, false);
} else {

View File

@ -67,6 +67,7 @@ import org.mariotaku.twidere.R;
import org.mariotaku.twidere.activity.iface.IThemedActivity;
import org.mariotaku.twidere.graphic.ActionBarColorDrawable;
import org.mariotaku.twidere.graphic.ActionIconDrawable;
import org.mariotaku.twidere.graphic.iface.DoNotWrapDrawable;
import org.mariotaku.twidere.preference.ThemeBackgroundPreference;
import org.mariotaku.twidere.util.menu.TwidereMenuInfo;
import org.mariotaku.twidere.util.support.ViewSupport;
@ -1005,15 +1006,23 @@ public class ThemeUtils implements Constants {
}
public static void wrapMenuIcon(ActionMenuView view, int... excludeGroups) {
final Resources resources = view.getResources();
final int colorDark = resources.getColor(R.color.action_icon_dark);
final int colorLight = resources.getColor(R.color.action_icon_light);
final Context context = view.getContext();
final int colorDark = ContextCompat.getColor(context, R.color.action_icon_dark);
final int colorLight = ContextCompat.getColor(context, R.color.action_icon_light);
wrapMenuIcon(view, colorDark, colorLight, excludeGroups);
}
public static int getActionIconColor(Context context) {
final int colorDark = ContextCompat.getColor(context, R.color.action_icon_dark);
final int colorLight = ContextCompat.getColor(context, R.color.action_icon_light);
final int itemBackgroundColor = ThemeUtils.getThemeBackgroundColor(context);
return TwidereColorUtils.getContrastYIQ(itemBackgroundColor, colorDark, colorLight);
}
public static void wrapMenuIcon(ActionMenuView view, int colorDark, int colorLight, int... excludeGroups) {
final int itemBackgroundColor = ThemeUtils.getThemeBackgroundColor(view.getContext());
final int popupItemBackgroundColor = ThemeUtils.getThemeBackgroundColor(view.getContext(), view.getPopupTheme());
final Context context = view.getContext();
final int itemBackgroundColor = ThemeUtils.getThemeBackgroundColor(context);
final int popupItemBackgroundColor = ThemeUtils.getThemeBackgroundColor(context, view.getPopupTheme());
final int itemColor = TwidereColorUtils.getContrastYIQ(itemBackgroundColor, colorDark, colorLight);
final int popupItemColor = TwidereColorUtils.getContrastYIQ(popupItemBackgroundColor, colorDark, colorLight);
final Menu menu = view.getMenu();
@ -1034,7 +1043,7 @@ public class ThemeUtils implements Constants {
public static void wrapMenuItemIcon(@NonNull MenuItem item, int itemColor, int... excludeGroups) {
if (ArrayUtils.contains(excludeGroups, item.getGroupId())) return;
final Drawable icon = item.getIcon();
if (icon == null) return;
if (icon == null || icon instanceof DoNotWrapDrawable) return;
if (icon instanceof ActionIconDrawable) {
((ActionIconDrawable) icon).setDefaultColor(itemColor);
item.setIcon(icon);

View File

@ -235,12 +235,12 @@ public class ThemedLayoutInflaterFactory implements LayoutInflaterFactory {
} else if (tintable instanceof EditText) {
tintable.setSupportBackgroundTintList(ColorStateList.valueOf(backgroundTintColor));
} else if (isColorTint) {
final int[][] states = {{android.R.attr.state_selected}, {android.R.attr.state_focused},
{android.R.attr.state_pressed}, {0}};
final int[] colors = {accentColor, accentColor, accentColor, noTintColor};
tintable.setSupportBackgroundTintList(new ColorStateList(states, colors));
// final int[][] states = {{android.R.attr.state_selected}, {android.R.attr.state_focused},
// {android.R.attr.state_pressed}, {0}};
// final int[] colors = {accentColor, accentColor, accentColor, noTintColor};
// tintable.setSupportBackgroundTintList(new ColorStateList(states, colors));
} else {
tintable.setSupportBackgroundTintList(ColorStateList.valueOf(accentColor));
// tintable.setSupportBackgroundTintList(ColorStateList.valueOf(accentColor));
}
}

View File

@ -138,6 +138,7 @@ import org.mariotaku.twidere.api.twitter.model.Relationship;
import org.mariotaku.twidere.api.twitter.model.Status;
import org.mariotaku.twidere.api.twitter.model.UserMentionEntity;
import org.mariotaku.twidere.fragment.iface.IBaseFragment.SystemWindowsInsetsCallback;
import org.mariotaku.twidere.fragment.support.AbsStatusesFragment.DefaultOnLikedListener;
import org.mariotaku.twidere.fragment.support.AccountsManagerFragment;
import org.mariotaku.twidere.fragment.support.AddStatusFilterDialogFragment;
import org.mariotaku.twidere.fragment.support.DestroyStatusDialogFragment;
@ -174,6 +175,7 @@ import org.mariotaku.twidere.fragment.support.UsersListFragment;
import org.mariotaku.twidere.graphic.ActionIconDrawable;
import org.mariotaku.twidere.graphic.PaddingDrawable;
import org.mariotaku.twidere.menu.SupportStatusShareProvider;
import org.mariotaku.twidere.menu.support.FavoriteItemProvider;
import org.mariotaku.twidere.model.AccountPreferences;
import org.mariotaku.twidere.model.ParcelableAccount;
import org.mariotaku.twidere.model.ParcelableCredentials;
@ -215,7 +217,6 @@ import java.nio.charset.Charset;
import java.text.NumberFormat;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Locale;
@ -2251,27 +2252,37 @@ public final class Utils implements Constants {
}
final MenuItem favorite = menu.findItem(R.id.favorite);
if (favorite != null) {
final boolean is_favorite;
final boolean isFavorite;
if (twitter.isCreatingFavorite(status.account_id, status.id)) {
is_favorite = true;
isFavorite = true;
} else if (twitter.isDestroyingFavorite(status.account_id, status.id)) {
is_favorite = false;
isFavorite = false;
} else {
is_favorite = status.is_favorite;
isFavorite = status.is_favorite;
}
if (preferences.getBoolean(KEY_I_WANT_MY_STARS_BACK)) {
final Drawable oldIcon = favorite.getIcon();
if (oldIcon instanceof ActionIconDrawable) {
final Drawable starIcon = ContextCompat.getDrawable(context, R.drawable.ic_action_star);
favorite.setIcon(new ActionIconDrawable(starIcon, ((ActionIconDrawable) oldIcon).getDefaultColor()));
} else {
favorite.setIcon(R.drawable.ic_action_star);
}
ActionIconDrawable.setMenuHighlight(favorite, new TwidereMenuInfo(is_favorite, favoriteHighlight));
favorite.setTitle(is_favorite ? R.string.unfavorite : R.string.favorite);
ActionProvider provider = MenuItemCompat.getActionProvider(favorite);
final boolean useStar = preferences.getBoolean(KEY_I_WANT_MY_STARS_BACK);
if (provider instanceof FavoriteItemProvider) {
} else {
ActionIconDrawable.setMenuHighlight(favorite, new TwidereMenuInfo(is_favorite, likeHighlight));
favorite.setTitle(is_favorite ? R.string.undo_like : R.string.like);
if (useStar) {
final Drawable oldIcon = favorite.getIcon();
if (oldIcon instanceof ActionIconDrawable) {
final Drawable starIcon = ContextCompat.getDrawable(context, R.drawable.ic_action_star);
favorite.setIcon(new ActionIconDrawable(starIcon, ((ActionIconDrawable) oldIcon).getDefaultColor()));
} else {
favorite.setIcon(R.drawable.ic_action_star);
}
ActionIconDrawable.setMenuHighlight(favorite, new TwidereMenuInfo(isFavorite, favoriteHighlight));
} else {
ActionIconDrawable.setMenuHighlight(favorite, new TwidereMenuInfo(isFavorite, likeHighlight));
}
}
if (useStar) {
favorite.setTitle(isFavorite ? R.string.unfavorite : R.string.favorite);
} else {
favorite.setTitle(isFavorite ? R.string.undo_like : R.string.like);
}
}
final MenuItem translate = menu.findItem(R.id.translate);
@ -2561,11 +2572,13 @@ public final class Utils implements Constants {
return pm.getDrawable(info.packageName, info.metaData.getInt(key), info.applicationInfo);
}
public static boolean handleMenuItemClick(@NonNull Context context, @Nullable Fragment fragment,
@NonNull FragmentManager fm,
@NonNull UserColorNameManager colorNameManager,
@NonNull AsyncTwitterWrapper twitter,
@NonNull ParcelableStatus status, @NonNull MenuItem item) {
public static boolean handleMenuItemClick(@NonNull final Context context,
@Nullable final Fragment fragment,
@NonNull final FragmentManager fm,
@NonNull final UserColorNameManager colorNameManager,
@NonNull final AsyncTwitterWrapper twitter,
@NonNull final ParcelableStatus status,
@NonNull final MenuItem item) {
switch (item.getItemId()) {
case R.id.copy: {
if (ClipboardUtils.setText(context, status.text_plain)) {
@ -2597,7 +2610,13 @@ public final class Utils implements Constants {
if (status.is_favorite) {
twitter.destroyFavoriteAsync(status.account_id, status.id);
} else {
twitter.createFavoriteAsync(status.account_id, status.id);
ActionProvider provider = MenuItemCompat.getActionProvider(item);
if (provider instanceof FavoriteItemProvider) {
((FavoriteItemProvider) provider).invokeItem(item,
new DefaultOnLikedListener(twitter, status));
} else {
twitter.createFavoriteAsync(status.account_id, status.id);
}
}
break;
}

View File

@ -1,6 +1,7 @@
package org.mariotaku.twidere.view.holder;
import android.content.Context;
import android.graphics.drawable.Drawable;
import android.support.annotation.NonNull;
import android.support.v4.content.ContextCompat;
import android.support.v4.text.BidiFormatter;
@ -18,6 +19,7 @@ import org.apache.commons.lang3.ArrayUtils;
import org.mariotaku.twidere.Constants;
import org.mariotaku.twidere.R;
import org.mariotaku.twidere.adapter.iface.IStatusesAdapter;
import org.mariotaku.twidere.graphic.LikeAnimationDrawable;
import org.mariotaku.twidere.model.ParcelableLocation;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableStatus;
@ -401,15 +403,40 @@ public class StatusViewHolder extends ViewHolder implements Constants, IStatusVi
nameView.setNameFirst(nameFirst);
quotedNameView.setNameFirst(nameFirst);
final int likeIcon, likeStyle;
if (adapter.shouldUseStarsForLikes()) {
favoriteCountView.setActivatedColor(ContextCompat.getColor(adapter.getContext(),
R.color.highlight_favorite));
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(favoriteCountView,
R.drawable.ic_action_star, 0, 0, 0);
likeIcon = R.drawable.ic_action_star;
likeStyle = LikeAnimationDrawable.Style.FAVORITE;
} else {
likeIcon = R.drawable.ic_action_heart;
likeStyle = LikeAnimationDrawable.Style.LIKE;
}
final LikeAnimationDrawable drawable = new LikeAnimationDrawable(adapter.getContext(),
likeIcon, favoriteCountView.getColor(), favoriteCountView.getActivatedColor(),
likeStyle);
TextViewCompat.setCompoundDrawablesRelativeWithIntrinsicBounds(favoriteCountView,
drawable, null, null, null);
drawable.setCallback(favoriteCountView);
timeView.setShowAbsoluteTime(adapter.isShowAbsoluteTime());
}
@Override
public void playLikeAnimation(@NonNull LikeAnimationDrawable.OnLikedListener listener) {
boolean handled = false;
for (Drawable drawable : favoriteCountView.getCompoundDrawables()) {
if (drawable instanceof LikeAnimationDrawable) {
((LikeAnimationDrawable) drawable).setOnLikedListener(listener);
((LikeAnimationDrawable) drawable).start();
handled = true;
}
}
if (!handled) {
listener.onLiked();
}
}
void displayExtraTypeIcon(String cardName, ParcelableMedia[] media, ParcelableLocation location, String placeFullName, boolean sensitive) {
if (TwitterCardUtils.CARD_NAME_AUDIO.equals(cardName)) {
extraTypeView.setImageResource(sensitive ? R.drawable.ic_action_warning : R.drawable.ic_action_music);

View File

@ -21,10 +21,12 @@ package org.mariotaku.twidere.view.holder.iface;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.support.v7.widget.RecyclerView;
import android.view.View;
import android.widget.ImageView;
import org.mariotaku.twidere.adapter.iface.ContentCardClickListener;
import org.mariotaku.twidere.graphic.LikeAnimationDrawable;
import org.mariotaku.twidere.model.ParcelableMedia;
import org.mariotaku.twidere.model.ParcelableStatus;
import org.mariotaku.twidere.view.CardMediaContainer;
@ -51,6 +53,8 @@ public interface IStatusViewHolder extends CardMediaContainer.OnMediaClickListen
void setTextSize(float textSize);
void playLikeAnimation(LikeAnimationDrawable.OnLikedListener listener);
interface StatusClickListener extends ContentCardClickListener {
void onMediaClick(IStatusViewHolder holder, View view, ParcelableMedia media, int statusPosition);
@ -61,4 +65,33 @@ public interface IStatusViewHolder extends CardMediaContainer.OnMediaClickListen
void onUserProfileClick(IStatusViewHolder holder, int position);
}
abstract class SimpleStatusClickListener implements StatusClickListener {
public void onMediaClick(IStatusViewHolder holder, View view, ParcelableMedia media, int statusPosition) {
}
public void onStatusClick(IStatusViewHolder holder, int position) {
}
public boolean onStatusLongClick(IStatusViewHolder holder, int position) {
return false;
}
public void onUserProfileClick(IStatusViewHolder holder, int position) {
}
@Override
public void onItemActionClick(RecyclerView.ViewHolder holder, int id, int position) {
}
@Override
public void onItemMenuClick(RecyclerView.ViewHolder holder, View menuView, int position) {
}
}
}

View File

@ -28,6 +28,7 @@
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:background="?selectableItemBackground"
android:clipChildren="false"
android:focusable="true"
android:paddingTop="@dimen/element_spacing_small"
app:ignorePadding="true">
@ -272,6 +273,7 @@
android:layout_alignStart="@+id/profile_container"
android:layout_below="@+id/status_content_space"
android:layout_marginTop="@dimen/element_spacing_minus_mlarge"
android:clipChildren="false"
android:gravity="center_vertical|start"
android:orientation="horizontal">

View File

@ -396,6 +396,7 @@
android:id="@+id/menu_bar"
android:layout_width="match_parent"
android:layout_height="?android:actionBarSize"
android:layout_below="@+id/counts_users_height_holder"/>
android:layout_below="@+id/counts_users_height_holder"
android:clipChildren="false"/>
</RelativeLayout>

View File

@ -0,0 +1,21 @@
<?xml version="1.0" encoding="utf-8"?>
<FrameLayout
style="?actionButtonStyle"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="wrap_content"
android:layout_height="match_parent"
android:layout_gravity="center"
android:addStatesFromChildren="true"
android:background="?actionBarItemBackground"
android:focusable="true">
<ImageView
android:id="@+id/icon"
xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent"
android:layout_height="match_parent"
android:layout_gravity="center"
android:contentDescription="@string/like"
android:scaleType="centerInside"
android:src="@drawable/ic_action_heart"/>
</FrameLayout>

View File

@ -18,6 +18,7 @@
android:id="@id/favorite"
android:icon="@drawable/ic_action_heart"
android:title="@string/like"
app:actionProviderClass="org.mariotaku.twidere.menu.support.FavoriteItemProvider"
app:showAsAction="always"/>
<item
android:id="@id/share"