Android Open Source - wally Palette Loader






From Project

Back to project page wally.

License

The source code is released under:

Apache License

If you think the Android project wally listed in this page is inappropriate, such as containing malicious code/tools or violating the copyright, please email info at java2s dot com, thanks.

Java Source Code

/*
 * Copyright (C) 2014 Freddie (Musenkishi) Lust-Hed
 */*from  w  w  w .  ja  v a 2s . c o m*/
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.musenkishi.wally.util;

import android.animation.ArgbEvaluator;
import android.animation.ValueAnimator;
import android.content.Context;
import android.graphics.Bitmap;
import android.graphics.Color;
import android.graphics.PorterDuff;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.Drawable;
import android.graphics.drawable.TransitionDrawable;
import android.os.Build;
import android.os.Handler;
import android.os.HandlerThread;
import android.os.Message;
import android.support.v7.graphics.Palette;
import android.util.Pair;
import android.view.View;
import android.widget.ImageView;
import android.widget.TextView;

import org.apache.commons.collections4.map.LRUMap;

import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;

/**
 * A class that can apply colors to Views lazily.
 * Can be used in ListViews.
 *
 * Created by Freddie (Musenkishi) Lust-Hed on 2014-10-21.
 */
public class PaletteLoader {

    private static final int MSG_RENDER_PALETTE = 4194;
    private static final int MSG_DISPLAY_PALETTE = 4195;

    private static final int MAX_ITEMS_IN_CACHE = 100;
    private static final int MAX_CONCURRENT_THREADS = 5;

    private static int TRUE = 1;
    private static int FALSE = 0;

    private static Handler uiHandler;
    private static Handler backgroundHandler;

    private static Map<String, Palette> colorSchemeCache;
    private static ExecutorService executorService = Executors.newFixedThreadPool(MAX_CONCURRENT_THREADS);

    private PaletteLoader(){
        //Don't use
    }

    public static PaletteBuilder with(Context context, String id) {
        if (colorSchemeCache == null){
            colorSchemeCache = Collections.synchronizedMap(
                    new LRUMap<String, Palette>(MAX_ITEMS_IN_CACHE)
            );
        }
        if (uiHandler == null || backgroundHandler == null) {
            setupHandlers(context);
        }
        return new PaletteBuilder(id);
    }

    private static void setupHandlers(Context context) {
        HandlerThread handlerThread = new HandlerThread("palette-loader-background");
        handlerThread.start();
        backgroundHandler = new Handler(handlerThread.getLooper(), sCallback);
        uiHandler = new Handler(context.getMainLooper(), sCallback);
    }

    private static Handler.Callback sCallback = new Handler.Callback() {
        @Override
        public boolean handleMessage(Message message) {

            switch (message.what) {

                case MSG_RENDER_PALETTE:
                    Pair<Bitmap, PaletteTarget> pair = (Pair<Bitmap, PaletteTarget>) message.obj;
                    if (pair != null && !pair.first.isRecycled()) {
                        Palette palette = Palette.generate(pair.first);

                        colorSchemeCache.put(pair.second.getId(), palette);

                        Message uiMessage = uiHandler.obtainMessage();
                        uiMessage.what = MSG_DISPLAY_PALETTE;
                        uiMessage.obj = new Pair<Palette, PaletteTarget>(palette, pair.second);
                        uiMessage.arg1 = FALSE;

                        uiHandler.sendMessage(uiMessage);
                    }
                    break;

                case MSG_DISPLAY_PALETTE:
                    Pair<Palette, PaletteTarget> pairDisplay = (Pair<Palette, PaletteTarget>) message.obj;
                    boolean fromCache = message.arg1 == TRUE;
                    applyColorToView(pairDisplay.second, pairDisplay.first, fromCache);

                    break;

            }

            return false;
        }
    };

    public static class PaletteBuilder {

        private String id;
        private Bitmap bitmap;
        private boolean maskDrawable;
        private int fallbackColor = Color.TRANSPARENT;
        private PaletteRequest paletteRequest = new PaletteRequest(PaletteRequest.SwatchType.REGULAR_VIBRANT, PaletteRequest.SwatchColor.BACKGROUND);
        private Palette palette;

        public PaletteBuilder(String id) {
            this.id = id;
        }

        public PaletteBuilder load(Bitmap bitmap) {
            this.bitmap = bitmap;
            return this;
        }

        public PaletteBuilder load(Palette colorScheme) {
            this.palette = colorScheme;
            return this;
        }

