package com.wangdaye.mysplash.common.utils.helper; import android.animation.Animator; import android.animation.AnimatorListenerAdapter; import android.animation.ObjectAnimator; import android.animation.ValueAnimator; import android.content.Context; import android.graphics.Bitmap; import android.graphics.Color; import android.graphics.ColorMatrixColorFilter; import android.os.Build; import android.support.annotation.Nullable; import android.support.v7.widget.RecyclerView; import android.text.TextUtils; import android.widget.ImageView; import com.bumptech.glide.DrawableRequestBuilder; import com.bumptech.glide.Glide; import com.bumptech.glide.Priority; import com.bumptech.glide.load.engine.DiskCacheStrategy; import com.bumptech.glide.load.resource.bitmap.BitmapTransformation; import com.bumptech.glide.load.resource.drawable.GlideDrawable; import com.bumptech.glide.request.RequestListener; import com.bumptech.glide.request.animation.ViewPropertyAnimation; import com.bumptech.glide.request.target.Target; import com.wangdaye.mysplash.R; import com.wangdaye.mysplash.common.data.entity.unsplash.Collection; import com.wangdaye.mysplash.common.data.entity.unsplash.Photo; import com.wangdaye.mysplash.common.data.entity.unsplash.User; import com.wangdaye.mysplash.common.utils.AnimUtils; import com.wangdaye.mysplash.common.utils.manager.SettingsOptionManager; import com.wangdaye.mysplash.common.utils.manager.ThemeManager; import com.wangdaye.mysplash.common.utils.widget.glide.CircleTransformation; import com.wangdaye.mysplash.common.utils.widget.glide.FadeAnimator; import org.greenrobot.greendao.annotation.NotNull; import java.io.File; import java.util.regex.Pattern; /** * Image helper. * * A helper class that makes operations of {@link Glide} easier. * * */ public class ImageHelper { // photo. public static void loadRegularPhoto(Context context, final ImageView view, Photo photo, @Nullable OnLoadImageListener l) { if (photo != null && photo.urls != null && photo.width != 0 && photo.height != 0) { DrawableRequestBuilder<String> thumbnailRequest = Glide .with(context) .load(photo.urls.thumb) .diskCacheStrategy(DiskCacheStrategy.SOURCE) .listener(new RequestListener<String, GlideDrawable>() { @Override public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) { return false; } @Override public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { view.setEnabled(true); return false; } }); if (l != null && !photo.hasFadedIn && Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { AnimUtils.ObservableColorMatrix matrix = new AnimUtils.ObservableColorMatrix(); matrix.setSaturation(0); view.setColorFilter(new ColorMatrixColorFilter(matrix)); view.setEnabled(false); } loadImage( context, view, photo.urls.regular, photo.getRegularWidth(), photo.getRegularHeight(), false, false, l == null ? null : thumbnailRequest, null, l == null ? null : new FadeAnimator(), l); } } public static void loadFullPhoto(Context context, ImageView view, String url, String thumbnail, @Nullable OnLoadImageListener l) { DrawableRequestBuilder<String> thumbnailRequest = Glide .with(context) .load(thumbnail) .diskCacheStrategy(DiskCacheStrategy.SOURCE); loadImage(context, view, url, 0, 0, false, false, thumbnailRequest, null, null, l); } public static void loadPhoto(Context context, ImageView view, String url, boolean lowPriority, @Nullable OnLoadImageListener l) { loadImage(context, view, url, 0, 0, false, lowPriority, null, null, null, l); } // collection cover. public static void loadCollectionCover(Context context, ImageView view, Collection collection, @Nullable OnLoadImageListener l) { if (collection != null) { loadRegularPhoto(context, view, collection.cover_photo, l); } } // avatar. public static void loadAvatar(Context context, ImageView view, User user, @Nullable OnLoadImageListener l) { if (user != null && user.profile_image != null) { loadAvatar(context, view, user.profile_image.large, l); } else { loadImage( context, view, R.drawable.default_avatar, 128, 128, false, new CircleTransformation(context), l); } } public static void loadAvatar(Context context, ImageView view, @NotNull String url, @Nullable OnLoadImageListener l) { DrawableRequestBuilder<Integer> thumbnailRequest = Glide.with(context) .load(R.drawable.default_avatar) .override(128, 128) .transform(new CircleTransformation(context)) .diskCacheStrategy(DiskCacheStrategy.SOURCE); loadImage( context, view, url, 128, 128, false, false, thumbnailRequest, new CircleTransformation(context), null, l); } // icon. public static void loadIcon(Context context, ImageView view, int resId) { loadImage(context, view, resId, 0, 0, true, null, null); } // builder. private static void loadImage(Context context, ImageView view, String url, int width, int height, boolean dontAnimate, boolean lowPriority, @Nullable DrawableRequestBuilder thumbnail, @Nullable BitmapTransformation transformation, @Nullable ViewPropertyAnimation.Animator animator, @Nullable final OnLoadImageListener l) { DrawableRequestBuilder<String> builder = Glide .with(context) .load(url) .diskCacheStrategy(DiskCacheStrategy.SOURCE) .listener(new RequestListener<String, GlideDrawable>() { @Override public boolean onException(Exception e, String model, Target<GlideDrawable> target, boolean isFirstResource) { if (l != null) { l.onLoadFailed(); } return false; } @Override public boolean onResourceReady(GlideDrawable resource, String model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { if (l != null) { l.onLoadSucceed(); } return false; } }); if (width != 0 && height != 0) { builder.override(width, height); } if (dontAnimate) { builder.dontAnimate(); } if (lowPriority) { builder.priority(Priority.LOW); } else { builder.priority(Priority.NORMAL); } if (thumbnail != null) { builder.thumbnail(thumbnail); } if (transformation != null) { builder.transform(transformation); } if (animator != null) { builder.animate(animator); } builder.into(view); } private static void loadImage(Context context, ImageView view, int resId, int width, int height, boolean dontAnimate, @Nullable BitmapTransformation transformation, @Nullable final OnLoadImageListener l) { DrawableRequestBuilder<Integer> builder = Glide .with(context) .load(resId) .diskCacheStrategy(DiskCacheStrategy.SOURCE) .listener(new RequestListener<Integer, GlideDrawable>() { @Override public boolean onException(Exception e, Integer model, Target<GlideDrawable> target, boolean isFirstResource) { if (l != null) { l.onLoadFailed(); } return false; } @Override public boolean onResourceReady(GlideDrawable resource, Integer model, Target<GlideDrawable> target, boolean isFromMemoryCache, boolean isFirstResource) { if (l != null) { l.onLoadSucceed(); } return false; } }); if (width != 0 && height != 0) { builder.override(width, height); } if (dontAnimate) { builder.dontAnimate(); } if (transformation != null) { builder.transform(transformation); } builder.into(view); } public static void loadBitmap(Context context, Target<Bitmap> target, String url, boolean clipWithCircle) { if (clipWithCircle) { Glide.with(context) .load(url) .asBitmap() .diskCacheStrategy(DiskCacheStrategy.SOURCE) .transform(new CircleTransformation(context)) .into(target); } else { Glide.with(context) .load(url) .asBitmap() .diskCacheStrategy(DiskCacheStrategy.SOURCE) .into(target); } } public static void loadBitmap(Context context, Target<Bitmap> target, File file) { Glide.with(context) .load(file) .asBitmap() .into(target); } public static void loadImage(Context context, ImageView view, File file) { if (file.exists()) { Glide.with(context) .load(file) .diskCacheStrategy(DiskCacheStrategy.NONE) .into(view); } } // animation. /** * Execute a saturation animation to make a image from white and black into color. * * @param c Context. * @param target ImageView which will execute saturation animation. * */ public static void startSaturationAnimation(Context c, final ImageView target) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) { target.setHasTransientState(true); final AnimUtils.ObservableColorMatrix matrix = new AnimUtils.ObservableColorMatrix(); final ObjectAnimator saturation = ObjectAnimator.ofFloat( matrix, AnimUtils.ObservableColorMatrix.SATURATION, 0f, 1f); saturation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener () { @Override public void onAnimationUpdate(ValueAnimator valueAnimator) { target.setColorFilter(new ColorMatrixColorFilter(matrix)); } }); saturation.setDuration( SettingsOptionManager.getInstance(c).getSaturationAnimationDuration()); saturation.setInterpolator(AnimUtils.getFastOutSlowInInterpolator(c)); saturation.addListener(new AnimatorListenerAdapter() { @Override public void onAnimationEnd(Animator animation) { target.clearColorFilter(); target.setHasTransientState(false); } }); saturation.start(); } } /** <br> data. */ /** * Compute the background color for item view in photo list or collection list. * * @param context Context. * @param color A string that can be converted to a color without "#". For example, "000000". * */ public static int computeCardBackgroundColor(Context context, String color) { if (TextUtils.isEmpty(color) || (!Pattern.compile("^#[a-fA-F0-9]{6}").matcher(color).matches() && !Pattern.compile("^[a-fA-F0-9]{6}").matcher(color).matches())) { return Color.argb(0, 0, 0, 0); } else { if (Pattern.compile("^[a-fA-F0-9]{6}").matcher(color).matches()) { color = "#" + color; } int backgroundColor = Color.parseColor(color); int red = ((backgroundColor & 0x00FF0000) >> 16); int green = ((backgroundColor & 0x0000FF00) >> 8); int blue = (backgroundColor & 0x000000FF); if (ThemeManager.getInstance(context).isLightTheme()) { return Color.rgb( (int) (red + (255 - red) * 0.7), (int) (green + (255 - green) * 0.7), (int) (blue + (255 - blue) * 0.7)); } else { return Color.rgb( (int) (red * 0.3), (int) (green * 0.3), (int) (blue * 0.3)); } } } /** * Release the {@link ImageView} from {@link Glide}. * A ViewHolder in {@link android.support.v7.widget.RecyclerView} need to call this method in * {@link android.support.v7.widget.RecyclerView.Adapter#onViewRecycled(RecyclerView.ViewHolder)}. * Otherwise, there might be a OOM problem. * * @param view The ImageView to be released. * */ public static void releaseImageView(ImageView view) { Glide.clear(view); } // interface. // on load image listener. public interface OnLoadImageListener { void onLoadSucceed(); void onLoadFailed(); } }