Android Open Source - wally Swipe Clear Layout






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  .  j a v a2 s.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.views.swipeclearlayout;

import android.animation.Animator;
import android.annotation.SuppressLint;
import android.content.Context;
import android.content.res.Resources;
import android.content.res.TypedArray;
import android.graphics.Color;
import android.graphics.drawable.ClipDrawable;
import android.graphics.drawable.ColorDrawable;
import android.graphics.drawable.GradientDrawable;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.v4.view.ViewCompat;
import android.util.AttributeSet;
import android.util.DisplayMetrics;
import android.view.MotionEvent;
import android.view.View;
import android.view.ViewConfiguration;
import android.view.animation.AccelerateInterpolator;
import android.view.animation.Animation;
import android.view.animation.Animation.AnimationListener;
import android.view.animation.DecelerateInterpolator;
import android.view.animation.Transformation;
import android.widget.AbsListView;
import android.widget.ImageView;
import android.widget.ProgressBar;
import android.widget.RelativeLayout;

import com.musenkishi.wally.R;

/**
 * The SwipeRefreshLayout should be used whenever the user can refresh the
 * contents of a view via a vertical swipe gesture. The activity that
 * instantiates this view should add an OnRefreshListener to be notified
 * whenever the swipe to refresh gesture is completed. The SwipeRefreshLayout
 * will notify the listener each and every time the gesture is completed again;
 * the listener is responsible for correctly determining when to actually
 * initiate a refresh of its content. If the listener determines there should
 * not be a refresh, it must call setRefreshing(false) to cancel any visual
 * indication of a refresh. If an activity wishes to show just the progress
 * animation, it should call setRefreshing(true). To disable the gesture and
 * progress animation, call setEnabled(false) on the view.
 * <p>
 * This layout should be made the parent of the view that will be refreshed as a
 * result of the gesture and can only support one direct child. This view will
 * also be made the target of the gesture and will be forced to match both the
 * width and the height supplied in this layout. The SwipeRefreshLayout does not
 * provide accessibility events; instead, a menu item must be provided to allow
 * refresh of the content wherever this gesture is used.
 * </p>
 */
public class SwipeClearLayout extends RelativeLayout {
    private static final long RETURN_TO_ORIGINAL_POSITION_TIMEOUT = 300;
    private static final float ACCELERATE_INTERPOLATION_FACTOR = 1.5f;
    private static final float DECELERATE_INTERPOLATION_FACTOR = 2f;
    private static final float MAX_SWIPE_DISTANCE_FACTOR = .6f;
    private static final int REFRESH_TRIGGER_DISTANCE = 120;
    private static final int CIRCLE_SIZE = 48;
    private static final int CIRCLE_DEFAULT_COLOR = Color.MAGENTA;
    private static final int DEFAULT_ANIMATION_DURATION = 300*2;

    private View circle;
    private int circleTopMargin = 0;
    private int circleColor;
    private int duration = DEFAULT_ANIMATION_DURATION;

    private ProgressBar progressBar;

    private View target; //the content that gets pulled down
    private int originalOffsetTop;
    private OnRefreshListener listener;
    private OnSwipeListener onSwipeListener;
    private MotionEvent downEvent;
    private int from;
    private boolean refreshing = false;
    private int touchSlop;
    private float distanceToTriggerSync = -1;
    private float prevY;
    private int mediumAnimationDuration;
    private float currPercentage = 0;
    private int currentTargetOffsetTop;
    // Target is returning to its start offset because it was cancelled or a
    // refresh was triggered.
    private boolean returningToStart;
    private final DecelerateInterpolator decelerateInterpolator;
    private final AccelerateInterpolator accelerateInterpolator;
    private static final int[] LAYOUT_ATTRS = new int[] {
            android.R.attr.enabled
    };

    private final Animation animateToStartPosition = new Animation() {
        @Override
        public void applyTransformation(float interpolatedTime, Transformation t) {
            int targetTop = 0;
            if (from != originalOffsetTop) {
                targetTop = (from + (int)((originalOffsetTop - from) * interpolatedTime));
            }
            int offset = targetTop - circle.getTop();
            final int currentTop = circle.getTop();
            if (offset + currentTop < 0) {
                offset = 0 - currentTop;
            }
            setTargetOffsetTopAndBottom(offset);
        }
    };

    private final AnimationListener returnToStartPositionListener = new BaseAnimationListener() {
        @Override
        public void onAnimationEnd(Animation animation) {
            // Once the target content has returned to its start position, reset
            // the target offset to 0
            currentTargetOffsetTop = 0;
        }
    };