        public PaletteBuilder mask() {
            maskDrawable = true;
            return this;
        }

        public PaletteBuilder fallbackColor(int fallbackColor) {
            this.fallbackColor = fallbackColor;
            return this;
        }

        public PaletteBuilder setPaletteRequest(PaletteRequest paletteRequest) {
            this.paletteRequest = paletteRequest;
            return this;
        }

        public void into(View view) {
            final PaletteTarget paletteTarget = new PaletteTarget(id, paletteRequest, view, maskDrawable, fallbackColor);
            if (palette != null) {
                colorSchemeCache.put(paletteTarget.getId(), palette);
                applyColorToView(paletteTarget, palette, false);
            } else {
                if (colorSchemeCache.get(id) != null) {
                    Palette palette = colorSchemeCache.get(id);
                    applyColorToView(paletteTarget, palette, true);
                } else {
                    if (Build.VERSION.SDK_INT >= 21) {
                        executorService.submit(new PaletteRenderer(bitmap, paletteTarget));
                    } else {
                        Message bgMessage = backgroundHandler.obtainMessage();
                        bgMessage.what = MSG_RENDER_PALETTE;
                        bgMessage.obj = new Pair<Bitmap, PaletteTarget>(bitmap, paletteTarget);
                        backgroundHandler.sendMessage(bgMessage);
                    }
                }
            }
        }
    }

    private static void applyColorToView(PaletteTarget target, Palette palette, boolean fromCache) {
        if (!isViewRecycled(target)) {
            applyColorToView(target, target.getPaletteRequest().getColor(palette), fromCache);
        }
    }

