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; 2054d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkeyimport android.content.res.TypedArray; 21ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Rect; 2252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.util.AttributeSet; 23ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.view.Gravity; 24ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.view.View; 2554d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkeyimport android.view.ViewDebug; 26ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.widget.FrameLayout; 27ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 28e6c5003278184c202833209164ddf1ae8c083f12Jeff Sharkeyimport com.android.internal.util.Preconditions; 2954d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkeyimport com.android.settings.R; 3054d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey 31ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey/** 32ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * Container for two-dimensional chart, drawn with a combination of 33ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * {@link ChartGridView}, {@link ChartNetworkSeriesView} and {@link ChartSweepView} 34ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * children. The entire chart uses {@link ChartAxis} to map between raw values 35ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * and screen coordinates. 36ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey */ 37ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeypublic class ChartView extends FrameLayout { 38ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey // TODO: extend something that supports two-dimensional scrolling 39ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 4079d8e80a304922c34a6bd344f1fa49dd7dfd106dFabrice Di Meglio private static final int SWEEP_GRAVITY = Gravity.TOP | Gravity.START; 41f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 4252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey ChartAxis mHoriz; 4352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey ChartAxis mVert; 44ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 4554d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey @ViewDebug.ExportedProperty 4654d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey private int mOptimalWidth = -1; 4754d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey private float mOptimalWidthWeight = 0; 4854d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey 49ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey private Rect mContent = new Rect(); 50ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 5152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public ChartView(Context context) { 5252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey this(context, null, 0); 5352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 54ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 5552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public ChartView(Context context, AttributeSet attrs) { 5652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey this(context, attrs, 0); 5752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 5852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 5952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public ChartView(Context context, AttributeSet attrs, int defStyle) { 6052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey super(context, attrs, defStyle); 61ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 6254d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey final TypedArray a = context.obtainStyledAttributes( 6354d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey attrs, R.styleable.ChartView, defStyle, 0); 6454d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey setOptimalWidth(a.getDimensionPixelSize(R.styleable.ChartView_optimalWidth, -1), 6554d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey a.getFloat(R.styleable.ChartView_optimalWidthWeight, 0)); 6654d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey a.recycle(); 6754d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey 68ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey setClipToPadding(false); 69ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey setClipChildren(false); 70ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 71ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 7252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey void init(ChartAxis horiz, ChartAxis vert) { 73e6c5003278184c202833209164ddf1ae8c083f12Jeff Sharkey mHoriz = Preconditions.checkNotNull(horiz, "missing horiz"); 74e6c5003278184c202833209164ddf1ae8c083f12Jeff Sharkey mVert = Preconditions.checkNotNull(vert, "missing vert"); 7552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 7652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 7754d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey public void setOptimalWidth(int optimalWidth, float optimalWidthWeight) { 7854d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey mOptimalWidth = optimalWidth; 7954d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey mOptimalWidthWeight = optimalWidthWeight; 8054d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey requestLayout(); 8154d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey } 8254d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey 8354d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey @Override 8454d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 8554d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey super.onMeasure(widthMeasureSpec, heightMeasureSpec); 8654d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey 8754d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey final int slack = getMeasuredWidth() - mOptimalWidth; 8854d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey if (mOptimalWidth > 0 && slack > 0) { 8954d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey final int targetWidth = (int) (mOptimalWidth + (slack * mOptimalWidthWeight)); 9054d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey widthMeasureSpec = MeasureSpec.makeMeasureSpec(targetWidth, MeasureSpec.EXACTLY); 9154d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey super.onMeasure(widthMeasureSpec, heightMeasureSpec); 9254d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey } 9354d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey } 9454d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey 95ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey @Override 96ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey protected void onLayout(boolean changed, int l, int t, int r, int b) { 978a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey mContent.set(getPaddingLeft(), getPaddingTop(), r - l - getPaddingRight(), 988a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey b - t - getPaddingBottom()); 99ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final int width = mContent.width(); 100ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final int height = mContent.height(); 101ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 102ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey // no scrolling yet, so tell dimensions to fill exactly 103ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey mHoriz.setSize(width); 104ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey mVert.setSize(height); 105ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 106ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final Rect parentRect = new Rect(); 107ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final Rect childRect = new Rect(); 108ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 109ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey for (int i = 0; i < getChildCount(); i++) { 110ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final View child = getChildAt(i); 111ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final LayoutParams params = (LayoutParams) child.getLayoutParams(); 112ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 113ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey parentRect.set(mContent); 114ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 115b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey if (child instanceof ChartNetworkSeriesView) { 116ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey // series are always laid out to fill entire graph area 117ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey // TODO: handle scrolling for series larger than content area 118ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey Gravity.apply(params.gravity, width, height, parentRect, childRect); 119ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); 120ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 121b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey } else if (child instanceof ChartGridView) { 122b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey // Grid uses some extra room for labels 123b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey Gravity.apply(params.gravity, width, height, parentRect, childRect); 124b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey child.layout(childRect.left, childRect.top, childRect.right, 125b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey childRect.bottom + child.getPaddingBottom()); 126b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey 127ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } else if (child instanceof ChartSweepView) { 12855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey layoutSweep((ChartSweepView) child, parentRect, childRect); 12955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey child.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); 130ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 13155d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 13255d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 13355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 13455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey protected void layoutSweep(ChartSweepView sweep) { 13555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey final Rect parentRect = new Rect(mContent); 13655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey final Rect childRect = new Rect(); 13755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 13855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey layoutSweep(sweep, parentRect, childRect); 13955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey sweep.layout(childRect.left, childRect.top, childRect.right, childRect.bottom); 14055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 141ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 14255d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey protected void layoutSweep(ChartSweepView sweep, Rect parentRect, Rect childRect) { 14355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey final Rect sweepMargins = sweep.getMargins(); 14455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 14555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey // sweep is always placed along specific dimension 14655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey if (sweep.getFollowAxis() == ChartSweepView.VERTICAL) { 14755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey parentRect.top += sweepMargins.top + (int) sweep.getPoint(); 14855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey parentRect.bottom = parentRect.top; 14955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey parentRect.left += sweepMargins.left; 15055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey parentRect.right += sweepMargins.right; 15155d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey Gravity.apply(SWEEP_GRAVITY, parentRect.width(), sweep.getMeasuredHeight(), 15255d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey parentRect, childRect); 15355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 15455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } else { 15555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey parentRect.left += sweepMargins.left + (int) sweep.getPoint(); 15655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey parentRect.right = parentRect.left; 15755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey parentRect.top += sweepMargins.top; 15855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey parentRect.bottom += sweepMargins.bottom; 15955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey Gravity.apply(SWEEP_GRAVITY, sweep.getMeasuredWidth(), parentRect.height(), 16055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey parentRect, childRect); 161ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 162ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 163ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey} 164