    private final Runnable returnToStartPosition = new Runnable() {

        @Override
        public void run() {
            returningToStart = true;
            animateOffsetToStartPosition(currentTargetOffsetTop + getPaddingTop(),
                    returnToStartPositionListener);
        }

    };

    // Cancel the refresh gesture and animate everything back to its original state.
    private final Runnable cancel = new Runnable() {

        @Override
        public void run() {
            returningToStart = true;
            // Timeout fired since the user last moved their finger; animate the
            // trigger to 0 and put the target back at its original position
            animateOffsetToStartPosition(currentTargetOffsetTop + getPaddingTop(),
                    returnToStartPositionListener);
        }

    };
    private View filledView;

    /**
     * Simple constructor to use when creating a SwipeRefreshLayout from code.
     * @param context
     */
    public SwipeClearLayout(Context context) {
        this(context, null);
    }

    /**
     * Constructor that is called when inflating SwipeRefreshLayout from XML.
     * @param context
     * @param attrs
     */
    public SwipeClearLayout(Context context, AttributeSet attrs) {
        super(context, attrs);

        touchSlop = ViewConfiguration.get(context).getScaledTouchSlop();

        mediumAnimationDuration = getResources().getInteger(
                android.R.integer.config_mediumAnimTime);

        setWillNotDraw(false);
        final DisplayMetrics metrics = getResources().getDisplayMetrics();

        circle = generateCircle(context, attrs, metrics);
        progressBar = new ProgressBar(context, attrs);

        decelerateInterpolator = new DecelerateInterpolator(DECELERATE_INTERPOLATION_FACTOR);
        accelerateInterpolator = new AccelerateInterpolator(ACCELERATE_INTERPOLATION_FACTOR);

        final TypedArray a = context.obtainStyledAttributes(attrs, LAYOUT_ATTRS);
        setEnabled(a.getBoolean(0, true));
        initAttrs(context, attrs);
        a.recycle();
    }

    private void initAttrs(Context context, AttributeSet attrs){
        final Resources.Theme theme = context.getTheme();
        if (theme != null) {
            final TypedArray typedArray = theme.obtainStyledAttributes(
                    attrs,
                    R.styleable.SwipeClearLayout,
                    0, 0);
            if (typedArray != null) {
                try {
                    circleTopMargin = (int) typedArray.getDimension(R.styleable.SwipeClearLayout_circleTopMargin, 0);
                    circleColor = typedArray.getColor(R.styleable.SwipeClearLayout_circleColor, CIRCLE_DEFAULT_COLOR);
                    duration = typedArray.getInteger(R.styleable.SwipeClearLayout_duration, DEFAULT_ANIMATION_DURATION);
                } finally {
                    typedArray.recycle();
                }
            }
        }
    }