    private static void applyColorToView(final PaletteTarget target, int color, boolean fromCache) {
        if (target.getView() instanceof TextView) {
            applyColorToView((TextView) target.getView(), color, fromCache);
            return;
        }
        if (fromCache) {
            if (target.getView() instanceof ImageView && target.shouldMaskDrawable()) {
                ((ImageView) target.getView()).getDrawable().mutate()
                        .setColorFilter(color, PorterDuff.Mode.MULTIPLY);
            } else {
                target.getView().setBackgroundColor(color);
            }
        } else {
            if (target.getView() instanceof ImageView && target.shouldMaskDrawable()) {
                Integer colorFrom;
                ValueAnimator.AnimatorUpdateListener imageAnimatorUpdateListener = new ValueAnimator.AnimatorUpdateListener() {
                    @Override
                    public void onAnimationUpdate(ValueAnimator valueAnimator) {
                        ((ImageView) target.getView()).getDrawable().mutate()
                                .setColorFilter((Integer) valueAnimator
                                        .getAnimatedValue(), PorterDuff.Mode.MULTIPLY);
                    }
                };
                ValueAnimator.AnimatorUpdateListener animatorUpdateListener;

                PaletteTag paletteTag = (PaletteTag) target.getView().getTag();
                animatorUpdateListener = imageAnimatorUpdateListener;
                colorFrom = paletteTag.getColor();
                target.getView().setTag(new PaletteTag(paletteTag.getId(), color));

                Integer colorTo = color;
                ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
                colorAnimation.addUpdateListener(animatorUpdateListener);
                colorAnimation.setDuration(300);
                colorAnimation.start();
            } else {

                Drawable preDrawable;

                if (target.getView().getBackground() == null) {
                    preDrawable = new ColorDrawable(Color.TRANSPARENT);
                } else {
                    preDrawable = target.getView().getBackground();
                }

                TransitionDrawable transitionDrawable = new TransitionDrawable(new Drawable[]{
                        preDrawable,
                        new ColorDrawable(color)
                });
                if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                    target.getView().setBackground(transitionDrawable);
                } else {
                    target.getView().setBackgroundDrawable(transitionDrawable);
                }
                transitionDrawable.startTransition(300);
            }
        }
    }

    private static void applyColorToView(final TextView textView, int color, boolean fromCache) {
        if (fromCache) {
            textView.setTextColor(color);
        } else {
            Integer colorFrom = textView.getCurrentTextColor();
            Integer colorTo = color;
            ValueAnimator colorAnimation = ValueAnimator.ofObject(new ArgbEvaluator(), colorFrom, colorTo);
            colorAnimation.addUpdateListener(new ValueAnimator.AnimatorUpdateListener() {
                @Override
                public void onAnimationUpdate(ValueAnimator animator) {
                    textView.setTextColor((Integer) animator.getAnimatedValue());
                }
            });
            colorAnimation.start();
        }
    }

    /**
     * Is it null? Is that null? What about that one? Is that null too? What about this one?
     * And this one? Is this null? Is null even null? How can null be real if our eyes aren't real?
     *
     * <p>Checks whether the view has been recycled or not.</p>
     *
     * @param target A {@link com.musenkishi.wally.util.PaletteLoader.PaletteTarget} to check
     * @return true is view has been recycled, otherwise false.
     */
    private static boolean isViewRecycled(PaletteTarget target) {

        if (target != null && target.getId() != null && target.getView() != null
                && target.getView().getTag() != null) {
            if (target.getView().getTag() instanceof PaletteTag) {
                return !target.getId().equals(((PaletteTag) target.getView().getTag()).getId());
            } else {
                throw new NoPaletteTagFoundException("PaletteLoader couldn't determine whether" +
                        " a View has been reused or not. PaletteLoader uses View.setTag() and " +
                        "View.getTag() for keeping check if a View has been reused and it's " +
                        "recommended to refrain from setting tags to Views PaletteLoader is using."
                );
            }
        } else {
            return false;
        }
    }

    private static class PaletteRenderer implements Runnable {

        private Bitmap bitmap;
        private PaletteTarget paletteTarget;

        private PaletteRenderer(Bitmap bitmap, PaletteTarget paletteTarget) {
            this.bitmap = bitmap;
            this.paletteTarget = paletteTarget;
        }

        @Override
        public void run() {
            if (bitmap != null && !bitmap.isRecycled()) {
                Palette colorScheme = Palette.generate(bitmap);
                colorSchemeCache.put(paletteTarget.getId(), colorScheme);

                PalettePresenter palettePresenter = new PalettePresenter(
                        paletteTarget,
                        colorScheme,
                        false
                );
                uiHandler.post(palettePresenter);
            }
        }
    }

    private static class PalettePresenter implements Runnable {

        private PaletteTarget paletteTarget;
        private Palette palette;
        private boolean fromCache;

        private PalettePresenter(PaletteTarget paletteTarget, Palette palette, boolean fromCache) {
            this.paletteTarget = paletteTarget;
            this.palette = palette;
            this.fromCache = fromCache;
        }

        @Override
        public void run() {
            applyColorToView(paletteTarget, palette, fromCache);
        }
    }

    private static class PaletteTarget {
        private String id;
        private PaletteRequest paletteRequest;
        private View view;
        private boolean maskDrawable;

        private PaletteTarget(String id, PaletteRequest paletteRequest, View view, boolean maskDrawable, int fallbackColor) {
            this.id = id;
            this.paletteRequest = paletteRequest;
            this.view = view;
            this.view.setTag(new PaletteTag(this.id, fallbackColor));
            this.maskDrawable = maskDrawable;
        }

        public String getId() {
            return id;
        }

        public PaletteRequest getPaletteRequest() {
            return paletteRequest;
        }

        public View getView() {
            return view;
        }

        public boolean shouldMaskDrawable() {
            return maskDrawable;
        }
    }

    private static class PaletteTag {
        private String id;
        private Integer color;

        private PaletteTag(String id, Integer color) {
            this.id = id;
            this.color = color;
        }

        public String getId() {
            return id;
        }

        public void setId(String id) {
            this.id = id;
        }

        public Integer getColor() {
            return color;
        }

        public void setColor(Integer color) {
            this.color = color;
        }
    }

    public static class NoPaletteTagFoundException extends NullPointerException {
        public NoPaletteTagFoundException() { super(); }
        public NoPaletteTagFoundException(String message) { super(message); }
    }

}




Java Source Code List

