1adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project/* 2320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * Copyright (C) 2011 The Android Open Source Project 3320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * 4320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * Licensed under the Apache License, Version 2.0 (the "License"); 5320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * you may not use this file except in compliance with the License. 6320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * You may obtain a copy of the License at 7320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * 8adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * http://www.apache.org/licenses/LICENSE-2.0 9320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * 10adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Unless required by applicable law or agreed to in writing, software 11adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * distributed under the License is distributed on an "AS IS" BASIS, 12adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * See the License for the specific language governing permissions and 14adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * limitations under the License. 15adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project */ 16adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 17adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpackage com.android.settings.widget; 18320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 19320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport static android.net.TrafficStats.MB_IN_BYTES; 20adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 21adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport android.content.Context; 22adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport android.content.res.Resources; 23adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport android.net.NetworkPolicy; 24320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.net.NetworkStatsHistory; 25320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.net.TrafficStats; 26adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport android.os.Handler; 27320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.os.Message; 28320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.text.Spannable; 29320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.text.SpannableStringBuilder; 30320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.text.TextUtils; 31320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.text.format.DateUtils; 32adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport android.text.format.Formatter; 33adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport android.text.format.Formatter.BytesResult; 34320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.text.format.Time; 35adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport android.util.AttributeSet; 36320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.util.Log; 37320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.util.MathUtils; 38320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.view.MotionEvent; 39320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport android.view.View; 40320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 41320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport com.android.settings.R; 42f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughesimport com.android.settings.widget.ChartSweepView.OnSweepListener; 43320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 44320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilsonimport java.util.Arrays; 45adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Calendar; 46adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectimport java.util.Objects; 47320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 48320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson/** 49320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * Specific {@link ChartView} that displays {@link ChartNetworkSeriesView} along 50320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * with {@link ChartSweepView} for inspection ranges and warning/limits. 51320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson */ 52adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Projectpublic class ChartDataUsageView extends ChartView { 53320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 54320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private static final int MSG_UPDATE_AXIS = 100; 55320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private static final long DELAY_MILLIS = 250; 56320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 57320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private ChartGridView mGrid; 58adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project private ChartNetworkSeriesView mSeries; 59adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project private ChartNetworkSeriesView mDetailSeries; 60320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 61320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private NetworkStatsHistory mHistory; 62320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 63320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private ChartSweepView mSweepWarning; 64320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private ChartSweepView mSweepLimit; 65320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 66320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private long mInspectStart; 67320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private long mInspectEnd; 68320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 69320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private Handler mHandler; 70320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 71320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson /** Current maximum value of {@link #mVert}. */ 72320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private long mVertMax; 73320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 74320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public interface DataUsageChartListener { 75320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public void onWarningChanged(); 76320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public void onLimitChanged(); 77320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public void requestWarningEdit(); 78320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public void requestLimitEdit(); 79320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 80320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 81320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private DataUsageChartListener mListener; 82320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 83320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public ChartDataUsageView(Context context) { 84320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson this(context, null, 0); 85320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 86adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 87320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public ChartDataUsageView(Context context, AttributeSet attrs) { 88320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson this(context, attrs, 0); 89320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 90320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 91320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public ChartDataUsageView(Context context, AttributeSet attrs, int defStyle) { 92320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson super(context, attrs, defStyle); 93adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project init(new TimeAxis(), new InvertedChartAxis(new DataAxis())); 94f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 95320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mHandler = new Handler() { 96320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson @Override 97f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes public void handleMessage(Message msg) { 98f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes final ChartSweepView sweep = (ChartSweepView) msg.obj; 99adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project updateVertAxisBounds(sweep); 100320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson updateEstimateVisible(); 101320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 102320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // we keep dispatching repeating updates until sweep is dropped 103adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project sendUpdateAxisDelayed(sweep, true); 104320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 105320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson }; 106adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 107f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 108320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson @Override 109320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson protected void onFinishInflate() { 110320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson super.onFinishInflate(); 111adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 112320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mGrid = (ChartGridView) findViewById(R.id.grid); 113320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSeries = (ChartNetworkSeriesView) findViewById(R.id.series); 114320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mDetailSeries = (ChartNetworkSeriesView) findViewById(R.id.detail_series); 115320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mDetailSeries.setVisibility(View.GONE); 116f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 117320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepLimit = (ChartSweepView) findViewById(R.id.sweep_limit); 118320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning = (ChartSweepView) findViewById(R.id.sweep_warning); 119320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 120320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // prevent sweeps from crossing each other 121320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning.setValidRangeDynamic(null, mSweepLimit); 122320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepLimit.setValidRangeDynamic(mSweepWarning, null); 123320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 124320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // mark neighbors for checking touch events against 125f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes mSweepLimit.setNeighbors(mSweepWarning); 126320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning.setNeighbors(mSweepLimit); 127320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 128320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning.addOnSweepListener(mVertListener); 129320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepLimit.addOnSweepListener(mVertListener); 130320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 131320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning.setDragInterval(5 * MB_IN_BYTES); 132320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepLimit.setDragInterval(5 * MB_IN_BYTES); 133f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 134320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // tell everyone about our axis 135320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mGrid.init(mHoriz, mVert); 136320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSeries.init(mHoriz, mVert); 137f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes mDetailSeries.init(mHoriz, mVert); 138320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning.init(mVert); 139320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepLimit.init(mVert); 140320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 141320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson setActivated(false); 142adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 143320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 144320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public void setListener(DataUsageChartListener listener) { 145320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mListener = listener; 146320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 147320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 148320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public void bindNetworkStats(NetworkStatsHistory stats) { 149320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSeries.bindNetworkStats(stats); 150320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mHistory = stats; 151320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson updateVertAxisBounds(null); 152320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson updateEstimateVisible(); 153320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson updatePrimaryRange(); 154320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson requestLayout(); 155adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 156320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 157320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public void bindDetailNetworkStats(NetworkStatsHistory stats) { 158320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mDetailSeries.bindNetworkStats(stats); 159adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mDetailSeries.setVisibility(stats != null ? View.VISIBLE : View.GONE); 160adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (mHistory != null) { 161adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mDetailSeries.setEndTime(mHistory.getEnd()); 162320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 163320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson updateVertAxisBounds(null); 164320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson updateEstimateVisible(); 165320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson updatePrimaryRange(); 166320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson requestLayout(); 167320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 168adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 169f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes public void bindNetworkPolicy(NetworkPolicy policy) { 170320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson if (policy == null) { 171320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepLimit.setVisibility(View.INVISIBLE); 172adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mSweepLimit.setValue(-1); 173adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mSweepWarning.setVisibility(View.INVISIBLE); 174adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mSweepWarning.setValue(-1); 175320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return; 176320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 177320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 178f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes if (policy.limitBytes != NetworkPolicy.LIMIT_DISABLED) { 179320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepLimit.setVisibility(View.VISIBLE); 180adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mSweepLimit.setEnabled(true); 181f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes mSweepLimit.setValue(policy.limitBytes); 182320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } else { 183320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepLimit.setVisibility(View.INVISIBLE); 184adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mSweepLimit.setEnabled(false); 185adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mSweepLimit.setValue(-1); 186adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 187320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 188320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson if (policy.warningBytes != NetworkPolicy.WARNING_DISABLED) { 189320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning.setVisibility(View.VISIBLE); 190320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning.setValue(policy.warningBytes); 191320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } else { 192320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning.setVisibility(View.INVISIBLE); 193320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning.setValue(-1); 194320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 195adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 196f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes updateVertAxisBounds(null); 197320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson requestLayout(); 198320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson invalidate(); 199adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 200adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 201adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 202320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * Update {@link #mVert} to both show data from {@link NetworkStatsHistory} 203320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * and controls from {@link NetworkPolicy}. 204320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson */ 205320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private void updateVertAxisBounds(ChartSweepView activeSweep) { 206320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson final long max = mVertMax; 207adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 208f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes long newMax = 0; 209adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (activeSweep != null) { 210adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project final int adjustAxis = activeSweep.shouldAdjustAxis(); 211adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (adjustAxis > 0) { 212adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project // hovering around upper edge, grow axis 213adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project newMax = max * 11 / 10; 214320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } else if (adjustAxis < 0) { 215320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // hovering around lower edge, shrink axis 216320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson newMax = max * 9 / 10; 217320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } else { 218320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson newMax = max; 219adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 220f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes } 221adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 222320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // always show known data and policy lines 223adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project final long maxSweep = Math.max(mSweepWarning.getValue(), mSweepLimit.getValue()); 224adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project final long maxSeries = Math.max(mSeries.getMaxVisible(), mDetailSeries.getMaxVisible()); 225adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project final long maxVisible = Math.max(maxSeries, maxSweep) * 12 / 10; 226320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson final long maxDefault = Math.max(maxVisible, 50 * MB_IN_BYTES); 227320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson newMax = Math.max(maxDefault, newMax); 228320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 229320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // only invalidate when vertMax actually changed 230320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson if (newMax != mVertMax) { 231320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mVertMax = newMax; 232adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 233f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes final boolean changed = mVert.setBounds(0L, newMax); 234320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepWarning.setValidRange(0L, newMax); 235320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSweepLimit.setValidRange(0L, newMax); 236adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 237adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (changed) { 238adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mSeries.invalidatePath(); 239320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mDetailSeries.invalidatePath(); 240320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 241320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 242320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mGrid.invalidate(); 243320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 244adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project // since we just changed axis, make sweep recalculate its value 245f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes if (activeSweep != null) { 246320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson activeSweep.updateValueFromPosition(); 247320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 248320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 249adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project // layout other sweeps to match changed axis 250adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project // TODO: find cleaner way of doing this, such as requesting full 251320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // layout and making activeSweep discard its tracking MotionEvent. 252320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson if (mSweepLimit != activeSweep) { 253320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson layoutSweep(mSweepLimit); 254320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 255320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson if (mSweepWarning != activeSweep) { 256adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project layoutSweep(mSweepWarning); 257f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes } 258320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 259320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 260adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 261adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project /** 262adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Control {@link ChartNetworkSeriesView#setEstimateVisible(boolean)} based 263320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * on how close estimate comes to {@link #mSweepWarning}. 264320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson */ 265320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private void updateEstimateVisible() { 266320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson final long maxEstimate = mSeries.getMaxEstimate(); 267320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 268320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // show estimate when near warning/limit 269320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson long interestLine = Long.MAX_VALUE; 270adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (mSweepWarning.isEnabled()) { 271f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes interestLine = mSweepWarning.getValue(); 272320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } else if (mSweepLimit.isEnabled()) { 273320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson interestLine = mSweepLimit.getValue(); 274320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 275adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 276adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (interestLine < 0) { 277320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson interestLine = Long.MAX_VALUE; 278320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 279320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 280320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson final boolean estimateVisible = (maxEstimate >= interestLine * 7 / 10); 281320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSeries.setEstimateVisible(estimateVisible); 282320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 283adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 284320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private void sendUpdateAxisDelayed(ChartSweepView sweep, boolean force) { 285320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson if (force || !mHandler.hasMessages(MSG_UPDATE_AXIS, sweep)) { 286adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mHandler.sendMessageDelayed( 287adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mHandler.obtainMessage(MSG_UPDATE_AXIS, sweep), DELAY_MILLIS); 288320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 289320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 290320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 291320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private void clearUpdateAxisDelayed(ChartSweepView sweep) { 292320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mHandler.removeMessages(MSG_UPDATE_AXIS, sweep); 293320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 294adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 295320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private OnSweepListener mVertListener = new OnSweepListener() { 296320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson @Override 297f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes public void onSweep(ChartSweepView sweep, boolean sweepDone) { 298d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes if (sweepDone) { 299d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes clearUpdateAxisDelayed(sweep); 300f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes updateEstimateVisible(); 301d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes 302d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes if (sweep == mSweepWarning && mListener != null) { 303d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes mListener.onWarningChanged(); 304d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } else if (sweep == mSweepLimit && mListener != null) { 305d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes mListener.onLimitChanged(); 306d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 307d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } else { 308f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes // while moving, kick off delayed grow/shrink axis updates 309d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes sendUpdateAxisDelayed(sweep, false); 310d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 311d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 312d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes 313d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes @Override 314d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes public void requestEdit(ChartSweepView sweep) { 315d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes if (sweep == mSweepWarning && mListener != null) { 316d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes mListener.requestWarningEdit(); 317d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } else if (sweep == mSweepLimit && mListener != null) { 318d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes mListener.requestLimitEdit(); 319d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 320d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 321d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes }; 322d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes 323d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes @Override 324d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes public boolean onTouchEvent(MotionEvent event) { 325f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes if (isActivated()) return false; 326d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes switch (event.getAction()) { 327d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes case MotionEvent.ACTION_DOWN: { 328f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes return true; 329d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 330d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes case MotionEvent.ACTION_UP: { 331320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson setActivated(true); 332d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes return true; 333d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 334d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes default: { 335320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return false; 336d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 337d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 338f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes } 339d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes 340d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes public long getInspectStart() { 341d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes return mInspectStart; 342d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 343d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes 344d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes public long getInspectEnd() { 345d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes return mInspectEnd; 346f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes } 347d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes 348f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes public long getWarningBytes() { 349d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes return mSweepWarning.getLabelValue(); 350f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes } 351d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes 352d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes public long getLimitBytes() { 353d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes return mSweepLimit.getLabelValue(); 354d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes } 355d21d78fd49a2d798218e8c8aefbddb26a0e71bbbElliott Hughes 356f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes /** 357adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project * Set the exact time range that should be displayed, updating how 358320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * {@link ChartNetworkSeriesView} paints. Moves inspection ranges to be the 359320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson * last "week" of available data, without triggering listener events. 360f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes */ 361f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes public void setVisibleRange(long visibleStart, long visibleEnd) { 362320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson final boolean changed = mHoriz.setBounds(visibleStart, visibleEnd); 363320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mGrid.setBounds(visibleStart, visibleEnd); 364320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSeries.setBounds(visibleStart, visibleEnd); 365320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mDetailSeries.setBounds(visibleStart, visibleEnd); 366f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 367320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mInspectStart = visibleStart; 368320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mInspectEnd = visibleEnd; 369320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 370320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson requestLayout(); 371320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson if (changed) { 372f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes mSeries.invalidatePath(); 373320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mDetailSeries.invalidatePath(); 374adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project } 375320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 376320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson updateVertAxisBounds(null); 377320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson updateEstimateVisible(); 378320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson updatePrimaryRange(); 379320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 380320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 381320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private void updatePrimaryRange() { 382320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // prefer showing primary range on detail series, when available 383adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (mDetailSeries.getVisibility() == View.VISIBLE) { 384adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mSeries.setSecondary(true); 385f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes } else { 386adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project mSeries.setSecondary(false); 387320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 388320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 389f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes 390320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public static class TimeAxis implements ChartAxis { 391320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private static final int FIRST_DAY_OF_WEEK = Calendar.getInstance().getFirstDayOfWeek() - 1; 392320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 393320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private long mMin; 394f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes private long mMax; 395320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson private float mSize; 396320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 397320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public TimeAxis() { 398320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson final long currentTime = System.currentTimeMillis(); 399320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson setBounds(currentTime - DateUtils.DAY_IN_MILLIS * 30, currentTime); 400320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 401320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 402320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson @Override 403f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes public int hashCode() { 404320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return Objects.hash(mMin, mMax, mSize); 405320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 406320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 407320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson @Override 408f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes public boolean setBounds(long min, long max) { 409f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes if (mMin != min || mMax != max) { 410320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mMin = min; 411f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes mMax = max; 412f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes return true; 413320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } else { 414320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return false; 415320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 416f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes } 417320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 418320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson @Override 419320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public boolean setSize(float size) { 420320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson if (mSize != size) { 421320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mSize = size; 422320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return true; 423320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } else { 424f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes return false; 425320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 426320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 427320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 428320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson @Override 429320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public float convertToPoint(long value) { 430320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return (mSize * (value - mMin)) / (mMax - mMin); 431f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes } 432320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 433f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes @Override 434320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public long convertToValue(float point) { 435320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return (long) (mMin + ((point * (mMax - mMin)) / mSize)); 436320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 437320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 438f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes @Override 439320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public long buildLabel(Resources res, SpannableStringBuilder builder, long value) { 440adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project // TODO: convert to better string 441320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson builder.replace(0, builder.length(), Long.toString(value)); 442320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return value; 443320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 444320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 445320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson @Override 446320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public float[] getTickPoints() { 447320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson final float[] ticks = new float[32]; 448320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson int i = 0; 449adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 450f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes // tick mark for first day of each week 451adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project final Time time = new Time(); 452320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson time.set(mMax); 453f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes time.monthDay -= time.weekDay - FIRST_DAY_OF_WEEK; 454320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson time.hour = time.minute = time.second = 0; 455320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 456320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson time.normalize(true); 457f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes long timeMillis = time.toMillis(true); 458320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson while (timeMillis > mMin) { 459f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes if (timeMillis <= mMax) { 460320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson ticks[i++] = convertToPoint(timeMillis); 461320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 462f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes time.monthDay -= 7; 463320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson time.normalize(true); 464320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson timeMillis = time.toMillis(true); 465320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 466320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 467f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes return Arrays.copyOf(ticks, i); 468320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 469adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project 470320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson @Override 471320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public int shouldAdjustAxis(long value) { 472320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson // time axis never adjusts 473320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return 0; 474320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 475320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 476320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 477320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public static class DataAxis implements ChartAxis { 478adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project private long mMin; 479adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project private long mMax; 480adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project private float mSize; 481320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 482f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes private static final boolean LOG_SCALE = false; 483320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 484f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes @Override 485320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public int hashCode() { 486320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return Objects.hash(mMin, mMax, mSize); 487320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 488320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 489f33eae7e84eb6d3b0f4e86b59605bb3de73009f3Elliott Hughes @Override 490320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson public boolean setBounds(long min, long max) { 491adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project if (mMin != min || mMax != max) { 492320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mMin = min; 493320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson mMax = max; 494320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return true; 495320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } else { 496320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson return false; 497320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 498320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson } 499320c9890e8241fb0ad05de6fa5e6c3eb3aece159Jesse Wilson 500adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project @Override 501adc854b798c1cfe3bfd4c27d68d5cee38ca617daThe Android Open Source Project public boolean setSize(float size) { 502 if (mSize != size) { 503 mSize = size; 504 return true; 505 } else { 506 return false; 507 } 508 } 509 510 @Override 511 public float convertToPoint(long value) { 512 if (LOG_SCALE) { 513 // derived polynomial fit to make lower values more visible 514 final double normalized = ((double) value - mMin) / (mMax - mMin); 515 final double fraction = Math.pow(10, 516 0.36884343106175121463 * Math.log10(normalized) + -0.04328199452018252624); 517 return (float) (fraction * mSize); 518 } else { 519 return (mSize * (value - mMin)) / (mMax - mMin); 520 } 521 } 522 523 @Override 524 public long convertToValue(float point) { 525 if (LOG_SCALE) { 526 final double normalized = point / mSize; 527 final double fraction = 1.3102228476089056629 528 * Math.pow(normalized, 2.7111774693164631640); 529 return (long) (mMin + (fraction * (mMax - mMin))); 530 } else { 531 return (long) (mMin + ((point * (mMax - mMin)) / mSize)); 532 } 533 } 534 535 private static final Object sSpanSize = new Object(); 536 private static final Object sSpanUnit = new Object(); 537 538 @Override 539 public long buildLabel(Resources res, SpannableStringBuilder builder, long value) { 540 value = MathUtils.constrain(value, 0, TrafficStats.TB_IN_BYTES); 541 final BytesResult result = Formatter.formatBytes(res, value, 542 Formatter.FLAG_SHORTER | Formatter.FLAG_CALCULATE_ROUNDED); 543 setText(builder, sSpanSize, result.value, "^1"); 544 setText(builder, sSpanUnit, result.units, "^2"); 545 return result.roundedBytes; 546 } 547 548 @Override 549 public float[] getTickPoints() { 550 final long range = mMax - mMin; 551 552 // target about 16 ticks on screen, rounded to nearest power of 2 553 final long tickJump = roundUpToPowerOfTwo(range / 16); 554 final int tickCount = (int) (range / tickJump); 555 final float[] tickPoints = new float[tickCount]; 556 long value = mMin; 557 for (int i = 0; i < tickPoints.length; i++) { 558 tickPoints[i] = convertToPoint(value); 559 value += tickJump; 560 } 561 562 return tickPoints; 563 } 564 565 @Override 566 public int shouldAdjustAxis(long value) { 567 final float point = convertToPoint(value); 568 if (point < mSize * 0.1) { 569 return -1; 570 } else if (point > mSize * 0.85) { 571 return 1; 572 } else { 573 return 0; 574 } 575 } 576 } 577 578 private static void setText( 579 SpannableStringBuilder builder, Object key, CharSequence text, String bootstrap) { 580 int start = builder.getSpanStart(key); 581 int end = builder.getSpanEnd(key); 582 if (start == -1) { 583 start = TextUtils.indexOf(builder, bootstrap); 584 end = start + bootstrap.length(); 585 builder.setSpan(key, start, end, Spannable.SPAN_INCLUSIVE_INCLUSIVE); 586 } 587 builder.replace(start, end, text); 588 } 589 590 private static long roundUpToPowerOfTwo(long i) { 591 // NOTE: borrowed from Hashtable.roundUpToPowerOfTwo() 592 593 i--; // If input is a power of two, shift its high-order bit right 594 595 // "Smear" the high-order bit all the way to the right 596 i |= i >>> 1; 597 i |= i >>> 2; 598 i |= i >>> 4; 599 i |= i >>> 8; 600 i |= i >>> 16; 601 i |= i >>> 32; 602 603 i++; 604 605 return i > 0 ? i : Long.MAX_VALUE; 606 } 607} 608