diff --git a/app/src/main/java/com/gh/common/util/LogUtils.java b/app/src/main/java/com/gh/common/util/LogUtils.java index ae7aa137c6..0c15b549d4 100644 --- a/app/src/main/java/com/gh/common/util/LogUtils.java +++ b/app/src/main/java/com/gh/common/util/LogUtils.java @@ -2,6 +2,7 @@ package com.gh.common.util; import android.app.Application; import android.content.Context; +import android.os.Build; import android.provider.Settings; import android.text.TextUtils; @@ -29,6 +30,7 @@ public class LogUtils { object.put("launch_type", launchType.name()); object.put("network", DeviceUtils.getNetwork(application)); object.put("device_model", android.os.Build.MODEL); + object.put("manufacture", Build.MANUFACTURER); object.put("device_system", android.os.Build.VERSION.RELEASE); } catch (JSONException e) { e.printStackTrace(); diff --git a/app/src/main/java/com/gh/common/view/materialratingbar/BaseDrawable.java b/app/src/main/java/com/gh/common/view/materialratingbar/BaseDrawable.java new file mode 100644 index 0000000000..a2fd8b374d --- /dev/null +++ b/app/src/main/java/com/gh/common/view/materialratingbar/BaseDrawable.java @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2016 Zhang Hai + * All Rights Reserved. + */ + +package com.gh.common.view.materialratingbar; + +import android.content.res.ColorStateList; +import android.graphics.Canvas; +import android.graphics.Color; +import android.graphics.ColorFilter; +import android.graphics.PixelFormat; +import android.graphics.PorterDuff; +import android.graphics.PorterDuffColorFilter; +import android.graphics.Rect; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +abstract class BaseDrawable extends Drawable implements TintableDrawable { + + protected int mAlpha = 0xFF; + protected ColorFilter mColorFilter; + protected ColorStateList mTintList; + protected PorterDuff.Mode mTintMode = PorterDuff.Mode.SRC_IN; + protected PorterDuffColorFilter mTintFilter; + + private DummyConstantState mConstantState = new DummyConstantState(); + + @Override + public int getAlpha() { + return mAlpha; + } + + /** + * {@inheritDoc} + */ + @Override + public void setAlpha(int alpha) { + if (mAlpha != alpha) { + mAlpha = alpha; + invalidateSelf(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public ColorFilter getColorFilter() { + return mColorFilter; + } + + /** + * {@inheritDoc} + */ + @Override + public void setColorFilter(@Nullable ColorFilter colorFilter) { + mColorFilter = colorFilter; + invalidateSelf(); + } + + /** + * {@inheritDoc} + */ + @Override + public void setTint(@ColorInt int tintColor) { + setTintList(ColorStateList.valueOf(tintColor)); + } + + /** + * {@inheritDoc} + */ + @Override + public void setTintList(@Nullable ColorStateList tint) { + mTintList = tint; + if (updateTintFilter()) { + invalidateSelf(); + } + } + + /** + * {@inheritDoc} + */ + @Override + public void setTintMode(@NonNull PorterDuff.Mode tintMode) { + mTintMode = tintMode; + if (updateTintFilter()) { + invalidateSelf(); + } + } + + @Override + public boolean isStateful() { + return mTintList != null && mTintList.isStateful(); + } + + @Override + protected boolean onStateChange(int[] state) { + return updateTintFilter(); + } + + private boolean updateTintFilter() { + + if (mTintList == null || mTintMode == null) { + boolean hadTintFilter = mTintFilter != null; + mTintFilter = null; + return hadTintFilter; + } + + int tintColor = mTintList.getColorForState(getState(), Color.TRANSPARENT); + // They made PorterDuffColorFilter.setColor() and setMode() @hide. + mTintFilter = new PorterDuffColorFilter(tintColor, mTintMode); + return true; + } + + /** + * {@inheritDoc} + */ + @Override + public int getOpacity() { + // Be safe. + return PixelFormat.TRANSLUCENT; + } + + /** + * {@inheritDoc} + */ + @Override + public void draw(Canvas canvas) { + + Rect bounds = getBounds(); + if (bounds.width() == 0 || bounds.height() == 0) { + return; + } + + int saveCount = canvas.save(); + canvas.translate(bounds.left, bounds.top); + onDraw(canvas, bounds.width(), bounds.height()); + canvas.restoreToCount(saveCount); + } + + protected ColorFilter getColorFilterForDrawing() { + return mColorFilter != null ? mColorFilter : mTintFilter; + } + + protected abstract void onDraw(Canvas canvas, int width, int height); + + // Workaround LayerDrawable.ChildDrawable which calls getConstantState().newDrawable() + // without checking for null. + // We are never inflated from XML so the protocol of ConstantState does not apply to us. In + // order to make LayerDrawable happy, we return ourselves from DummyConstantState.newDrawable(). + + @Override + public ConstantState getConstantState() { + return mConstantState; + } + + private class DummyConstantState extends ConstantState { + + @Override + public int getChangingConfigurations() { + return 0; + } + + @NonNull + @Override + public Drawable newDrawable() { + return BaseDrawable.this; + } + } +} diff --git a/app/src/main/java/com/gh/common/view/materialratingbar/ClipDrawableCompat.java b/app/src/main/java/com/gh/common/view/materialratingbar/ClipDrawableCompat.java new file mode 100644 index 0000000000..a32d42274c --- /dev/null +++ b/app/src/main/java/com/gh/common/view/materialratingbar/ClipDrawableCompat.java @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2016 Zhang Hai + * All Rights Reserved. + */ + +package com.gh.common.view.materialratingbar; + +import android.content.res.ColorStateList; +import android.graphics.PorterDuff; +import android.graphics.drawable.ClipDrawable; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; +import android.util.Log; + +class ClipDrawableCompat extends ClipDrawable implements TintableDrawable { + + private static final String TAG = ClipDrawableCompat.class.getSimpleName(); + + private Drawable mDrawable; + + private DummyConstantState mConstantState = new DummyConstantState(); + + public ClipDrawableCompat(Drawable drawable, int gravity, int orientation) { + super(drawable, gravity, orientation); + + mDrawable = drawable; + } + + @Nullable + @Override + public Drawable getDrawable() { + return mDrawable; + } + + @Override + public void setTint(int tintColor) { + if (mDrawable instanceof TintableDrawable) { + //noinspection RedundantCast + ((TintableDrawable) mDrawable).setTint(tintColor); + } else { + Log.w(TAG, "Drawable did not implement TintableDrawable, it won't be tinted below" + + " Lollipop"); + super.setTint(tintColor); + } + } + + @Override + public void setTintList(ColorStateList tint) { + if (mDrawable instanceof TintableDrawable) { + //noinspection RedundantCast + ((TintableDrawable) mDrawable).setTintList(tint); + } else { + Log.w(TAG, "Drawable did not implement TintableDrawable, it won't be tinted below" + + " Lollipop"); + super.setTintList(tint); + } + } + + @Override + public void setTintMode(PorterDuff.Mode tintMode) { + if (mDrawable instanceof TintableDrawable) { + //noinspection RedundantCast + ((TintableDrawable) mDrawable).setTintMode(tintMode); + } else { + Log.w(TAG, "Drawable did not implement TintableDrawable, it won't be tinted below" + + " Lollipop"); + super.setTintMode(tintMode); + } + } + + // Workaround LayerDrawable.ChildDrawable which calls getConstantState().newDrawable() + // without checking for null. + // We are never inflated from XML so the protocol of ConstantState does not apply to us. In + // order to make LayerDrawable happy, we return ourselves from DummyConstantState.newDrawable(). + + @Override + public ConstantState getConstantState() { + return mConstantState; + } + + private class DummyConstantState extends ConstantState { + + @Override + public int getChangingConfigurations() { + return 0; + } + + @NonNull + @Override + public Drawable newDrawable() { + return ClipDrawableCompat.this; + } + } +} diff --git a/app/src/main/java/com/gh/common/view/materialratingbar/MaterialRatingBar.java b/app/src/main/java/com/gh/common/view/materialratingbar/MaterialRatingBar.java new file mode 100644 index 0000000000..1b76d6870e --- /dev/null +++ b/app/src/main/java/com/gh/common/view/materialratingbar/MaterialRatingBar.java @@ -0,0 +1,662 @@ +/* + * Copyright (c) 2016 Zhang Hai + * All Rights Reserved. + */ + +package com.gh.common.view.materialratingbar; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.content.res.ColorStateList; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.os.Build; +import android.support.annotation.Nullable; +import android.support.v7.widget.TintTypedArray; +import android.util.AttributeSet; +import android.util.Log; +import android.view.View; +import android.widget.RatingBar; + +import com.gh.common.view.materialratingbar.internal.DrawableCompat; +import com.gh.gamecenter.R; + + +// AppCompatRatingBar will add undesired measuring behavior. +@SuppressLint("AppCompatCustomView") +public class MaterialRatingBar extends RatingBar { + + private static final String TAG = MaterialRatingBar.class.getSimpleName(); + + private TintInfo mProgressTintInfo = new TintInfo(); + + private MaterialRatingDrawable mDrawable; + + private OnRatingChangeListener mOnRatingChangeListener; + private float mLastKnownRating; + + public MaterialRatingBar(Context context) { + super(context); + + init(null, 0); + } + + public MaterialRatingBar(Context context, AttributeSet attrs) { + super(context, attrs); + + init(attrs, 0); + } + + public MaterialRatingBar(Context context, AttributeSet attrs, int defStyleAttr) { + super(context, attrs, defStyleAttr); + + init(attrs, defStyleAttr); + } + + @SuppressWarnings("RestrictedApi") + private void init(AttributeSet attrs, int defStyleAttr) { + + Context context = getContext(); + TintTypedArray a = TintTypedArray.obtainStyledAttributes(context, attrs, + R.styleable.MaterialRatingBar, defStyleAttr, 0); + if (a.hasValue(R.styleable.MaterialRatingBar_mrb_progressTint)) { + mProgressTintInfo.mProgressTintList = a.getColorStateList( + R.styleable.MaterialRatingBar_mrb_progressTint); + mProgressTintInfo.mHasProgressTintList = true; + } + if (a.hasValue(R.styleable.MaterialRatingBar_mrb_progressTintMode)) { + mProgressTintInfo.mProgressTintMode = DrawableCompat.parseTintMode(a.getInt( + R.styleable.MaterialRatingBar_mrb_progressTintMode, -1), null); + mProgressTintInfo.mHasProgressTintMode = true; + } + if (a.hasValue(R.styleable.MaterialRatingBar_mrb_secondaryProgressTint)) { + mProgressTintInfo.mSecondaryProgressTintList = a.getColorStateList( + R.styleable.MaterialRatingBar_mrb_secondaryProgressTint); + mProgressTintInfo.mHasSecondaryProgressTintList = true; + } + if (a.hasValue(R.styleable.MaterialRatingBar_mrb_secondaryProgressTintMode)) { + mProgressTintInfo.mSecondaryProgressTintMode = DrawableCompat.parseTintMode(a.getInt( + R.styleable.MaterialRatingBar_mrb_secondaryProgressTintMode, -1), null); + mProgressTintInfo.mHasSecondaryProgressTintMode = true; + } + if (a.hasValue(R.styleable.MaterialRatingBar_mrb_progressBackgroundTint)) { + mProgressTintInfo.mProgressBackgroundTintList = a.getColorStateList( + R.styleable.MaterialRatingBar_mrb_progressBackgroundTint); + mProgressTintInfo.mHasProgressBackgroundTintList = true; + } + if (a.hasValue(R.styleable.MaterialRatingBar_mrb_progressBackgroundTintMode)) { + mProgressTintInfo.mProgressBackgroundTintMode = DrawableCompat.parseTintMode(a.getInt( + R.styleable.MaterialRatingBar_mrb_progressBackgroundTintMode, -1), null); + mProgressTintInfo.mHasProgressBackgroundTintMode = true; + } + if (a.hasValue(R.styleable.MaterialRatingBar_mrb_indeterminateTint)) { + mProgressTintInfo.mIndeterminateTintList = a.getColorStateList( + R.styleable.MaterialRatingBar_mrb_indeterminateTint); + mProgressTintInfo.mHasIndeterminateTintList = true; + } + if (a.hasValue(R.styleable.MaterialRatingBar_mrb_indeterminateTintMode)) { + mProgressTintInfo.mIndeterminateTintMode = DrawableCompat.parseTintMode(a.getInt( + R.styleable.MaterialRatingBar_mrb_indeterminateTintMode, -1), null); + mProgressTintInfo.mHasIndeterminateTintMode = true; + } + boolean fillBackgroundStars = a.getBoolean( + R.styleable.MaterialRatingBar_mrb_fillBackgroundStars, isIndicator()); + a.recycle(); + + mDrawable = new MaterialRatingDrawable(getContext(), fillBackgroundStars); + mDrawable.setStarCount(getNumStars()); + setProgressDrawable(mDrawable); + } + + @Override + public void setNumStars(int numStars) { + super.setNumStars(numStars); + + // mDrawable can be null during super class initialization. + if (mDrawable != null) { + mDrawable.setStarCount(numStars); + } + } + + @Override + protected synchronized void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { + super.onMeasure(widthMeasureSpec, heightMeasureSpec); + + int height = getMeasuredHeight(); + int width = Math.round(height * mDrawable.getTileRatio() * getNumStars()); + setMeasuredDimension(View.resolveSizeAndState(width, widthMeasureSpec, 0), height); + } + + @Override + public void setProgressDrawable(Drawable d) { + super.setProgressDrawable(d); + + // mProgressTintInfo can be null during super class initialization. + if (mProgressTintInfo != null) { + applyProgressTints(); + } + } + + @Override + public void setIndeterminateDrawable(Drawable d) { + super.setIndeterminateDrawable(d); + + // mProgressTintInfo can be null during super class initialization. + if (mProgressTintInfo != null) { + applyIndeterminateTint(); + } + } + + /** + * @deprecated Use {@link #getSupportProgressTintList()} instead. + */ + @Nullable + @Override + public ColorStateList getProgressTintList() { + logRatingBarTintWarning(); + return getSupportProgressTintList(); + } + + /** + * @deprecated Use {@link #setSupportProgressTintList(ColorStateList)} instead. + */ + @Override + public void setProgressTintList(@Nullable ColorStateList tint) { + logRatingBarTintWarning(); + setSupportProgressTintList(tint); + } + + /** + * @deprecated Use {@link #getSupportProgressTintMode()} instead. + */ + @Nullable + @Override + public PorterDuff.Mode getProgressTintMode() { + logRatingBarTintWarning(); + return getSupportProgressTintMode(); + } + + /** + * @deprecated Use {@link #setSupportProgressTintMode(PorterDuff.Mode)} instead. + */ + @Override + public void setProgressTintMode(@Nullable PorterDuff.Mode tintMode) { + logRatingBarTintWarning(); + setSupportProgressTintMode(tintMode); + } + + /** + * @deprecated Use {@link #getSupportSecondaryProgressTintList()} instead. + */ + @Nullable + @Override + public ColorStateList getSecondaryProgressTintList() { + logRatingBarTintWarning(); + return getSupportSecondaryProgressTintList(); + } + + /** + * @deprecated Use {@link #setSupportSecondaryProgressTintList(ColorStateList)} instead. + */ + @Override + public void setSecondaryProgressTintList(@Nullable ColorStateList tint) { + logRatingBarTintWarning(); + setSupportSecondaryProgressTintList(tint); + } + + /** + * @deprecated Use {@link #getSupportSecondaryProgressTintMode()} instead. + */ + @Nullable + @Override + public PorterDuff.Mode getSecondaryProgressTintMode() { + logRatingBarTintWarning(); + return getSupportSecondaryProgressTintMode(); + } + + /** + * @deprecated Use {@link #setSupportSecondaryProgressTintMode(PorterDuff.Mode)} instead. + */ + @Override + public void setSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) { + logRatingBarTintWarning(); + setSupportSecondaryProgressTintMode(tintMode); + } + + /** + * @deprecated Use {@link #getSupportProgressBackgroundTintList()} instead. + */ + @Nullable + @Override + public ColorStateList getProgressBackgroundTintList() { + logRatingBarTintWarning(); + return getSupportProgressBackgroundTintList(); + } + + /** + * @deprecated Use {@link #setSupportProgressBackgroundTintList(ColorStateList)} instead. + */ + @Override + public void setProgressBackgroundTintList(@Nullable ColorStateList tint) { + logRatingBarTintWarning(); + setSupportProgressBackgroundTintList(tint); + } + + /** + * @deprecated Use {@link #getSupportProgressBackgroundTintMode()} instead. + */ + @Nullable + @Override + public PorterDuff.Mode getProgressBackgroundTintMode() { + logRatingBarTintWarning(); + return getSupportProgressBackgroundTintMode(); + } + + /** + * @deprecated Use {@link #setSupportProgressBackgroundTintMode(PorterDuff.Mode)} instead. + */ + @Override + public void setProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { + logRatingBarTintWarning(); + setSupportProgressBackgroundTintMode(tintMode); + } + + /** + * @deprecated Use {@link #getSupportIndeterminateTintList()} instead. + */ + @Nullable + @Override + public ColorStateList getIndeterminateTintList() { + logRatingBarTintWarning(); + return getSupportIndeterminateTintList(); + } + + /** + * @deprecated Use {@link #setSupportIndeterminateTintList(ColorStateList)} instead. + */ + @Override + public void setIndeterminateTintList(@Nullable ColorStateList tint) { + logRatingBarTintWarning(); + setSupportIndeterminateTintList(tint); + } + + /** + * @deprecated Use {@link #getSupportIndeterminateTintMode()} instead. + */ + @Nullable + @Override + public PorterDuff.Mode getIndeterminateTintMode() { + logRatingBarTintWarning(); + return getSupportIndeterminateTintMode(); + } + + /** + * @deprecated Use {@link #setSupportIndeterminateTintMode(PorterDuff.Mode)} instead. + */ + @Override + public void setIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) { + logRatingBarTintWarning(); + setSupportIndeterminateTintMode(tintMode); + } + + private void logRatingBarTintWarning() { + if (getContext().getApplicationInfo().minSdkVersion >= Build.VERSION_CODES.LOLLIPOP) { + return; + } + Log.w(TAG, "Non-support version of tint method called, this is error-prone and will crash" + + " below Lollipop if you are calling it as a method of RatingBar instead of" + + " MaterialRatingBar"); + } + + /** + * @see RatingBar#getProgressTintList() + */ + @Nullable + public ColorStateList getSupportProgressTintList() { + return mProgressTintInfo.mProgressTintList; + } + + /** + * @see RatingBar#setProgressTintList(ColorStateList) + */ + public void setSupportProgressTintList(@Nullable ColorStateList tint) { + mProgressTintInfo.mProgressTintList = tint; + mProgressTintInfo.mHasProgressTintList = true; + + applyPrimaryProgressTint(); + } + + /** + * @see RatingBar#getProgressTintMode() + */ + @Nullable + public PorterDuff.Mode getSupportProgressTintMode() { + return mProgressTintInfo.mProgressTintMode; + } + + /** + * @see RatingBar#setProgressTintMode(PorterDuff.Mode) + */ + public void setSupportProgressTintMode(@Nullable PorterDuff.Mode tintMode) { + mProgressTintInfo.mProgressTintMode = tintMode; + mProgressTintInfo.mHasProgressTintMode = true; + + applyPrimaryProgressTint(); + } + + /** + * @see RatingBar#getSecondaryProgressTintList() + */ + @Nullable + public ColorStateList getSupportSecondaryProgressTintList() { + return mProgressTintInfo.mSecondaryProgressTintList; + } + + /** + * @see RatingBar#setSecondaryProgressTintList(ColorStateList) + */ + public void setSupportSecondaryProgressTintList(@Nullable ColorStateList tint) { + mProgressTintInfo.mSecondaryProgressTintList = tint; + mProgressTintInfo.mHasSecondaryProgressTintList = true; + + applySecondaryProgressTint(); + } + + /** + * @see RatingBar#getSecondaryProgressTintMode() + */ + @Nullable + public PorterDuff.Mode getSupportSecondaryProgressTintMode() { + return mProgressTintInfo.mSecondaryProgressTintMode; + } + + /** + * @see RatingBar#setSecondaryProgressTintMode(PorterDuff.Mode) + */ + public void setSupportSecondaryProgressTintMode(@Nullable PorterDuff.Mode tintMode) { + mProgressTintInfo.mSecondaryProgressTintMode = tintMode; + mProgressTintInfo.mHasSecondaryProgressTintMode = true; + + applySecondaryProgressTint(); + } + + /** + * @see RatingBar#getProgressBackgroundTintList() + */ + @Nullable + public ColorStateList getSupportProgressBackgroundTintList() { + return mProgressTintInfo.mProgressBackgroundTintList; + } + + /** + * @see RatingBar#setProgressBackgroundTintList(ColorStateList) + */ + public void setSupportProgressBackgroundTintList(@Nullable ColorStateList tint) { + mProgressTintInfo.mProgressBackgroundTintList = tint; + mProgressTintInfo.mHasProgressBackgroundTintList = true; + + applyProgressBackgroundTint(); + } + + /** + * @see RatingBar#getProgressBackgroundTintMode() + */ + @Nullable + public PorterDuff.Mode getSupportProgressBackgroundTintMode() { + return mProgressTintInfo.mProgressBackgroundTintMode; + } + + /** + * @see RatingBar#setProgressBackgroundTintMode(PorterDuff.Mode) + */ + public void setSupportProgressBackgroundTintMode(@Nullable PorterDuff.Mode tintMode) { + mProgressTintInfo.mProgressBackgroundTintMode = tintMode; + mProgressTintInfo.mHasProgressBackgroundTintMode = true; + + applyProgressBackgroundTint(); + } + + /** + * @see RatingBar#getIndeterminateTintList() + */ + @Nullable + public ColorStateList getSupportIndeterminateTintList() { + return mProgressTintInfo.mIndeterminateTintList; + } + + /** + * @see RatingBar#setIndeterminateTintList(ColorStateList) + */ + public void setSupportIndeterminateTintList(@Nullable ColorStateList tint) { + mProgressTintInfo.mIndeterminateTintList = tint; + mProgressTintInfo.mHasIndeterminateTintList = true; + + applyIndeterminateTint(); + } + + /** + * @see RatingBar#getIndeterminateTintMode() + */ + @Nullable + public PorterDuff.Mode getSupportIndeterminateTintMode() { + return mProgressTintInfo.mIndeterminateTintMode; + } + + /** + * @see RatingBar#setIndeterminateTintMode(PorterDuff.Mode) + */ + public void setSupportIndeterminateTintMode(@Nullable PorterDuff.Mode tintMode) { + mProgressTintInfo.mIndeterminateTintMode = tintMode; + mProgressTintInfo.mHasIndeterminateTintMode = true; + + applyIndeterminateTint(); + } + + private void applyProgressTints() { + if (getProgressDrawable() == null) { + return; + } + applyPrimaryProgressTint(); + applyProgressBackgroundTint(); + applySecondaryProgressTint(); + } + + private void applyPrimaryProgressTint() { + if (getProgressDrawable() == null) { + return; + } + if (mProgressTintInfo.mHasProgressTintList || mProgressTintInfo.mHasProgressTintMode) { + Drawable target = getTintTargetFromProgressDrawable(android.R.id.progress, true); + if (target != null) { + applyTintForDrawable(target, mProgressTintInfo.mProgressTintList, + mProgressTintInfo.mHasProgressTintList, mProgressTintInfo.mProgressTintMode, + mProgressTintInfo.mHasProgressTintMode); + } + } + } + + private void applySecondaryProgressTint() { + if (getProgressDrawable() == null) { + return; + } + if (mProgressTintInfo.mHasSecondaryProgressTintList + || mProgressTintInfo.mHasSecondaryProgressTintMode) { + Drawable target = getTintTargetFromProgressDrawable(android.R.id.secondaryProgress, + false); + if (target != null) { + applyTintForDrawable(target, mProgressTintInfo.mSecondaryProgressTintList, + mProgressTintInfo.mHasSecondaryProgressTintList, + mProgressTintInfo.mSecondaryProgressTintMode, + mProgressTintInfo.mHasSecondaryProgressTintMode); + } + } + } + + private void applyProgressBackgroundTint() { + if (getProgressDrawable() == null) { + return; + } + if (mProgressTintInfo.mHasProgressBackgroundTintList + || mProgressTintInfo.mHasProgressBackgroundTintMode) { + Drawable target = getTintTargetFromProgressDrawable(android.R.id.background, false); + if (target != null) { + applyTintForDrawable(target, mProgressTintInfo.mProgressBackgroundTintList, + mProgressTintInfo.mHasProgressBackgroundTintList, + mProgressTintInfo.mProgressBackgroundTintMode, + mProgressTintInfo.mHasProgressBackgroundTintMode); + } + } + } + + private Drawable getTintTargetFromProgressDrawable(int layerId, boolean shouldFallback) { + Drawable progressDrawable = getProgressDrawable(); + if (progressDrawable == null) { + return null; + } + progressDrawable.mutate(); + Drawable layerDrawable = null; + if (progressDrawable instanceof LayerDrawable) { + layerDrawable = ((LayerDrawable) progressDrawable).findDrawableByLayerId(layerId); + } + if (layerDrawable == null && shouldFallback) { + layerDrawable = progressDrawable; + } + return layerDrawable; + } + + private void applyIndeterminateTint() { + Drawable indeterminateDrawable = getIndeterminateDrawable(); + if (indeterminateDrawable == null) { + return; + } + if (mProgressTintInfo.mHasIndeterminateTintList + || mProgressTintInfo.mHasIndeterminateTintMode) { + indeterminateDrawable.mutate(); + applyTintForDrawable(indeterminateDrawable, mProgressTintInfo.mIndeterminateTintList, + mProgressTintInfo.mHasIndeterminateTintList, + mProgressTintInfo.mIndeterminateTintMode, + mProgressTintInfo.mHasIndeterminateTintMode); + } + } + + // Progress drawables in this library has already rewritten tint related methods for + // compatibility. + @SuppressLint("NewApi") + private void applyTintForDrawable(Drawable drawable, ColorStateList tintList, + boolean hasTintList, PorterDuff.Mode tintMode, + boolean hasTintMode) { + + if (hasTintList || hasTintMode) { + + if (hasTintList) { + if (drawable instanceof TintableDrawable) { + //noinspection RedundantCast + ((TintableDrawable) drawable).setTintList(tintList); + } else { + logDrawableTintWarning(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + drawable.setTintList(tintList); + } + } + } + + if (hasTintMode) { + if (drawable instanceof TintableDrawable) { + //noinspection RedundantCast + ((TintableDrawable) drawable).setTintMode(tintMode); + } else { + logDrawableTintWarning(); + if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { + drawable.setTintMode(tintMode); + } + } + } + + // The drawable (or one of its children) may not have been + // stateful before applying the tint, so let's try again. + if (drawable.isStateful()) { + drawable.setState(getDrawableState()); + } + } + } + + private void logDrawableTintWarning() { + Log.w(TAG, "Drawable did not implement TintableDrawable, it won't be tinted below" + + " Lollipop"); + } + + /** + * Get the listener that is listening for rating change events. + * + * @return The listener, may be null. + */ + public OnRatingChangeListener getOnRatingChangeListener() { + return mOnRatingChangeListener; + } + + /** + * Sets the listener to be called when the rating changes. + * + * @param listener The listener. + */ + public void setOnRatingChangeListener(OnRatingChangeListener listener) { + mOnRatingChangeListener = listener; + } + + @Override + public synchronized void setSecondaryProgress(int secondaryProgress) { + super.setSecondaryProgress(secondaryProgress); + + // HACK: Check and call our listener here because this method is always called by + // updateSecondaryProgress() from onProgressRefresh(). + float rating = getRating(); + if (mOnRatingChangeListener != null && rating != mLastKnownRating) { + mOnRatingChangeListener.onRatingChanged(this, rating); + } + mLastKnownRating = rating; + } + + /** + * A callback that notifies clients when the rating has been changed. This includes changes that + * were initiated by the user through a touch gesture or arrow key/trackball as well as changes + * that were initiated programmatically. This callback will be called + * continuously while the user is dragging, different from framework's + * {@link OnRatingBarChangeListener}. + */ + public interface OnRatingChangeListener { + + /** + * Notification that the rating has changed. This will be called + * continuously while the user is dragging, different from framework's + * {@link OnRatingBarChangeListener}. + * + * @param ratingBar The RatingBar whose rating has changed. + * @param rating The current rating. This will be in the range 0..numStars. + */ + void onRatingChanged(MaterialRatingBar ratingBar, float rating); + } + + private static class TintInfo { + + public ColorStateList mProgressTintList; + public PorterDuff.Mode mProgressTintMode; + public boolean mHasProgressTintList; + public boolean mHasProgressTintMode; + + public ColorStateList mSecondaryProgressTintList; + public PorterDuff.Mode mSecondaryProgressTintMode; + public boolean mHasSecondaryProgressTintList; + public boolean mHasSecondaryProgressTintMode; + + public ColorStateList mProgressBackgroundTintList; + public PorterDuff.Mode mProgressBackgroundTintMode; + public boolean mHasProgressBackgroundTintList; + public boolean mHasProgressBackgroundTintMode; + + public ColorStateList mIndeterminateTintList; + public PorterDuff.Mode mIndeterminateTintMode; + public boolean mHasIndeterminateTintList; + public boolean mHasIndeterminateTintMode; + } +} diff --git a/app/src/main/java/com/gh/common/view/materialratingbar/MaterialRatingDrawable.java b/app/src/main/java/com/gh/common/view/materialratingbar/MaterialRatingDrawable.java new file mode 100644 index 0000000000..6870d6459c --- /dev/null +++ b/app/src/main/java/com/gh/common/view/materialratingbar/MaterialRatingDrawable.java @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2016 Zhang Hai + * All Rights Reserved. + */ + +package com.gh.common.view.materialratingbar; + +import android.annotation.SuppressLint; +import android.content.Context; +import android.graphics.Color; +import android.graphics.drawable.ColorDrawable; +import android.graphics.drawable.Drawable; +import android.graphics.drawable.LayerDrawable; +import android.support.v7.content.res.AppCompatResources; +import android.view.Gravity; + +import com.gh.common.view.materialratingbar.internal.ThemeUtils; +import com.gh.gamecenter.R; + + +public class MaterialRatingDrawable extends LayerDrawable { + + public MaterialRatingDrawable(Context context, boolean fillBackgroundStars) { + super(new Drawable[]{ + createLayerDrawableWithTintAttrRes(fillBackgroundStars ? + R.drawable.ic_star + : R.drawable.ic_star_border, fillBackgroundStars ? + R.attr.colorControlHighlight : R.attr.colorControlNormal, context), + fillBackgroundStars ? createClippedLayerDrawableWithTintColor( + R.drawable.ic_star, Color.TRANSPARENT, context) + : createClippedLayerDrawableWithTintAttrRes( + R.drawable.ic_star_border, R.attr.colorControlActivated, + context), + createClippedLayerDrawableWithTintAttrRes(R.drawable.ic_star, + R.attr.colorControlActivated, context) + }); + + setId(0, android.R.id.background); + setId(1, android.R.id.secondaryProgress); + setId(2, android.R.id.progress); + } + + private static Drawable createLayerDrawableWithTintColor(int tileRes, int tintColor, + Context context) { + TileDrawable drawable = new TileDrawable(AppCompatResources.getDrawable(context, + tileRes)); + drawable.mutate(); + //noinspection RedundantCast + ((TintableDrawable) drawable).setTint(tintColor); + return drawable; + } + + private static Drawable createLayerDrawableWithTintAttrRes(int tileRes, int tintAttrRes, + Context context) { + int tintColor = ThemeUtils.getColorFromAttrRes(tintAttrRes, context); + return createLayerDrawableWithTintColor(tileRes, tintColor, context); + } + + @SuppressLint("RtlHardcoded") + private static Drawable createClippedLayerDrawableWithTintColor(int tileResId, int tintColor, + Context context) { + return new ClipDrawableCompat(createLayerDrawableWithTintColor(tileResId, tintColor, + context), Gravity.LEFT, ClipDrawableCompat.HORIZONTAL); + } + + @SuppressLint("RtlHardcoded") + private static Drawable createClippedLayerDrawableWithTintAttrRes(int tileResId, + int tintAttrRes, + Context context) { + return new ClipDrawableCompat(createLayerDrawableWithTintAttrRes(tileResId, tintAttrRes, + context), Gravity.LEFT, ClipDrawableCompat.HORIZONTAL); + } + + @SuppressLint("RtlHardcoded") + private static Drawable createClippedTransparentLayerDrawable() { + return new ClipDrawableCompat(new TileDrawable(new ColorDrawable(Color.TRANSPARENT)), + Gravity.LEFT, ClipDrawableCompat.HORIZONTAL); + } + + public float getTileRatio() { + Drawable drawable = getTileDrawableByLayerId(android.R.id.progress).getDrawable(); + return (float) drawable.getIntrinsicWidth() / drawable.getIntrinsicHeight(); + } + + public void setStarCount(int count) { + getTileDrawableByLayerId(android.R.id.background).setTileCount(count); + getTileDrawableByLayerId(android.R.id.secondaryProgress).setTileCount(count); + getTileDrawableByLayerId(android.R.id.progress).setTileCount(count); + } + + @SuppressLint("NewApi") + private TileDrawable getTileDrawableByLayerId(int id) { + Drawable layerDrawable = findDrawableByLayerId(id); + switch (id) { + case android.R.id.background: + return (TileDrawable) layerDrawable; + case android.R.id.secondaryProgress: + case android.R.id.progress: { + ClipDrawableCompat clipDrawable = (ClipDrawableCompat) layerDrawable; + return (TileDrawable) clipDrawable.getDrawable(); + } + default: + // Should never reach here. + throw new RuntimeException(); + } + } +} diff --git a/app/src/main/java/com/gh/common/view/materialratingbar/TileDrawable.java b/app/src/main/java/com/gh/common/view/materialratingbar/TileDrawable.java new file mode 100644 index 0000000000..b349fec756 --- /dev/null +++ b/app/src/main/java/com/gh/common/view/materialratingbar/TileDrawable.java @@ -0,0 +1,74 @@ +/* + * Copyright (c) 2016 Zhang Hai + * All Rights Reserved. + */ + +package com.gh.common.view.materialratingbar; + +import android.graphics.Canvas; +import android.graphics.ColorFilter; +import android.graphics.drawable.Drawable; +import android.support.annotation.NonNull; + +class TileDrawable extends BaseDrawable { + + private Drawable mDrawable; + private int mTileCount = -1; + + public TileDrawable(Drawable drawable) { + mDrawable = drawable; + } + + public Drawable getDrawable() { + return mDrawable; + } + + public int getTileCount() { + return mTileCount; + } + + public void setTileCount(int tileCount) { + mTileCount = tileCount; + invalidateSelf(); + } + + @NonNull + @Override + public Drawable mutate() { + mDrawable = mDrawable.mutate(); + return this; + } + + @Override + protected void onDraw(Canvas canvas, int width, int height) { + + mDrawable.setAlpha(mAlpha); + ColorFilter colorFilter = getColorFilterForDrawing(); + if (colorFilter != null) { + mDrawable.setColorFilter(colorFilter); + } + + int tileHeight = mDrawable.getIntrinsicHeight(); + float scale = (float) height / tileHeight; + canvas.scale(scale, scale); + + float scaledWidth = width / scale; + if (mTileCount < 0) { + int tileWidth = mDrawable.getIntrinsicWidth(); + for (int x = 0; x < scaledWidth; x += tileWidth) { + mDrawable.setBounds(x, 0, x + tileWidth, tileHeight); + mDrawable.draw(canvas); + } + } else { + float tileWidth = scaledWidth / mTileCount; + for (int i = 0; i < mTileCount; ++i) { + int drawableWidth = mDrawable.getIntrinsicWidth(); + float tileCenter = tileWidth * (i + 0.5f); + float drawableWidthHalf = (float) drawableWidth / 2; + mDrawable.setBounds(Math.round(tileCenter - drawableWidthHalf), 0, + Math.round(tileCenter + drawableWidthHalf), tileHeight); + mDrawable.draw(canvas); + } + } + } +} diff --git a/app/src/main/java/com/gh/common/view/materialratingbar/TintableDrawable.java b/app/src/main/java/com/gh/common/view/materialratingbar/TintableDrawable.java new file mode 100644 index 0000000000..c552fc0c9e --- /dev/null +++ b/app/src/main/java/com/gh/common/view/materialratingbar/TintableDrawable.java @@ -0,0 +1,78 @@ +/* + * Copyright (c) 2016 Zhang Hai + * All Rights Reserved. + */ + +package com.gh.common.view.materialratingbar; + +import android.content.res.ColorStateList; +import android.graphics.ColorFilter; +import android.graphics.PorterDuff; +import android.graphics.drawable.Drawable; +import android.support.annotation.ColorInt; +import android.support.annotation.NonNull; +import android.support.annotation.Nullable; + +/** + * A {@code Drawable} that is tintable. + */ +public interface TintableDrawable { + + /** + * Specifies tint color for this drawable. + *

+ * A Drawable's drawing content will be blended together with its tint + * before it is drawn to the screen. This functions similarly to + * {@link Drawable#setColorFilter(int, PorterDuff.Mode)}. + *

+ *

+ * To clear the tint, pass {@code null} to + * {@link #setTintList(ColorStateList)}. + *

+ *

Note: Setting a color filter via + * {@link Drawable#setColorFilter(ColorFilter)} or + * {@link Drawable#setColorFilter(int, PorterDuff.Mode)} overrides tint. + *

+ * + * @param tintColor Color to use for tinting this drawable + * @see #setTintList(ColorStateList) + * @see #setTintMode(PorterDuff.Mode) + */ + void setTint(@ColorInt int tintColor); + + /** + * Specifies tint color for this drawable as a color state list. + *

+ * A Drawable's drawing content will be blended together with its tint + * before it is drawn to the screen. This functions similarly to + * {@link Drawable#setColorFilter(int, PorterDuff.Mode)}. + *

+ *

Note: Setting a color filter via + * {@link Drawable#setColorFilter(ColorFilter)} or + * {@link Drawable#setColorFilter(int, PorterDuff.Mode)} overrides tint. + *

+ * + * @param tint Color state list to use for tinting this drawable, or + * {@code null} to clear the tint + * @see #setTint(int) + * @see #setTintMode(PorterDuff.Mode) + */ + void setTintList(@Nullable ColorStateList tint); + + /** + * Specifies a tint blending mode for this drawable. + *

+ * Defines how this drawable's tint color should be blended into the drawable + * before it is drawn to screen. Default tint mode is {@link PorterDuff.Mode#SRC_IN}. + *

+ *

Note: Setting a color filter via + * {@link Drawable#setColorFilter(ColorFilter)} or + * {@link Drawable#setColorFilter(int, PorterDuff.Mode)} overrides tint. + *

+ * + * @param tintMode A Porter-Duff blending mode + * @see #setTint(int) + * @see #setTintList(ColorStateList) + */ + void setTintMode(@NonNull PorterDuff.Mode tintMode); +} diff --git a/app/src/main/java/com/gh/common/view/materialratingbar/internal/DrawableCompat.java b/app/src/main/java/com/gh/common/view/materialratingbar/internal/DrawableCompat.java new file mode 100644 index 0000000000..bbc1f3a66b --- /dev/null +++ b/app/src/main/java/com/gh/common/view/materialratingbar/internal/DrawableCompat.java @@ -0,0 +1,26 @@ +/* + * Copyright (c) 2016 Zhang Hai + * All Rights Reserved. + */ + +package com.gh.common.view.materialratingbar.internal; + +import android.graphics.PorterDuff; + +public class DrawableCompat { + + /** + * Parses a {@link PorterDuff.Mode} from a tintMode attribute's enum value. + */ + public static PorterDuff.Mode parseTintMode(int value, PorterDuff.Mode defaultMode) { + switch (value) { + case 3: return PorterDuff.Mode.SRC_OVER; + case 5: return PorterDuff.Mode.SRC_IN; + case 9: return PorterDuff.Mode.SRC_ATOP; + case 14: return PorterDuff.Mode.MULTIPLY; + case 15: return PorterDuff.Mode.SCREEN; + case 16: return PorterDuff.Mode.ADD; + default: return defaultMode; + } + } +} diff --git a/app/src/main/java/com/gh/common/view/materialratingbar/internal/ThemeUtils.java b/app/src/main/java/com/gh/common/view/materialratingbar/internal/ThemeUtils.java new file mode 100644 index 0000000000..25a7770a2f --- /dev/null +++ b/app/src/main/java/com/gh/common/view/materialratingbar/internal/ThemeUtils.java @@ -0,0 +1,32 @@ +/* + * Copyright (c) 2016 Zhang Hai + * All Rights Reserved. + */ + +package com.gh.common.view.materialratingbar.internal; + +import android.content.Context; +import android.content.res.TypedArray; + +public class ThemeUtils { + + private ThemeUtils() {} + + public static int getColorFromAttrRes(int attrRes, Context context) { + TypedArray a = context.obtainStyledAttributes(new int[] { attrRes }); + try { + return a.getColor(0, 0); + } finally { + a.recycle(); + } + } + + public static float getFloatFromAttrRes(int attrRes, Context context) { + TypedArray a = context.obtainStyledAttributes(new int[] { attrRes }); + try { + return a.getFloat(0, 0); + } finally { + a.recycle(); + } + } +} diff --git a/app/src/main/res/drawable/ic_star.xml b/app/src/main/res/drawable/ic_star.xml new file mode 100644 index 0000000000..4de8f9ba9b --- /dev/null +++ b/app/src/main/res/drawable/ic_star.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/drawable/ic_star_border.xml b/app/src/main/res/drawable/ic_star_border.xml new file mode 100644 index 0000000000..a8a5fbe156 --- /dev/null +++ b/app/src/main/res/drawable/ic_star_border.xml @@ -0,0 +1,9 @@ + + + diff --git a/app/src/main/res/layout/temp_ratingbar.xml b/app/src/main/res/layout/temp_ratingbar.xml new file mode 100644 index 0000000000..9db41b14b8 --- /dev/null +++ b/app/src/main/res/layout/temp_ratingbar.xml @@ -0,0 +1,42 @@ + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/attrs.xml b/app/src/main/res/values/attrs.xml index 46a07db7d5..388714ce63 100644 --- a/app/src/main/res/values/attrs.xml +++ b/app/src/main/res/values/attrs.xml @@ -26,4 +26,44 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/app/src/main/res/values/styles.xml b/app/src/main/res/values/styles.xml index 8c79bea10b..9b9925add2 100644 --- a/app/src/main/res/values/styles.xml +++ b/app/src/main/res/values/styles.xml @@ -168,4 +168,16 @@ @color/black + + + \ No newline at end of file