com.musenkishi.wally.activities.ImageDetailsActivity.java
com.musenkishi.wally.activities.MainActivity.java
com.musenkishi.wally.adapters.RecyclerImagesAdapter.java
com.musenkishi.wally.adapters.RecyclerSavedImagesAdapter.java
com.musenkishi.wally.adapters.SmartFragmentPagerAdapter.java
com.musenkishi.wally.adapters.SmartFragmentStatePagerAdapter.java
com.musenkishi.wally.anim.BaseItemAnimator.java
com.musenkishi.wally.anim.ScaleInOutItemAnimator.java
com.musenkishi.wally.anim.interpolator.EaseInOutBezierInterpolator.java
com.musenkishi.wally.anim.interpolator.FastOutLinearInInterpolator.java
com.musenkishi.wally.anim.interpolator.FastOutSlowInInterpolator.java
com.musenkishi.wally.anim.interpolator.LinearOutSlowInInterpolator.java
com.musenkishi.wally.base.BaseActivity.java
com.musenkishi.wally.base.BaseFragment.java
com.musenkishi.wally.base.GridFragment.java
com.musenkishi.wally.base.WallyApplication.java
com.musenkishi.wally.dataprovider.DataProvider.java
com.musenkishi.wally.dataprovider.FileManager.java
com.musenkishi.wally.dataprovider.NetworkDataProvider.java
com.musenkishi.wally.dataprovider.SharedPreferencesDataProvider.java
com.musenkishi.wally.dataprovider.models.DataProviderError.java
com.musenkishi.wally.dataprovider.models.SaveImageRequest.java
com.musenkishi.wally.dataprovider.okhttp.OkHttpStreamFetcher.java
com.musenkishi.wally.dataprovider.okhttp.OkHttpUrlLoader.java
com.musenkishi.wally.dataprovider.util.Parser.java
com.musenkishi.wally.fragments.CustomResolutionDialogFragment.java
com.musenkishi.wally.fragments.FilterDialogFragment.java
com.musenkishi.wally.fragments.ImageZoomFragment.java
com.musenkishi.wally.fragments.LatestFragment.java
com.musenkishi.wally.fragments.MaterialDialogFragment.java
com.musenkishi.wally.fragments.RandomImagesFragment.java
com.musenkishi.wally.fragments.SavedImagesFragment.java
com.musenkishi.wally.fragments.SearchFragment.java
com.musenkishi.wally.fragments.ToplistFragment.java
com.musenkishi.wally.models.Author.java
com.musenkishi.wally.models.ExceptionReporter.java
com.musenkishi.wally.models.Filter.java
com.musenkishi.wally.models.ImagePage.java
com.musenkishi.wally.models.Image.java
com.musenkishi.wally.models.ListFilterGroup.java
com.musenkishi.wally.models.Rating.java
com.musenkishi.wally.models.SavedImageData.java
com.musenkishi.wally.models.Size.java
com.musenkishi.wally.models.Tag.java
com.musenkishi.wally.models.filters.FilterAspectRatioKeys.java
com.musenkishi.wally.models.filters.FilterBoardsKeys.java
com.musenkishi.wally.models.filters.FilterBoards.java
com.musenkishi.wally.models.filters.FilterGroup.java
com.musenkishi.wally.models.filters.FilterGroupsStructure.java
com.musenkishi.wally.models.filters.FilterPurityKeys.java
com.musenkishi.wally.models.filters.FilterPurity.java
com.musenkishi.wally.models.filters.FilterResOptKeys.java
com.musenkishi.wally.models.filters.FilterResOpt.java
com.musenkishi.wally.models.filters.FilterResolutionKeys.java
com.musenkishi.wally.models.filters.FilterTimeSpanKeys.java
com.musenkishi.wally.muzei.WallyArtSource.java
com.musenkishi.wally.notification.NotificationProvider.java
com.musenkishi.wally.observers.FileChangeReceiver.java
com.musenkishi.wally.observers.FiltersChangeReceiver.java
com.musenkishi.wally.util.Blur.java
com.musenkishi.wally.util.PaletteLoader.java
com.musenkishi.wally.util.PaletteRequest.java
com.musenkishi.wally.util.SparseBooleanArrayParcelable.java
com.musenkishi.wally.util.TextClickableSpan.java
com.musenkishi.wally.util.TextLinkBuilder.java
com.musenkishi.wally.util.TypefaceSpan.java
com.musenkishi.wally.views.AutoGridView.java
com.musenkishi.wally.views.GridRecyclerView.java
com.musenkishi.wally.views.ObservableScrollView.java
com.musenkishi.wally.views.TabBarView.java
com.musenkishi.wally.views.TabView.java
com.musenkishi.wally.views.swipeclearlayout.SwipeClearLayout.java
net.margaritov.preference.colorpicker.dialog.ColorPickerDialogFragment.java
net.margaritov.preference.colorpicker.drawable.AlphaPatternDrawable.java
net.margaritov.preference.colorpicker.preference.ColorPickerPreference.java
net.margaritov.preference.colorpicker.view.ColorPanelView.java
net.margaritov.preference.colorpicker.view.ColorPickerView.java
nl.codesoup.cubicbezier.CubicBezierInterpolator.java