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; 2052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.content.res.TypedArray; 21ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.graphics.Canvas; 22ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkeyimport android.graphics.Color; 23ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkeyimport android.graphics.Paint; 2455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkeyimport android.graphics.Paint.Style; 25ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkeyimport android.graphics.Point; 2652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.graphics.Rect; 2752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.graphics.drawable.Drawable; 28ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkeyimport android.text.DynamicLayout; 29bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkeyimport android.text.Layout; 30ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkeyimport android.text.Layout.Alignment; 31ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkeyimport android.text.SpannableStringBuilder; 32ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkeyimport android.text.TextPaint; 3352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.util.AttributeSet; 3452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport android.util.MathUtils; 35ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.view.MotionEvent; 36ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport android.view.View; 37ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 3852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport com.android.settings.R; 39ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkeyimport com.google.common.base.Preconditions; 40ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 41ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey/** 42ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * Sweep across a {@link ChartView} at a specific {@link ChartAxis} value, which 43ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey * a user can drag. 44ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey */ 4554d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkeypublic class ChartSweepView extends View { 46ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 4755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey private static final boolean DRAW_OUTLINE = false; 4855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 495d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey // TODO: clean up all the various padding/offset/margins 505d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey 5152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey private Drawable mSweep; 52ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private Rect mSweepPadding = new Rect(); 5355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 5455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey /** Offset of content inside this view. */ 555d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey private Rect mContentOffset = new Rect(); 5655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey /** Offset of {@link #mSweep} inside this view. */ 57ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private Point mSweepOffset = new Point(); 58ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 59ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private Rect mMargins = new Rect(); 60e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey private float mNeighborMargin; 61f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 6252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey private int mFollowAxis; 63ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 64bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey private int mLabelMinSize; 65bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey private float mLabelSize; 66bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey 67ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private int mLabelTemplateRes; 68ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private int mLabelColor; 69ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 70ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private SpannableStringBuilder mLabelTemplate; 71ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private DynamicLayout mLabelLayout; 7252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 7352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey private ChartAxis mAxis; 74ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey private long mValue; 7528130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey private long mLabelValue; 76ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 77d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey private long mValidAfter; 78d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey private long mValidBefore; 79d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey private ChartSweepView mValidAfterDynamic; 80d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey private ChartSweepView mValidBeforeDynamic; 814ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 82b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey private float mLabelOffset; 83b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey 8455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey private Paint mOutlinePaint = new Paint(); 8555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 8652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public static final int HORIZONTAL = 0; 8752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public static final int VERTICAL = 1; 8852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 89a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private int mTouchMode = MODE_NONE; 90a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 91a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private static final int MODE_NONE = 0; 92a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private static final int MODE_DRAG = 1; 93a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private static final int MODE_LABEL = 2; 94a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 95bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey private static final int LARGE_WIDTH = 1024; 96bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey 97a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private long mDragInterval = 1; 98a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 99ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public interface OnSweepListener { 100ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public void onSweep(ChartSweepView sweep, boolean sweepDone); 101a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey public void requestEdit(ChartSweepView sweep); 102ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 103ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 104ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey private OnSweepListener mListener; 105a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 106a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private float mTrackingStart; 107ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey private MotionEvent mTracking; 108ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 109461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey private ChartSweepView[] mNeighbors = new ChartSweepView[0]; 110461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey 11152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public ChartSweepView(Context context) { 11254d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey this(context, null); 11352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 114ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 11552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public ChartSweepView(Context context, AttributeSet attrs) { 11652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey this(context, attrs, 0); 11752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 118ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 11952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public ChartSweepView(Context context, AttributeSet attrs, int defStyle) { 12052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey super(context, attrs, defStyle); 121ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 12252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey final TypedArray a = context.obtainStyledAttributes( 12352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey attrs, R.styleable.ChartSweepView, defStyle, 0); 1248a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey 12552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey setSweepDrawable(a.getDrawable(R.styleable.ChartSweepView_sweepDrawable)); 12652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey setFollowAxis(a.getInt(R.styleable.ChartSweepView_followAxis, -1)); 127e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey setNeighborMargin(a.getDimensionPixelSize(R.styleable.ChartSweepView_neighborMargin, 0)); 128ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 129bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey setLabelMinSize(a.getDimensionPixelSize(R.styleable.ChartSweepView_labelSize, 0)); 130ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey setLabelTemplate(a.getResourceId(R.styleable.ChartSweepView_labelTemplate, 0)); 131ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey setLabelColor(a.getColor(R.styleable.ChartSweepView_labelColor, Color.BLUE)); 13252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 133a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey // TODO: moved focused state directly into assets 134a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setBackgroundResource(R.drawable.data_usage_sweep_background); 135a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 13655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mOutlinePaint.setColor(Color.RED); 13755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mOutlinePaint.setStrokeWidth(1f); 13855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mOutlinePaint.setStyle(Style.STROKE); 13955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 14052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey a.recycle(); 14152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 142a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setClickable(true); 143a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setFocusable(true); 144a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setOnClickListener(mClickListener); 145a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 14652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey setWillNotDraw(false); 14752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 14852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 149a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private OnClickListener mClickListener = new OnClickListener() { 150a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey public void onClick(View v) { 151a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey dispatchRequestEdit(); 152a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 153a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey }; 154a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 15552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey void init(ChartAxis axis) { 15652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mAxis = Preconditions.checkNotNull(axis, "missing axis"); 15752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 158ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 159461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey public void setNeighbors(ChartSweepView... neighbors) { 160461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey mNeighbors = neighbors; 161461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey } 162461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey 16352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public int getFollowAxis() { 16452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey return mFollowAxis; 16552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 16652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 167ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey public Rect getMargins() { 168ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey return mMargins; 169f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } 170f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 171a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey public void setDragInterval(long dragInterval) { 172a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mDragInterval = dragInterval; 173a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 174a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 175f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey /** 176f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey * Return the number of pixels that the "target" area is inset from the 177f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey * {@link View} edge, along the current {@link #setFollowAxis(int)}. 178f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey */ 179ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private float getTargetInset() { 180f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey if (mFollowAxis == VERTICAL) { 181ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final float targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top 182ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey - mSweepPadding.bottom; 18355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey return mSweepPadding.top + (targetHeight / 2) + mSweepOffset.y; 184f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } else { 185ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final float targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left 186ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey - mSweepPadding.right; 18755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey return mSweepPadding.left + (targetWidth / 2) + mSweepOffset.x; 188f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } 189ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 190ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 191ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public void addOnSweepListener(OnSweepListener listener) { 192ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey mListener = listener; 193ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 194ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 195ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey private void dispatchOnSweep(boolean sweepDone) { 196ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey if (mListener != null) { 197ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey mListener.onSweep(this, sweepDone); 198ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 199ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 200ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 201a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private void dispatchRequestEdit() { 202a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (mListener != null) { 203a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mListener.requestEdit(this); 204a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 205a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 206a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 207ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey @Override 208ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey public void setEnabled(boolean enabled) { 209ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey super.setEnabled(enabled); 210a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setFocusable(enabled); 211ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey requestLayout(); 212ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 213ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 21452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public void setSweepDrawable(Drawable sweep) { 21552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (mSweep != null) { 21652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep.setCallback(null); 21752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey unscheduleDrawable(mSweep); 21852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 21952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 22052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (sweep != null) { 22152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey sweep.setCallback(this); 22252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (sweep.isStateful()) { 22352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey sweep.setState(getDrawableState()); 22452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 22552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey sweep.setVisible(getVisibility() == VISIBLE, false); 22652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep = sweep; 227ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey sweep.getPadding(mSweepPadding); 22852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } else { 22952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep = null; 23052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 23152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 23252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey invalidate(); 23352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 23452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 23552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public void setFollowAxis(int followAxis) { 23652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mFollowAxis = followAxis; 23752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 23852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 239bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey public void setLabelMinSize(int minSize) { 240bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelMinSize = minSize; 241ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidateLabelTemplate(); 242ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 243ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 244ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey public void setLabelTemplate(int resId) { 245ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelTemplateRes = resId; 246ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidateLabelTemplate(); 247ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 248ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 249ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey public void setLabelColor(int color) { 250ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelColor = color; 251ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidateLabelTemplate(); 252ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 253ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 254ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private void invalidateLabelTemplate() { 255ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (mLabelTemplateRes != 0) { 256ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final CharSequence template = getResources().getText(mLabelTemplateRes); 257ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 258ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); 259ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey paint.density = getResources().getDisplayMetrics().density; 260ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey paint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale); 261ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey paint.setColor(mLabelColor); 26255d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey paint.setShadowLayer(4 * paint.density, 0, 0, Color.BLACK); 263ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 264ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelTemplate = new SpannableStringBuilder(template); 265ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelLayout = new DynamicLayout( 266bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelTemplate, paint, LARGE_WIDTH, Alignment.ALIGN_RIGHT, 1f, 0f, false); 267ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidateLabel(); 268ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 269ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } else { 270ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelTemplate = null; 271ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelLayout = null; 272ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 273ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 27452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey invalidate(); 275ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey requestLayout(); 276ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 277ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 278ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private void invalidateLabel() { 279ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (mLabelTemplate != null && mAxis != null) { 28028130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey mLabelValue = mAxis.buildLabel(getResources(), mLabelTemplate, mValue); 281a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setContentDescription(mLabelTemplate); 282b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey invalidateLabelOffset(); 283ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidate(); 2845d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey } else { 2855d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mLabelValue = mValue; 286ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 28752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 28852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 289b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey /** 290b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey * When overlapping with neighbor, split difference and push label. 291b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey */ 292b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey public void invalidateLabelOffset() { 293b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey float margin; 294b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey float labelOffset = 0; 295b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (mFollowAxis == VERTICAL) { 296b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (mValidAfterDynamic != null) { 297bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelSize = Math.max(getLabelWidth(this), getLabelWidth(mValidAfterDynamic)); 298b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey margin = getLabelTop(mValidAfterDynamic) - getLabelBottom(this); 299b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (margin < 0) { 300b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey labelOffset = margin / 2; 301b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 302b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } else if (mValidBeforeDynamic != null) { 303bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelSize = Math.max(getLabelWidth(this), getLabelWidth(mValidBeforeDynamic)); 304b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey margin = getLabelTop(this) - getLabelBottom(mValidBeforeDynamic); 305b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (margin < 0) { 306b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey labelOffset = -margin / 2; 307b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 308bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey } else { 309bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelSize = getLabelWidth(this); 310b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 311b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } else { 312b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey // TODO: implement horizontal labels 313b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 314b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey 315bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelSize = Math.max(mLabelSize, mLabelMinSize); 316bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey 317b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey // when offsetting label, neighbor probably needs to offset too 318b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (labelOffset != mLabelOffset) { 319b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey mLabelOffset = labelOffset; 320b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey invalidate(); 321b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (mValidAfterDynamic != null) mValidAfterDynamic.invalidateLabelOffset(); 322b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (mValidBeforeDynamic != null) mValidBeforeDynamic.invalidateLabelOffset(); 323b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 324b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 325b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey 32652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey @Override 32752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public void jumpDrawablesToCurrentState() { 32852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey super.jumpDrawablesToCurrentState(); 32952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (mSweep != null) { 33052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep.jumpToCurrentState(); 33152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 33252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 33352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 33452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey @Override 33552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public void setVisibility(int visibility) { 33652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey super.setVisibility(visibility); 33752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (mSweep != null) { 33852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep.setVisible(visibility == VISIBLE, false); 33952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 34052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 34152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 34252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey @Override 34352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey protected boolean verifyDrawable(Drawable who) { 34452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey return who == mSweep || super.verifyDrawable(who); 34552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 34652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 347ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public ChartAxis getAxis() { 348ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey return mAxis; 349ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 350ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 3518a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey public void setValue(long value) { 3528a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey mValue = value; 353ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidateLabel(); 3548a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey } 3558a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey 356ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public long getValue() { 357ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey return mValue; 358ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 359ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 36028130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey public long getLabelValue() { 36128130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey return mLabelValue; 36228130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey } 36328130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey 364ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public float getPoint() { 3652af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey if (isEnabled()) { 3662af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey return mAxis.convertToPoint(mValue); 3672af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey } else { 3682af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey // when disabled, show along top edge 3692af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey return 0; 3702af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey } 371ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 372ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 373d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey /** 374d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * Set valid range this sweep can move within, in {@link #mAxis} values. The 375d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * most restrictive combination of all valid ranges is used. 376d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey */ 377d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey public void setValidRange(long validAfter, long validBefore) { 378d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey mValidAfter = validAfter; 379d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey mValidBefore = validBefore; 3804ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey } 3814ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 382e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey public void setNeighborMargin(float neighborMargin) { 383e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey mNeighborMargin = neighborMargin; 384e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 385e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 386d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey /** 387d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * Set valid range this sweep can move within, defined by the given 388d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * {@link ChartSweepView}. The most restrictive combination of all valid 389d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * ranges is used. 390d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey */ 391e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey public void setValidRangeDynamic(ChartSweepView validAfter, ChartSweepView validBefore) { 392d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey mValidAfterDynamic = validAfter; 393d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey mValidBeforeDynamic = validBefore; 3944ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey } 3954ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 39655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey /** 39755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey * Test if given {@link MotionEvent} is closer to another 39855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey * {@link ChartSweepView} compared to ourselves. 39955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey */ 40055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey public boolean isTouchCloserTo(MotionEvent eventInParent, ChartSweepView another) { 401461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey final float selfDist = getTouchDistanceFromTarget(eventInParent); 402461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey final float anotherDist = another.getTouchDistanceFromTarget(eventInParent); 403461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey return anotherDist < selfDist; 404461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey } 40555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 406461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey private float getTouchDistanceFromTarget(MotionEvent eventInParent) { 40755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey if (mFollowAxis == HORIZONTAL) { 408461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey return Math.abs(eventInParent.getX() - (getX() + getTargetInset())); 40955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } else { 410461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey return Math.abs(eventInParent.getY() - (getY() + getTargetInset())); 41155d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 41255d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 41355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 414ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey @Override 415ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public boolean onTouchEvent(MotionEvent event) { 4168a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey if (!isEnabled()) return false; 4178a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey 418ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final View parent = (View) getParent(); 419ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey switch (event.getAction()) { 420ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey case MotionEvent.ACTION_DOWN: { 421f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 422f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey // only start tracking when in sweet spot 423a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey final boolean acceptDrag; 424a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey final boolean acceptLabel; 425f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey if (mFollowAxis == VERTICAL) { 426a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey acceptDrag = event.getX() > getWidth() - (mSweepPadding.right * 8); 427a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey acceptLabel = mLabelLayout != null ? event.getX() < mLabelLayout.getWidth() 428a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey : false; 429f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } else { 430a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey acceptDrag = event.getY() > getHeight() - (mSweepPadding.bottom * 8); 431a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey acceptLabel = mLabelLayout != null ? event.getY() < mLabelLayout.getHeight() 432a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey : false; 43355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 43455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 43555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey final MotionEvent eventInParent = event.copy(); 43655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey eventInParent.offsetLocation(getLeft(), getTop()); 43755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 43855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey // ignore event when closer to a neighbor 439461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey for (ChartSweepView neighbor : mNeighbors) { 440461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey if (isTouchCloserTo(eventInParent, neighbor)) { 441461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey return false; 442461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey } 443f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } 444f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 445a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (acceptDrag) { 446a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (mFollowAxis == VERTICAL) { 447a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTrackingStart = getTop() - mMargins.top; 448a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } else { 449a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTrackingStart = getLeft() - mMargins.left; 450a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 451f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey mTracking = event.copy(); 452a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTouchMode = MODE_DRAG; 453d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 454d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey // starting drag should activate entire chart 455d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey if (!parent.isActivated()) { 456d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey parent.setActivated(true); 457d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 458d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 459f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey return true; 460a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } else if (acceptLabel) { 461a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTouchMode = MODE_LABEL; 462a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey return true; 463f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } else { 464a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTouchMode = MODE_NONE; 465f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey return false; 466f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } 467ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 468ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey case MotionEvent.ACTION_MOVE: { 469a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (mTouchMode == MODE_LABEL) { 470a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey return true; 471a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 472a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 4738a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey getParent().requestDisallowInterceptTouchEvent(true); 4748a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey 475f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey // content area of parent 476e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final Rect parentContent = getParentContentRect(); 4774ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey final Rect clampRect = computeClampRect(parentContent); 478a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (clampRect.isEmpty()) return true; 479f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 480a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey long value; 48152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (mFollowAxis == VERTICAL) { 482ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final float currentTargetY = getTop() - mMargins.top; 483a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey final float requestedTargetY = mTrackingStart 484f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey + (event.getRawY() - mTracking.getRawY()); 485f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey final float clampedTargetY = MathUtils.constrain( 4864ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey requestedTargetY, clampRect.top, clampRect.bottom); 487f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey setTranslationY(clampedTargetY - currentTargetY); 488f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 489a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey value = mAxis.convertToValue(clampedTargetY - parentContent.top); 490ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } else { 491ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final float currentTargetX = getLeft() - mMargins.left; 492a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey final float requestedTargetX = mTrackingStart 493f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey + (event.getRawX() - mTracking.getRawX()); 494f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey final float clampedTargetX = MathUtils.constrain( 4954ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey requestedTargetX, clampRect.left, clampRect.right); 496f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey setTranslationX(clampedTargetX - currentTargetX); 497f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 498a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey value = mAxis.convertToValue(clampedTargetX - parentContent.left); 499ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 500ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 501a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey // round value from drag to nearest increment 502a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey value -= value % mDragInterval; 503a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setValue(value); 504a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 505ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey dispatchOnSweep(false); 506ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey return true; 507ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 508ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey case MotionEvent.ACTION_UP: { 509a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (mTouchMode == MODE_LABEL) { 510a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey performClick(); 511a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } else if (mTouchMode == MODE_DRAG) { 512a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTrackingStart = 0; 513a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTracking = null; 514a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mValue = mLabelValue; 515a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey dispatchOnSweep(true); 516a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setTranslationX(0); 517a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setTranslationY(0); 518a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey requestLayout(); 519a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 520a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 521a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTouchMode = MODE_NONE; 522ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey return true; 523ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 524ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey default: { 525ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey return false; 526ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 527ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 528ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 529ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 530e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey /** 531e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey * Update {@link #mValue} based on current position, including any 532e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey * {@link #onTouchEvent(MotionEvent)} in progress. Typically used when 533e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey * {@link ChartAxis} changes during sweep adjustment. 534e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey */ 535e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey public void updateValueFromPosition() { 536e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final Rect parentContent = getParentContentRect(); 537e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (mFollowAxis == VERTICAL) { 538e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final float effectiveY = getY() - mMargins.top - parentContent.top; 539e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey setValue(mAxis.convertToValue(effectiveY)); 540e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } else { 541e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final float effectiveX = getX() - mMargins.left - parentContent.left; 542e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey setValue(mAxis.convertToValue(effectiveX)); 543e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 544e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 545e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 546e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey public int shouldAdjustAxis() { 547e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey return mAxis.shouldAdjustAxis(getValue()); 548e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 549e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 550e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey private Rect getParentContentRect() { 551e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final View parent = (View) getParent(); 552e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey return new Rect(parent.getPaddingLeft(), parent.getPaddingTop(), 553e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey parent.getWidth() - parent.getPaddingRight(), 554e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey parent.getHeight() - parent.getPaddingBottom()); 555e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 556e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 557d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey @Override 558d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey public void addOnLayoutChangeListener(OnLayoutChangeListener listener) { 559d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey // ignored to keep LayoutTransition from animating us 560d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 561d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 562d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey @Override 563d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey public void removeOnLayoutChangeListener(OnLayoutChangeListener listener) { 564d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey // ignored to keep LayoutTransition from animating us 565d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 566d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 567e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey private long getValidAfterDynamic() { 568d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey final ChartSweepView dynamic = mValidAfterDynamic; 569e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey return dynamic != null && dynamic.isEnabled() ? dynamic.getValue() : Long.MIN_VALUE; 570d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 571d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 572e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey private long getValidBeforeDynamic() { 573d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey final ChartSweepView dynamic = mValidBeforeDynamic; 574e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey return dynamic != null && dynamic.isEnabled() ? dynamic.getValue() : Long.MAX_VALUE; 575d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 576d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 5774ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey /** 5784ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey * Compute {@link Rect} in {@link #getParent()} coordinates that we should 579d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * be clamped inside of, usually from {@link #setValidRange(long, long)} 5804ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey * style rules. 5814ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey */ 5824ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey private Rect computeClampRect(Rect parentContent) { 583e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey // create two rectangles, and pick most restrictive combination 584e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final Rect rect = buildClampRect(parentContent, mValidAfter, mValidBefore, 0f); 585e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final Rect dynamicRect = buildClampRect( 586e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey parentContent, getValidAfterDynamic(), getValidBeforeDynamic(), mNeighborMargin); 5874ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 588a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (!rect.intersect(dynamicRect)) { 589a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey rect.setEmpty(); 590a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 591e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey return rect; 592e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 593e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 594e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey private Rect buildClampRect( 595e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey Rect parentContent, long afterValue, long beforeValue, float margin) { 596e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (mAxis instanceof InvertedChartAxis) { 597e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey long temp = beforeValue; 598e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey beforeValue = afterValue; 599e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey afterValue = temp; 600d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 6014ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 602e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final boolean afterValid = afterValue != Long.MIN_VALUE && afterValue != Long.MAX_VALUE; 603e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final boolean beforeValid = beforeValue != Long.MIN_VALUE && beforeValue != Long.MAX_VALUE; 604e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 605e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final float afterPoint = mAxis.convertToPoint(afterValue) + margin; 606e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final float beforePoint = mAxis.convertToPoint(beforeValue) - margin; 607e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 608e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final Rect clampRect = new Rect(parentContent); 6094ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey if (mFollowAxis == VERTICAL) { 610e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (beforeValid) clampRect.bottom = clampRect.top + (int) beforePoint; 611e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (afterValid) clampRect.top += afterPoint; 6124ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey } else { 613e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (beforeValid) clampRect.right = clampRect.left + (int) beforePoint; 614e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (afterValid) clampRect.left += afterPoint; 6154ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey } 6164ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey return clampRect; 6174ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey } 6184ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 619ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey @Override 62052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey protected void drawableStateChanged() { 62152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey super.drawableStateChanged(); 62252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (mSweep.isStateful()) { 62352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep.setState(getDrawableState()); 62452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 62552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 62652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 62752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey @Override 628ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 629ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 630ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey // TODO: handle vertical labels 631ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (isEnabled() && mLabelLayout != null) { 632ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final int sweepHeight = mSweep.getIntrinsicHeight(); 633ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final int templateHeight = mLabelLayout.getHeight(); 634ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 635ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mSweepOffset.x = 0; 63655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mSweepOffset.y = 0; 637ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mSweepOffset.y = (int) ((templateHeight / 2) - getTargetInset()); 638ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey setMeasuredDimension(mSweep.getIntrinsicWidth(), Math.max(sweepHeight, templateHeight)); 639ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 640ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } else { 641ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mSweepOffset.x = 0; 642ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mSweepOffset.y = 0; 643ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey setMeasuredDimension(mSweep.getIntrinsicWidth(), mSweep.getIntrinsicHeight()); 644ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 645ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 646ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (mFollowAxis == VERTICAL) { 647ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final int targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top 648ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey - mSweepPadding.bottom; 649ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.top = -(mSweepPadding.top + (targetHeight / 2)); 650ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.bottom = 0; 651ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.left = -mSweepPadding.left; 652ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.right = mSweepPadding.right; 653ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } else { 654ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final int targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left 655ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey - mSweepPadding.right; 656ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.left = -(mSweepPadding.left + (targetWidth / 2)); 657ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.right = 0; 658ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.top = -mSweepPadding.top; 659ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.bottom = mSweepPadding.bottom; 660ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 661ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 6625d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mContentOffset.set(0, 0, 0, 0); 66355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 66455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey // make touch target area larger 6655d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey final int widthBefore = getMeasuredWidth(); 6665d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey final int heightBefore = getMeasuredHeight(); 66755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey if (mFollowAxis == HORIZONTAL) { 66855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey final int widthAfter = widthBefore * 3; 6695d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey setMeasuredDimension(widthAfter, heightBefore); 6705d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mContentOffset.left = (widthAfter - widthBefore) / 2; 6715d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey 6725d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey final int offset = mSweepPadding.bottom * 2; 6735d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mContentOffset.bottom -= offset; 6745d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mMargins.bottom += offset; 67555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } else { 676a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey final int heightAfter = heightBefore * 2; 6775d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey setMeasuredDimension(widthBefore, heightAfter); 67855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mContentOffset.offset(0, (heightAfter - heightBefore) / 2); 6795d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey 6805d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey final int offset = mSweepPadding.right * 2; 6815d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mContentOffset.right -= offset; 6825d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mMargins.right += offset; 68355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 68455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 6855d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mSweepOffset.offset(mContentOffset.left, mContentOffset.top); 686ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.offset(-mSweepOffset.x, -mSweepOffset.y); 687ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 688ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 689ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey @Override 690b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 691b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey super.onLayout(changed, left, top, right, bottom); 692b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey invalidateLabelOffset(); 693b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 694b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey 695b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey @Override 696ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey protected void onDraw(Canvas canvas) { 697a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey super.onDraw(canvas); 698a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 699ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final int width = getWidth(); 700ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final int height = getHeight(); 701ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 702ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final int labelSize; 703ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (isEnabled() && mLabelLayout != null) { 70455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey final int count = canvas.save(); 70555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey { 706bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey final float alignOffset = mLabelSize - LARGE_WIDTH; 707bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey canvas.translate( 708bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mContentOffset.left + alignOffset, mContentOffset.top + mLabelOffset); 70955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mLabelLayout.draw(canvas); 71055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 71155d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey canvas.restoreToCount(count); 712bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey labelSize = (int) mLabelSize; 713ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } else { 714ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey labelSize = 0; 715ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 716ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 717ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (mFollowAxis == VERTICAL) { 7185d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mSweep.setBounds(labelSize, mSweepOffset.y, width + mContentOffset.right, 719ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mSweepOffset.y + mSweep.getIntrinsicHeight()); 720ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } else { 7215d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mSweep.setBounds(mSweepOffset.x, labelSize, mSweepOffset.x + mSweep.getIntrinsicWidth(), 7225d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey height + mContentOffset.bottom); 723ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 724ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 72552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep.draw(canvas); 726a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 727a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (DRAW_OUTLINE) { 728a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mOutlinePaint.setColor(Color.RED); 729a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey canvas.drawRect(0, 0, width, height, mOutlinePaint); 730a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 731ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 732ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 73355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey public static float getLabelTop(ChartSweepView view) { 7345d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey return view.getY() + view.mContentOffset.top; 73555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 73655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 73755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey public static float getLabelBottom(ChartSweepView view) { 73855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey return getLabelTop(view) + view.mLabelLayout.getHeight(); 73955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 740bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey 741bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey public static float getLabelWidth(ChartSweepView view) { 742bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey return Layout.getDesiredWidth(view.mLabelLayout.getText(), view.mLabelLayout.getPaint()); 743bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey } 744ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey} 745