    private View generateCircle(Context context, AttributeSet attrs, DisplayMetrics metrics){
        ImageView view = new ImageView(context, attrs);
        GradientDrawable circle = (GradientDrawable) getResources().getDrawable(R.drawable.circle);
        circle.setColor(CIRCLE_DEFAULT_COLOR);
        if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN){
            view.setBackground(circle);
        } else {
            view.setBackgroundDrawable(circle);
        }
        int size = (int) (metrics.density * CIRCLE_SIZE);
        RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(size, size);
        view.setLayoutParams(params);
        view.setImageResource(R.drawable.clip_random);
        view.setScaleType(ImageView.ScaleType.CENTER_INSIDE);
        view.setRotation(90.0f);
        return view;
    }

    @Override
    public void onAttachedToWindow() {
        super.onAttachedToWindow();
        removeCallbacks(cancel);
        removeCallbacks(returnToStartPosition);
    }

    @Override
    public void onDetachedFromWindow() {
        super.onDetachedFromWindow();
        removeCallbacks(returnToStartPosition);
        removeCallbacks(cancel);
    }

    private void animateOffsetToStartPosition(int from, AnimationListener listener) {
        this.from = from;
        animateToStartPosition.reset();
        animateToStartPosition.setDuration(mediumAnimationDuration);
        animateToStartPosition.setAnimationListener(listener);
        animateToStartPosition.setInterpolator(decelerateInterpolator);
//        target.startAnimation(animateToStartPosition);
        circle.startAnimation(animateToStartPosition);
    }

    /**
     * Set the listener to be notified when a refresh is triggered via the swipe
     * gesture.
     */
    public void setOnRefreshListener(OnRefreshListener listener) {
        this.listener = listener;
    }

    /**
     * Set the listener to be notified when a swipe is triggered by the user.
     */
    public void setOnSwipeListener(OnSwipeListener listener) {
        onSwipeListener = listener;
    }

    private void setTriggerPercentage(float percent) {
        if (percent == 0f) {
            // No-op. A null trigger means it's uninitialized, and setting it to zero-percent
            // means we're trying to reset state, so there's nothing to reset in this case.
            currPercentage = 0;
            return;
        }
        currPercentage = percent;
    }

    /**
     * Notify the widget that refresh state has changed. Do not call this when
     * refresh is triggered by a swipe gesture.
     *
     * @param refreshing Whether or not the view should show refresh progress.
     */
    public void setRefreshing(boolean refreshing) {
        if (this.refreshing != refreshing) {
            ensureTarget();
            currPercentage = 0;
            this.refreshing = refreshing;
            if (this.refreshing) {
                animateCircle();
                progressBar.animate().alpha(1.0f).setStartDelay(duration /2).setDuration(duration /2).start();
            } else {
                progressBar.animate().alpha(0.0f).setStartDelay(0).setDuration(200).start();
                getChildAt(0).setAlpha(1.0f);
            }
        }
    }

    /**
     * Set the color for the swipe circle.
     * @param colorResId Color resource.
     */
    public void setCircleColorResourceId(int colorResId){
        ensureTarget();
        final Resources resources = getResources();
        circleColor = resources.getColor(colorResId);
        ((GradientDrawable) circle.getBackground()).setColor(circleColor);
    }

    /**
     * Set the color for the swipe circle.
     * @param color Color.
     */
    public void setCircleColor(int color){
        ensureTarget();
        circleColor = color;
        ((GradientDrawable) circle.getBackground()).setColor(circleColor);
    }

    /**
     * Set the top margin of the circle.
     */
    public void setCircleTopMargin(int topMargin){
        circleTopMargin = topMargin;
    }

    public ProgressBar getProgressBar(){
        return progressBar;
    }

    public void setProgressBar(ProgressBar progressBar){
        this.progressBar = progressBar;
    }

    /**
     * @return Whether the SwipeClearWidget is actively showing refresh
     *         progress.
     */
    public boolean isRefreshing() {
        return refreshing;
    }

    private void ensureTarget() {
        // Don't bother getting the parent height if the parent hasn't been laid out yet.
        if (target == null) {
            if (getChildCount() > 4 && !isInEditMode()) {
                throw new IllegalStateException(
                        "SwipeClearLayout can host only one direct child");
            }
            target = getChildAt(0);
            originalOffsetTop = circle.getTop() + getPaddingTop();
        }
        if (distanceToTriggerSync == -1) {
            if (getParent() != null && ((View)getParent()).getHeight() > 0) {
                final DisplayMetrics metrics = getResources().getDisplayMetrics();
                distanceToTriggerSync = (int) Math.min(
                        ((View) getParent()) .getHeight() * MAX_SWIPE_DISTANCE_FACTOR,
                        REFRESH_TRIGGER_DISTANCE * metrics.density);
            }
        }
    }

    @Override
    protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
        super.onLayout(changed, left, top, right, bottom);
        final int width =  getMeasuredWidth();
        final int height = getMeasuredHeight();
        if (getChildCount() == 0) {
            return;
        }
        final View child = getChildAt(0);
        final int childLeft = getPaddingLeft();
        final int childTop = currentTargetOffsetTop + getPaddingTop();
        final int childWidth = width - getPaddingLeft() - getPaddingRight();
        final int childHeight = height - getPaddingTop() - getPaddingBottom();
        child.layout(childLeft, childTop, childLeft + childWidth, childTop + childHeight);

        if (getChildAt(1) == null){
            final DisplayMetrics metrics = getResources().getDisplayMetrics();
            int size = (int) (metrics.density * CIRCLE_SIZE);
            @SuppressLint("DrawAllocation") final RelativeLayout.LayoutParams params = new RelativeLayout.LayoutParams(size, size);
            //The circle should start to grow from the top of SwipeClearLayout, hence "-(size/2)".
//            params.topMargin = circleTopMargin - size;
            params.addRule(CENTER_HORIZONTAL, TRUE);
            circle.setLayoutParams(params);
            addView(circle, 1);
            circle.setScaleX(1.0f);
            circle.setScaleY(1.0f);
            ((GradientDrawable) circle.getBackground()).setColor(circleColor);
            circle.setTranslationY(-size);
        }

        if (getChildAt(2) == null){
            addView(progressBar, 2);
            RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) progressBar.getLayoutParams();
            params.addRule(CENTER_IN_PARENT, TRUE);
            progressBar.setLayoutParams(params);
            progressBar.setAlpha(0.0f);
        }
    }

    @Override
    public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
        super.onMeasure(widthMeasureSpec, heightMeasureSpec);
        if (getChildCount() > 4 && !isInEditMode()) {
            throw new IllegalStateException("SwipeClearLayout can host only one direct child");
        }
        if (getChildCount() > 0) {
            getChildAt(0).measure(
                    MeasureSpec.makeMeasureSpec(
                            getMeasuredWidth() - getPaddingLeft() - getPaddingRight(),
                            MeasureSpec.EXACTLY),
                    MeasureSpec.makeMeasureSpec(
                            getMeasuredHeight() - getPaddingTop() - getPaddingBottom(),
                            MeasureSpec.EXACTLY));
        }
    }

    /**
     * @return Whether it is possible for the child view of this layout to
     *         scroll up. Override this if the child view is a custom view.
     */
    public boolean canChildScrollUp() {
        if (Build.VERSION.SDK_INT < 14) {
            if (target instanceof AbsListView) {
                final AbsListView absListView = (AbsListView) target;
                return absListView.getChildCount() > 0
                        && (absListView.getFirstVisiblePosition() > 0 || absListView.getChildAt(0)
                        .getTop() < absListView.getPaddingTop());
            } else {
                return target.getScrollY() > 0;
            }
        } else {
            return ViewCompat.canScrollVertically(target, -1);
        }
    }

    @Override
    public boolean onInterceptTouchEvent(MotionEvent ev) {
        ensureTarget();
        boolean handled = false;
        if (returningToStart && ev.getAction() == MotionEvent.ACTION_DOWN) {
            returningToStart = false;
        }
        if (isEnabled() && !returningToStart && !canChildScrollUp()) {
            handled = onTouchEvent(ev);
        }
        return !handled ? super.onInterceptTouchEvent(ev) : handled;
    }

    @Override
    public void requestDisallowInterceptTouchEvent(boolean b) {
        // Nope.
    }

    @Override
    public boolean onTouchEvent(@NonNull MotionEvent event) {
        final int action = event.getAction();
        boolean handled = false;
        switch (action) {
            case MotionEvent.ACTION_DOWN:
                currPercentage = 0;
                downEvent = MotionEvent.obtain(event);
                prevY = downEvent.getY();
                break;
            case MotionEvent.ACTION_MOVE:
                if (downEvent != null && !returningToStart) {
                    final float eventY = event.getY();
                    float yDiff = eventY - downEvent.getY();
                    if (yDiff > touchSlop) {
                        // User velocity passed min velocity; trigger a refresh
                        if (yDiff > distanceToTriggerSync) {
                            // User movement passed distance; trigger a refresh
                            startRefresh();
                            handled = true;
                            break;
                        } else {
                            // Just track the user's movement
                            setTriggerPercentage(
                                    accelerateInterpolator.getInterpolation(
                                            yDiff / distanceToTriggerSync));
                            float offsetTop = yDiff;
                            if (prevY > eventY) {
                                offsetTop = yDiff - touchSlop;
                            }
                            updateContentOffsetTop((int) (offsetTop));
//                            if (prevY > eventY && (target.getTop() < touchSlop)) {
                            if (prevY > eventY && (circle.getTop() < touchSlop)) {
                                // If the user puts the view back at the top, we
                                // don't need to. This shouldn't be considered
                                // cancelling the gesture as the user can restart from the top.
                                removeCallbacks(cancel);
                            } else {
                                updatePositionTimeout();
                            }
                            prevY = event.getY();
                            handled = true;
                        }
                    }
                }
                break;
            case MotionEvent.ACTION_UP:
            case MotionEvent.ACTION_CANCEL:
                if (downEvent != null) {
                    downEvent.recycle();
                    downEvent = null;
                }
                break;
        }
        return handled;
    }

    private void startRefresh() {
        removeCallbacks(cancel);
        returnToStartPosition.run();
        setRefreshing(true);
        listener.onRefresh();
    }

    private void animateCircle() {
        float currentScale = circle.getScaleX();

        if (currentScale == 0.0f){
            circle.setScaleX(1.0f);
            circle.setScaleY(1.0f);
            currentScale = 1.0f;
        }

        int currentPixelSize = (int) (circle.getHeight() * currentScale);

        if (currentPixelSize == 0) {
            return;
        }

        int width = getMeasuredWidth();
        int height = getMeasuredHeight();

        int hypotenuse = (int) Math.sqrt(Math.pow(width, 2) + Math.pow(height, 2));

        float goalScale = (hypotenuse / currentPixelSize) * 2;

        circle.setAlpha(1.0f);
        circle.animate().scaleX(goalScale).scaleY(goalScale).setDuration(duration / 2).setListener(new Animator.AnimatorListener() {
            @Override
            public void onAnimationStart(Animator animator) {
            }

            @Override
            public void onAnimationEnd(Animator animator) {

                if (isRefreshing()) {
                    getChildAt(0).setAlpha(0.0f); //list should be invisible until it stops refreshing
                }

                if (getChildAt(3) == null) {
                    filledView = new View(getContext());
                    ColorDrawable colorDrawable = new ColorDrawable(circleColor);
                    if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN) {
                        filledView.setBackground(colorDrawable);
                    } else {
                        filledView.setBackgroundDrawable(colorDrawable);
                    }
                    addView(filledView, 3);
                    RelativeLayout.LayoutParams params = (RelativeLayout.LayoutParams) filledView.getLayoutParams();
                    params.width = RelativeLayout.LayoutParams.MATCH_PARENT;
                    params.height = RelativeLayout.LayoutParams.MATCH_PARENT;
                    filledView.setLayoutParams(params);
                }
                filledView.setVisibility(VISIBLE);
                filledView.setAlpha(1.0f);
                filledView.animate().alpha(0.0f).setDuration(duration / 2).setListener(new Animator.AnimatorListener() {
                    @Override
                    public void onAnimationStart(Animator animator) {
                    }

                    @Override
                    public void onAnimationEnd(Animator animator) {
//                        removeViewAt(3);
                    }

                    @Override
                    public void onAnimationCancel(Animator animator) {
                    }

                    @Override
                    public void onAnimationRepeat(Animator animator) {
                    }
                }).start();
                circle.setScaleX(1.0f);
                circle.setScaleY(1.0f);
                circle.setAlpha(1.0f);
                circle.setTranslationY(-circle.getHeight());
            }

            @Override
            public void onAnimationCancel(Animator animator) {

            }

            @Override
            public void onAnimationRepeat(Animator animator) {

            }
        }).start();
    }

    private void updateContentOffsetTop(int targetTop) {
        final int currentTop = circle.getTop();
        if (targetTop > distanceToTriggerSync) {
            targetTop = (int) distanceToTriggerSync;
        } else if (targetTop < 0) {
            targetTop = 0;
        }
        setTargetOffsetTopAndBottom(targetTop - currentTop);
    }

    private void setTargetOffsetTopAndBottom(int offset) {
        circle.offsetTopAndBottom(offset);
        currentTargetOffsetTop = circle.getTop();
        int percent = (int) ((currentTargetOffsetTop / distanceToTriggerSync) * 100);
        if (onSwipeListener != null) {
            onSwipeListener.onSwipe(percent, currentTargetOffsetTop);
        }
        ViewCompat.setElevation(circle, percent);
        ImageView imageView = (ImageView) circle;
        ClipDrawable clipDrawable = (ClipDrawable) imageView.getDrawable();

        if (percent < 50) {
            clipDrawable.setLevel(0);
        } else {
            clipDrawable.setLevel((percent - 50) * 2 * 100);
        }
    }

    private void updatePositionTimeout() {
        removeCallbacks(cancel);
        postDelayed(cancel, RETURN_TO_ORIGINAL_POSITION_TIMEOUT);
    }

    /**
     * Classes that wish to be notified when the swipe gesture correctly
     * triggers a refresh should implement this interface.
     */
    public interface OnRefreshListener {
        public void onRefresh();
    }

    /**
     * Classes that wish to be notified how much there's left until a
     * refresh is triggered should implement this interface.
     */
    public interface OnSwipeListener {
        /**
         * Called when user starts pulling the layout for a refresh.
         * @param progress How much is left (in percent) until a refresh is triggered.
         * @param pixels How much the list has moved in pixels
         */
        abstract void onSwipe(int progress, int pixels);
    }

    /**
     * Simple AnimationListener to avoid having to implement unneeded methods in
     * AnimationListeners.
     */
    private class BaseAnimationListener implements AnimationListener {
        @Override
        public void onAnimationStart(Animation animation) {
        }

        @Override
        public void onAnimationEnd(Animation animation) {
        }

        @Override
        public void onAnimationRepeat(Animation animation) {
        }
    }
}




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