ChartSweepView.java revision ab2d8d3a38857b8c155e6c6393c5821f5a341aae
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;
20ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Canvas;
21ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Color;
22ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Paint;
23ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Paint.Style;
24ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.view.MotionEvent;
25ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.view.View;
26ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
27ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport com.google.common.base.Preconditions;
28ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
29ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey/**
30ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * Sweep across a {@link ChartView} at a specific {@link ChartAxis} value, which
31ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * a user can drag.
32ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey */
33ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeypublic class ChartSweepView extends View {
34ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
35ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private final Paint mPaintSweep;
36ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private final Paint mPaintShadow;
37ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
38ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private final ChartAxis mAxis;
39ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private long mValue;
40ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
41ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    public interface OnSweepListener {
42ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        public void onSweep(ChartSweepView sweep, boolean sweepDone);
43ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
44ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
45ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private OnSweepListener mListener;
46ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
47ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private boolean mHorizontal;
48ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private MotionEvent mTracking;
49ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
50ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    public ChartSweepView(Context context, ChartAxis axis, long value, int color) {
51ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        super(context);
52ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
53ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mAxis = Preconditions.checkNotNull(axis, "missing axis");
54ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mValue = value;
55ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
56ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintSweep = new Paint();
57ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintSweep.setColor(color);
58ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintSweep.setStrokeWidth(3.0f);
59ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintSweep.setStyle(Style.FILL_AND_STROKE);
60ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintSweep.setAntiAlias(true);
61ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
62ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintShadow = new Paint();
63ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintShadow.setColor(Color.BLACK);
64ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintShadow.setStrokeWidth(6.0f);
65ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintShadow.setStyle(Style.FILL_AND_STROKE);
66ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mPaintShadow.setAntiAlias(true);
67ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
68ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
69ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
70ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    public void addOnSweepListener(OnSweepListener listener) {
71ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mListener = listener;
72ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
73ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
74ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    private void dispatchOnSweep(boolean sweepDone) {
75ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        if (mListener != null) {
76ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            mListener.onSweep(this, sweepDone);
77ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        }
78ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
79ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
80ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    public ChartAxis getAxis() {
81ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        return mAxis;
82ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
83ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
84ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    public long getValue() {
85ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        return mValue;
86ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
87ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
88ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    public float getPoint() {
89ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        return mAxis.convertToPoint(mValue);
90ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
91ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
92ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    @Override
93ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    public boolean onTouchEvent(MotionEvent event) {
94ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        final View parent = (View) getParent();
95ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        switch (event.getAction()) {
96ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            case MotionEvent.ACTION_DOWN: {
97ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                mTracking = event.copy();
98ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                return true;
99ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            }
100ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            case MotionEvent.ACTION_MOVE: {
101ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                if (mHorizontal) {
102ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                    setTranslationY(event.getRawY() - mTracking.getRawY());
103ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                    final float point = (getTop() + getTranslationY() + (getHeight() / 2))
104ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                            - parent.getPaddingTop();
105ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                    mValue = mAxis.convertToValue(point);
106ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                    dispatchOnSweep(false);
107ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                } else {
108ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                    setTranslationX(event.getRawX() - mTracking.getRawX());
109ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                    final float point = (getLeft() + getTranslationX() + (getWidth() / 2))
110ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                            - parent.getPaddingLeft();
111ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                    mValue = mAxis.convertToValue(point);
112ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                    dispatchOnSweep(false);
113ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                }
114ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                return true;
115ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            }
116ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            case MotionEvent.ACTION_UP: {
117ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                mTracking = null;
118ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                setTranslationX(0);
119ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                setTranslationY(0);
120ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                requestLayout();
121ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                dispatchOnSweep(true);
122ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                return true;
123ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            }
124ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            default: {
125ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey                return false;
126ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            }
127ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        }
128ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
129ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
130ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    @Override
131ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
132ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        // need at least 50px in each direction for grippies
133ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        // TODO: provide this value through params
134ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        setMeasuredDimension(50, 50);
135ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
136ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
137ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    @Override
138ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    protected void onDraw(Canvas canvas) {
139ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
140ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        // draw line across larger dimension
141ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        final int width = getWidth();
142ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        final int height = getHeight();
143ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
144ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        mHorizontal = width > height;
145ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
146ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        if (mHorizontal) {
147ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            final int centerY = height / 2;
148ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            final int endX = width - height;
149ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
150ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            canvas.drawLine(0, centerY, endX, centerY, mPaintShadow);
151ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            canvas.drawLine(0, centerY, endX, centerY, mPaintSweep);
152ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            canvas.drawCircle(endX, centerY, 4.0f, mPaintShadow);
153ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            canvas.drawCircle(endX, centerY, 4.0f, mPaintSweep);
154ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        } else {
155ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            final int centerX = width / 2;
156ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            final int endY = height - width;
157ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
158ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            canvas.drawLine(centerX, 0, centerX, endY, mPaintShadow);
159ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            canvas.drawLine(centerX, 0, centerX, endY, mPaintSweep);
160ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            canvas.drawCircle(centerX, endY, 4.0f, mPaintShadow);
161ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey            canvas.drawCircle(centerX, endY, 4.0f, mPaintSweep);
162ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey        }
163ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey    }
164ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey
165ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey}
166