1ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey/*
2ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * Copyright (C) 2011 The Android Open Source Project
3ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey *
4ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * Licensed under the Apache License, Version 2.0 (the "License");
5ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * you may not use this file except in compliance with the License.
6ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * You may obtain a copy of the License at
7ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey *
8ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey *      http://www.apache.org/licenses/LICENSE-2.0
9ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey *
10ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * Unless required by applicable law or agreed to in writing, software
11ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * distributed under the License is distributed on an "AS IS" BASIS,
12ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * See the License for the specific language governing permissions and
14ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * limitations under the License.
15ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey */
16ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
17ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeypackage com.android.settings.widget;
18ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
19ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.content.Context;
20b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkeyimport android.content.res.ColorStateList;
21d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkeyimport android.content.res.Resources;
2252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.content.res.TypedArray;
23ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Canvas;
24d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkeyimport android.graphics.Paint;
2552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.graphics.drawable.Drawable;
26d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkeyimport android.text.Layout;
27d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkeyimport android.text.StaticLayout;
28d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkeyimport android.text.TextPaint;
2952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.util.AttributeSet;
30ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.view.View;
31ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
32e6c5003278184c202833209164ddf1ae8c083f12Jeff Sharkeyimport com.android.internal.util.Preconditions;
3352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport com.android.settings.R;
34ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
35b37e2887d3112082589997f1bbd802ec282cca6dJason Monkimport static com.android.settings.Utils.formatDateRange;
3639b467482d1bf256a111c757e9b7621c6f523271Jason Monk
37ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey/**
38ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * Background of {@link ChartView} that renders grid lines as requested by
39ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * {@link ChartAxis#getTickPoints()}.
40ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey */
41ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeypublic class ChartGridView extends View {
42ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
4352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    private ChartAxis mHoriz;
4452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    private ChartAxis mVert;
45ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
4652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    private Drawable mPrimary;
4752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    private Drawable mSecondary;
4852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    private Drawable mBorder;
49b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey
50b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey    private int mLabelSize;
51d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey    private int mLabelColor;
52d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey
53b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey    private Layout mLabelStart;
54b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey    private Layout mLabelMid;
55b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey    private Layout mLabelEnd;
56ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
5752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    public ChartGridView(Context context) {
5852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        this(context, null, 0);
5952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    }
6052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
6152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    public ChartGridView(Context context, AttributeSet attrs) {
6252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        this(context, attrs, 0);
6352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    }
6452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
6552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    public ChartGridView(Context context, AttributeSet attrs, int defStyle) {
6652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        super(context, attrs, defStyle);
67ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
68ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        setWillNotDraw(false);
69ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
7052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        final TypedArray a = context.obtainStyledAttributes(
7152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey                attrs, R.styleable.ChartGridView, defStyle, 0);
7252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
7352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mPrimary = a.getDrawable(R.styleable.ChartGridView_primaryDrawable);
7452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mSecondary = a.getDrawable(R.styleable.ChartGridView_secondaryDrawable);
7552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mBorder = a.getDrawable(R.styleable.ChartGridView_borderDrawable);
76b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey
77b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final int taId = a.getResourceId(R.styleable.ChartGridView_android_textAppearance, -1);
78b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final TypedArray ta = context.obtainStyledAttributes(taId,
79b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey                com.android.internal.R.styleable.TextAppearance);
80b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        mLabelSize = ta.getDimensionPixelSize(
81b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey                com.android.internal.R.styleable.TextAppearance_textSize, 0);
82b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        ta.recycle();
83b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey
84b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final ColorStateList labelColor = a.getColorStateList(
85b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey                R.styleable.ChartGridView_android_textColor);
86b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        mLabelColor = labelColor.getDefaultColor();
8752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
8852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        a.recycle();
8952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    }
9052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey
9152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey    void init(ChartAxis horiz, ChartAxis vert) {
9252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mHoriz = Preconditions.checkNotNull(horiz, "missing horiz");
9352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mVert = Preconditions.checkNotNull(vert, "missing vert");
94ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
95ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
96d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey    void setBounds(long start, long end) {
97d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        final Context context = getContext();
98b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final long mid = (start + end) / 2;
99b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        mLabelStart = makeLabel(formatDateRange(context, start, start));
100b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        mLabelMid = makeLabel(formatDateRange(context, mid, mid));
101b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        mLabelEnd = makeLabel(formatDateRange(context, end, end));
102d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        invalidate();
103d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey    }
104d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey
105ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    @Override
106ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    protected void onDraw(Canvas canvas) {
107ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        final int width = getWidth();
108b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final int height = getHeight() - getPaddingBottom();
109ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
11052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        final Drawable secondary = mSecondary;
111b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        if (secondary != null) {
112b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            final int secondaryHeight = secondary.getIntrinsicHeight();
113b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey
114b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            final float[] vertTicks = mVert.getTickPoints();
115b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            for (float y : vertTicks) {
116b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey                final int bottom = (int) Math.min(y + secondaryHeight, height);
117b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey                secondary.setBounds(0, (int) y, width, bottom);
118b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey                secondary.draw(canvas);
119b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            }
120ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        }
121ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
12252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        final Drawable primary = mPrimary;
123b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        if (primary != null) {
124b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            final int primaryWidth = primary.getIntrinsicWidth();
125b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            final int primaryHeight = primary.getIntrinsicHeight();
126b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey
127b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            final float[] horizTicks = mHoriz.getTickPoints();
128b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            for (float x : horizTicks) {
129b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey                final int right = (int) Math.min(x + primaryWidth, width);
130b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey                primary.setBounds((int) x, 0, right, height);
131b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey                primary.draw(canvas);
132b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            }
133ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        }
134ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
13552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mBorder.setBounds(0, 0, width, height);
13652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey        mBorder.draw(canvas);
137d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey
138b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final int padding = mLabelStart != null ? mLabelStart.getHeight() / 8 : 0;
139d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey
140b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final Layout start = mLabelStart;
141d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        if (start != null) {
142b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            final int saveCount = canvas.save();
143d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey            canvas.translate(0, height + padding);
144d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey            start.draw(canvas);
145b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            canvas.restoreToCount(saveCount);
146b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        }
147b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey
148b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final Layout mid = mLabelMid;
149b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        if (mid != null) {
150b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            final int saveCount = canvas.save();
151b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            canvas.translate((width - mid.getWidth()) / 2, height + padding);
152b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            mid.draw(canvas);
153b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            canvas.restoreToCount(saveCount);
154d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        }
155d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey
156b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        final Layout end = mLabelEnd;
157d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        if (end != null) {
158b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            final int saveCount = canvas.save();
159d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey            canvas.translate(width - end.getWidth(), height + padding);
160d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey            end.draw(canvas);
161b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey            canvas.restoreToCount(saveCount);
162d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        }
163ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
164d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey
165b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey    private Layout makeLabel(CharSequence text) {
166d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        final Resources res = getResources();
167d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
168d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        paint.density = res.getDisplayMetrics().density;
169d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        paint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
170d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        paint.setColor(mLabelColor);
171b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey        paint.setTextSize(mLabelSize);
172d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey
173d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey        return new StaticLayout(text, paint,
174d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey                (int) Math.ceil(Layout.getDesiredWidth(text, paint)),
175d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey                Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
176d39c6e4083f1212519d5dc14f64132a10f2b7c7aJeff Sharkey    }
177ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey}
178