165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/*
265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Copyright (C) 2014 The Android Open Source Project
365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Licensed under the Apache License, Version 2.0 (the "License");
565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * you may not use this file except in compliance with the License.
665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * You may obtain a copy of the License at
765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *      http://www.apache.org/licenses/LICENSE-2.0
965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane *
1065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Unless required by applicable law or agreed to in writing, software
1165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * distributed under the License is distributed on an "AS IS" BASIS,
1265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
1365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * See the License for the specific language governing permissions and
1465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * limitations under the License.
1565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
1665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepackage com.android.tv.settings.device.storage;
1865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
1965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.Canvas;
2065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.ColorFilter;
2165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.Paint;
2265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.PixelFormat;
2365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.graphics.drawable.Drawable;
2465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Parcel;
2565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport android.os.Parcelable;
2665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
2765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Laneimport java.util.ArrayList;
2865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
2965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane/**
3065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * Draws a horizontal bar chart with colored slices, each represented by
3165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane * {@link Entry}. Pulled from Android Settings App.
3265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane */
3365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lanepublic class PercentageBarChart extends Drawable {
3465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
3565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public static class Entry implements Comparable<Entry>, Parcelable {
3665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
3765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final int order;
3865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final float percentage;
3965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        final Paint paint;
4065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public Entry(int order, float percentage, int color) {
4265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            this.order = order;
4365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            this.percentage = percentage;
4465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            this.paint = new Paint();
4565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            this.paint.setColor(color);
4665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            this.paint.setStyle(Paint.Style.FILL);
4765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
4865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
4965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
5065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public int compareTo(Entry another) {
5165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return order - another.order;
5265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
5365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
5565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public int describeContents() {
5665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            return 0;
5765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
5865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
5965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        @Override
6065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public void writeToParcel(Parcel dest, int flags) {
6165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dest.writeInt(order);
6265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dest.writeFloat(percentage);
6365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            dest.writeInt(paint.getColor());
6465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
6565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
6665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        public static final Parcelable.Creator<Entry> CREATOR = new Parcelable.Creator<Entry>() {
6765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            @Override
6865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            public Entry createFromParcel(Parcel in) {
6965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return new Entry(in.readInt(), in.readFloat(), in.readInt());
7065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
7165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            @Override
7365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            public Entry[] newArray(int size) {
7465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return new Entry[size];
7565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
7665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        };
7765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
7865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
7965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final ArrayList<Entry> mEntries;
8065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final int mBackgroundColor;
8165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final int mMinTickWidth;
8265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final int mWidth;
8365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final int mHeight;
8465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final boolean mIsLayoutRtl;
8565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private final Paint mEmptyPaint;
8665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
8765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public PercentageBarChart(ArrayList<Entry> entries, int backgroundColor, int minTickWidth,
8865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            int width, int height, boolean isLayoutRtl) {
8965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        super();
9065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mEntries = entries;
9165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mBackgroundColor = backgroundColor;
9265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mMinTickWidth = minTickWidth;
9365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mWidth = width;
9465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mHeight = height;
9565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mIsLayoutRtl = isLayoutRtl;
9665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mEmptyPaint = new Paint();
9765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mEmptyPaint.setColor(backgroundColor);
9865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mEmptyPaint.setStyle(Paint.Style.FILL);
9965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
10065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
10165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
10265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public int getIntrinsicHeight() {
10365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mHeight;
10465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
10565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
10665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
10765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public int getIntrinsicWidth() {
10865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return mWidth;
10965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
11065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
11265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public void draw(Canvas canvas) {
11365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        mEmptyPaint.setColor(mBackgroundColor);
11465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        float end = (mIsLayoutRtl) ? 0 : mWidth;
11665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        float lastX = (mIsLayoutRtl) ? mWidth : 0;
11765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
11865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        for (final Entry e : mEntries) {
11965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (e.percentage == 0.0f) {
12065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                continue;
12165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
12265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
12365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            final float entryWidth = Math.max(mMinTickWidth, mWidth * e.percentage);
12465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
12565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // progress toward the end.
12665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            final float nextX = lastX + ((lastX < end) ? entryWidth : -entryWidth);
12765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
12865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            // if we've hit the limit, stop drawing.
12965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            if (drawEntry(canvas, lastX, nextX, e.paint)) {
13065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane                return;
13165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            }
13265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            lastX = nextX;
13365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
13465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
13565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        drawEntry(canvas, lastX, end, mEmptyPaint);
13665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
13765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
13865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
13965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public void setAlpha(int alpha) {
14065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
14165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
14365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public void setColorFilter(ColorFilter cf) {
14465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
14565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
14665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    @Override
14765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    public int getOpacity() {
14865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return PixelFormat.OPAQUE;
14965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
15065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane
15165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    /**
15265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * Draws a rectangle from the lesser of the two x inputs to the greater of
15365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * the two. If either of the two x inputs lie outside the bounds of this
15465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * drawable, limit the rectangle drawn to the bounds.
15565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     *
15665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @param canvas the canvas to draw to.
15765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @param x1 the first x input. This may be greater or smaller than x2.
15865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @param x2 the second x input. This may be greater or smaller than x1.
15965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @param paint the color to draw.
16065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     * @return true if either of the x inputs was beyond the bounds of this
16165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     *         drawable, false otherwise.
16265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane     */
16365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    private boolean drawEntry(Canvas canvas, float x1, float x2, Paint paint) {
16465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        boolean hitLimit = false;
16565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        float left = x1 > x2 ? x2 : x1;
16665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        float right = x1 > x2 ? x1 : x2;
16765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (left < 0) {
16865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            left = 0;
16965a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            hitLimit = true;
17065a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
17165a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        if (right > mWidth) {
17265a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            right = mWidth;
17365a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane            hitLimit = true;
17465a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        }
17565a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        canvas.drawRect(left, 0, right, mHeight, paint);
17665a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane        return hitLimit;
17765a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane    }
17865a5a7d84ad9b5324ae53eda526e39e513473af7Christopher Lane}
179