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 38e6c5003278184c202833209164ddf1ae8c083f12Jeff Sharkeyimport com.android.internal.util.Preconditions; 3952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkeyimport com.android.settings.R; 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; 61b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey private int mSafeRegion; 62f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 6352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey private int mFollowAxis; 64ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 65bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey private int mLabelMinSize; 66bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey private float mLabelSize; 67bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey 68ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private int mLabelTemplateRes; 69ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private int mLabelColor; 70ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 71ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private SpannableStringBuilder mLabelTemplate; 72ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private DynamicLayout mLabelLayout; 7352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 7452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey private ChartAxis mAxis; 75ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey private long mValue; 7628130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey private long mLabelValue; 77ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 78d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey private long mValidAfter; 79d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey private long mValidBefore; 80d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey private ChartSweepView mValidAfterDynamic; 81d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey private ChartSweepView mValidBeforeDynamic; 824ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 83b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey private float mLabelOffset; 84b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey 8555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey private Paint mOutlinePaint = new Paint(); 8655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 8752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public static final int HORIZONTAL = 0; 8852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public static final int VERTICAL = 1; 8952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 90a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private int mTouchMode = MODE_NONE; 91a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 92a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private static final int MODE_NONE = 0; 93a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private static final int MODE_DRAG = 1; 94a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private static final int MODE_LABEL = 2; 95a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 96bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey private static final int LARGE_WIDTH = 1024; 97bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey 98a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private long mDragInterval = 1; 99a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 100ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public interface OnSweepListener { 101ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public void onSweep(ChartSweepView sweep, boolean sweepDone); 102a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey public void requestEdit(ChartSweepView sweep); 103ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 104ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 105ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey private OnSweepListener mListener; 106a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 107a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private float mTrackingStart; 108ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey private MotionEvent mTracking; 109ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 110461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey private ChartSweepView[] mNeighbors = new ChartSweepView[0]; 111461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey 11252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public ChartSweepView(Context context) { 11354d0af57fd2dca14f0c7c34a48942aa6ecdc3f06Jeff Sharkey this(context, null); 11452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 115ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 11652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public ChartSweepView(Context context, AttributeSet attrs) { 11752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey this(context, attrs, 0); 11852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 119ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 12052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public ChartSweepView(Context context, AttributeSet attrs, int defStyle) { 12152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey super(context, attrs, defStyle); 122ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 12352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey final TypedArray a = context.obtainStyledAttributes( 12452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey attrs, R.styleable.ChartSweepView, defStyle, 0); 1258a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey 12652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey setSweepDrawable(a.getDrawable(R.styleable.ChartSweepView_sweepDrawable)); 12752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey setFollowAxis(a.getInt(R.styleable.ChartSweepView_followAxis, -1)); 128e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey setNeighborMargin(a.getDimensionPixelSize(R.styleable.ChartSweepView_neighborMargin, 0)); 129b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey setSafeRegion(a.getDimensionPixelSize(R.styleable.ChartSweepView_safeRegion, 0)); 130ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 131bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey setLabelMinSize(a.getDimensionPixelSize(R.styleable.ChartSweepView_labelSize, 0)); 132ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey setLabelTemplate(a.getResourceId(R.styleable.ChartSweepView_labelTemplate, 0)); 133ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey setLabelColor(a.getColor(R.styleable.ChartSweepView_labelColor, Color.BLUE)); 13452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 135a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey // TODO: moved focused state directly into assets 136a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setBackgroundResource(R.drawable.data_usage_sweep_background); 137a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 13855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mOutlinePaint.setColor(Color.RED); 13955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mOutlinePaint.setStrokeWidth(1f); 14055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mOutlinePaint.setStyle(Style.STROKE); 14155d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 14252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey a.recycle(); 14352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 144a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setClickable(true); 145a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setFocusable(true); 146a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setOnClickListener(mClickListener); 147a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 14852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey setWillNotDraw(false); 14952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 15052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 151a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private OnClickListener mClickListener = new OnClickListener() { 152a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey public void onClick(View v) { 153a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey dispatchRequestEdit(); 154a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 155a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey }; 156a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 15752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey void init(ChartAxis axis) { 15852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mAxis = Preconditions.checkNotNull(axis, "missing axis"); 15952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 160ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 161461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey public void setNeighbors(ChartSweepView... neighbors) { 162461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey mNeighbors = neighbors; 163461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey } 164461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey 16552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public int getFollowAxis() { 16652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey return mFollowAxis; 16752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 16852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 169ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey public Rect getMargins() { 170ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey return mMargins; 171f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } 172f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 173a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey public void setDragInterval(long dragInterval) { 174a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mDragInterval = dragInterval; 175a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 176a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 177f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey /** 178f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey * Return the number of pixels that the "target" area is inset from the 179f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey * {@link View} edge, along the current {@link #setFollowAxis(int)}. 180f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey */ 181ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private float getTargetInset() { 182f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey if (mFollowAxis == VERTICAL) { 183ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final float targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top 184ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey - mSweepPadding.bottom; 18555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey return mSweepPadding.top + (targetHeight / 2) + mSweepOffset.y; 186f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } else { 187ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final float targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left 188ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey - mSweepPadding.right; 18955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey return mSweepPadding.left + (targetWidth / 2) + mSweepOffset.x; 190f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } 191ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 192ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 193ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public void addOnSweepListener(OnSweepListener listener) { 194ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey mListener = listener; 195ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 196ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 197ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey private void dispatchOnSweep(boolean sweepDone) { 198ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey if (mListener != null) { 199ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey mListener.onSweep(this, sweepDone); 200ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 201ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 202ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 203a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey private void dispatchRequestEdit() { 204a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (mListener != null) { 205a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mListener.requestEdit(this); 206a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 207a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 208a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 209ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey @Override 210ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey public void setEnabled(boolean enabled) { 211ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey super.setEnabled(enabled); 212a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setFocusable(enabled); 213ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey requestLayout(); 214ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 215ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 21652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public void setSweepDrawable(Drawable sweep) { 21752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (mSweep != null) { 21852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep.setCallback(null); 21952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey unscheduleDrawable(mSweep); 22052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 22152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 22252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (sweep != null) { 22352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey sweep.setCallback(this); 22452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (sweep.isStateful()) { 22552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey sweep.setState(getDrawableState()); 22652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 22752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey sweep.setVisible(getVisibility() == VISIBLE, false); 22852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep = sweep; 229ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey sweep.getPadding(mSweepPadding); 23052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } else { 23152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep = null; 23252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 23352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 23452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey invalidate(); 23552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 23652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 23752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public void setFollowAxis(int followAxis) { 23852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mFollowAxis = followAxis; 23952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 24052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 241bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey public void setLabelMinSize(int minSize) { 242bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelMinSize = minSize; 243ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidateLabelTemplate(); 244ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 245ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 246ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey public void setLabelTemplate(int resId) { 247ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelTemplateRes = resId; 248ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidateLabelTemplate(); 249ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 250ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 251ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey public void setLabelColor(int color) { 252ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelColor = color; 253ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidateLabelTemplate(); 254ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 255ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 256ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private void invalidateLabelTemplate() { 257ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (mLabelTemplateRes != 0) { 258ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final CharSequence template = getResources().getText(mLabelTemplateRes); 259ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 260ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final TextPaint paint = new TextPaint(Paint.ANTI_ALIAS_FLAG); 261ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey paint.density = getResources().getDisplayMetrics().density; 262ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey paint.setCompatibilityScaling(getResources().getCompatibilityInfo().applicationScale); 263ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey paint.setColor(mLabelColor); 264ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 265ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelTemplate = new SpannableStringBuilder(template); 266ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelLayout = new DynamicLayout( 267bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelTemplate, paint, LARGE_WIDTH, Alignment.ALIGN_RIGHT, 1f, 0f, false); 268ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidateLabel(); 269ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 270ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } else { 271ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelTemplate = null; 272ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mLabelLayout = null; 273ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 274ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 27552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey invalidate(); 276ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey requestLayout(); 277ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 278ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 279ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey private void invalidateLabel() { 280ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (mLabelTemplate != null && mAxis != null) { 28128130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey mLabelValue = mAxis.buildLabel(getResources(), mLabelTemplate, mValue); 282a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setContentDescription(mLabelTemplate); 283b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey invalidateLabelOffset(); 284ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidate(); 2855d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey } else { 2865d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mLabelValue = mValue; 287ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 28852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 28952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 290b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey /** 291b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey * When overlapping with neighbor, split difference and push label. 292b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey */ 293b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey public void invalidateLabelOffset() { 294b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey float margin; 295b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey float labelOffset = 0; 296b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (mFollowAxis == VERTICAL) { 297b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (mValidAfterDynamic != null) { 298bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelSize = Math.max(getLabelWidth(this), getLabelWidth(mValidAfterDynamic)); 299b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey margin = getLabelTop(mValidAfterDynamic) - getLabelBottom(this); 300b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (margin < 0) { 301b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey labelOffset = margin / 2; 302b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 303b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } else if (mValidBeforeDynamic != null) { 304bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelSize = Math.max(getLabelWidth(this), getLabelWidth(mValidBeforeDynamic)); 305b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey margin = getLabelTop(this) - getLabelBottom(mValidBeforeDynamic); 306b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (margin < 0) { 307b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey labelOffset = -margin / 2; 308b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 309bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey } else { 310bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelSize = getLabelWidth(this); 311b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 312b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } else { 313b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey // TODO: implement horizontal labels 314b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 315b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey 316bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mLabelSize = Math.max(mLabelSize, mLabelMinSize); 317bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey 318b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey // when offsetting label, neighbor probably needs to offset too 319b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (labelOffset != mLabelOffset) { 320b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey mLabelOffset = labelOffset; 321b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey invalidate(); 322b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (mValidAfterDynamic != null) mValidAfterDynamic.invalidateLabelOffset(); 323b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey if (mValidBeforeDynamic != null) mValidBeforeDynamic.invalidateLabelOffset(); 324b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 325b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 326b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey 32752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey @Override 32852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public void jumpDrawablesToCurrentState() { 32952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey super.jumpDrawablesToCurrentState(); 33052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (mSweep != null) { 33152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep.jumpToCurrentState(); 33252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 33352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 33452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 33552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey @Override 33652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey public void setVisibility(int visibility) { 33752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey super.setVisibility(visibility); 33852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (mSweep != null) { 33952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep.setVisible(visibility == VISIBLE, false); 34052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 34152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 34252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 34352c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey @Override 34452c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey protected boolean verifyDrawable(Drawable who) { 34552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey return who == mSweep || super.verifyDrawable(who); 34652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 34752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 348ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public ChartAxis getAxis() { 349ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey return mAxis; 350ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 351ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 3528a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey public void setValue(long value) { 3538a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey mValue = value; 354ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey invalidateLabel(); 3558a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey } 3568a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey 357ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public long getValue() { 358ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey return mValue; 359ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 360ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 36128130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey public long getLabelValue() { 36228130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey return mLabelValue; 36328130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey } 36428130d96385d7d7b17992b45fb5d124836d85880Jeff Sharkey 365ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public float getPoint() { 3662af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey if (isEnabled()) { 3672af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey return mAxis.convertToPoint(mValue); 3682af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey } else { 3692af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey // when disabled, show along top edge 3702af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey return 0; 3712af35fb44d385d7a04ea1b421c426a0350f0e896Jeff Sharkey } 372ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 373ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 374d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey /** 375d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * Set valid range this sweep can move within, in {@link #mAxis} values. The 376d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * most restrictive combination of all valid ranges is used. 377d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey */ 378d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey public void setValidRange(long validAfter, long validBefore) { 379d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey mValidAfter = validAfter; 380d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey mValidBefore = validBefore; 3814ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey } 3824ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 383e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey public void setNeighborMargin(float neighborMargin) { 384e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey mNeighborMargin = neighborMargin; 385e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 386e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 387b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey public void setSafeRegion(int safeRegion) { 388b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey mSafeRegion = safeRegion; 389b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey } 390b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey 391d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey /** 392d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * Set valid range this sweep can move within, defined by the given 393d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * {@link ChartSweepView}. The most restrictive combination of all valid 394d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * ranges is used. 395d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey */ 396e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey public void setValidRangeDynamic(ChartSweepView validAfter, ChartSweepView validBefore) { 397d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey mValidAfterDynamic = validAfter; 398d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey mValidBeforeDynamic = validBefore; 3994ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey } 4004ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 40155d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey /** 40255d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey * Test if given {@link MotionEvent} is closer to another 40355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey * {@link ChartSweepView} compared to ourselves. 40455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey */ 40555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey public boolean isTouchCloserTo(MotionEvent eventInParent, ChartSweepView another) { 406461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey final float selfDist = getTouchDistanceFromTarget(eventInParent); 407461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey final float anotherDist = another.getTouchDistanceFromTarget(eventInParent); 408461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey return anotherDist < selfDist; 409461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey } 41055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 411461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey private float getTouchDistanceFromTarget(MotionEvent eventInParent) { 41255d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey if (mFollowAxis == HORIZONTAL) { 413461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey return Math.abs(eventInParent.getX() - (getX() + getTargetInset())); 41455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } else { 415461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey return Math.abs(eventInParent.getY() - (getY() + getTargetInset())); 41655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 41755d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 41855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 419ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey @Override 420ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey public boolean onTouchEvent(MotionEvent event) { 4218a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey if (!isEnabled()) return false; 4228a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey 423ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final View parent = (View) getParent(); 424ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey switch (event.getAction()) { 425ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey case MotionEvent.ACTION_DOWN: { 426f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 427f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey // only start tracking when in sweet spot 428a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey final boolean acceptDrag; 429a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey final boolean acceptLabel; 430f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey if (mFollowAxis == VERTICAL) { 431a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey acceptDrag = event.getX() > getWidth() - (mSweepPadding.right * 8); 432a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey acceptLabel = mLabelLayout != null ? event.getX() < mLabelLayout.getWidth() 433a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey : false; 434f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } else { 435a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey acceptDrag = event.getY() > getHeight() - (mSweepPadding.bottom * 8); 436a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey acceptLabel = mLabelLayout != null ? event.getY() < mLabelLayout.getHeight() 437a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey : false; 43855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 43955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 44055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey final MotionEvent eventInParent = event.copy(); 44155d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey eventInParent.offsetLocation(getLeft(), getTop()); 44255d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 44355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey // ignore event when closer to a neighbor 444461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey for (ChartSweepView neighbor : mNeighbors) { 445461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey if (isTouchCloserTo(eventInParent, neighbor)) { 446461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey return false; 447461842a8c0000ebc1351ec7fe30b0f820b769ccfJeff Sharkey } 448f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } 449f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 450a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (acceptDrag) { 451a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (mFollowAxis == VERTICAL) { 452a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTrackingStart = getTop() - mMargins.top; 453a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } else { 454a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTrackingStart = getLeft() - mMargins.left; 455a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 456f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey mTracking = event.copy(); 457a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTouchMode = MODE_DRAG; 458d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 459d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey // starting drag should activate entire chart 460d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey if (!parent.isActivated()) { 461d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey parent.setActivated(true); 462d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 463d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 464f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey return true; 465a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } else if (acceptLabel) { 466a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTouchMode = MODE_LABEL; 467a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey return true; 468f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } else { 469a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTouchMode = MODE_NONE; 470f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey return false; 471f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey } 472ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 473ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey case MotionEvent.ACTION_MOVE: { 474a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (mTouchMode == MODE_LABEL) { 475a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey return true; 476a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 477a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 4788a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey getParent().requestDisallowInterceptTouchEvent(true); 4798a50364a71e7c261b54840210f8bacff5abecb34Jeff Sharkey 480f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey // content area of parent 481e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final Rect parentContent = getParentContentRect(); 4824ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey final Rect clampRect = computeClampRect(parentContent); 483a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (clampRect.isEmpty()) return true; 484f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 485a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey long value; 48652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (mFollowAxis == VERTICAL) { 487ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final float currentTargetY = getTop() - mMargins.top; 488a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey final float requestedTargetY = mTrackingStart 489f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey + (event.getRawY() - mTracking.getRawY()); 490f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey final float clampedTargetY = MathUtils.constrain( 4914ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey requestedTargetY, clampRect.top, clampRect.bottom); 492f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey setTranslationY(clampedTargetY - currentTargetY); 493f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 494a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey value = mAxis.convertToValue(clampedTargetY - parentContent.top); 495ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } else { 496ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final float currentTargetX = getLeft() - mMargins.left; 497a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey final float requestedTargetX = mTrackingStart 498f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey + (event.getRawX() - mTracking.getRawX()); 499f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey final float clampedTargetX = MathUtils.constrain( 5004ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey requestedTargetX, clampRect.left, clampRect.right); 501f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey setTranslationX(clampedTargetX - currentTargetX); 502f54f435f1f3215b39798c671fc64344d1867de4eJeff Sharkey 503a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey value = mAxis.convertToValue(clampedTargetX - parentContent.left); 504ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 505ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 506a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey // round value from drag to nearest increment 507a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey value -= value % mDragInterval; 508a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setValue(value); 509a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 510ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey dispatchOnSweep(false); 511ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey return true; 512ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 513ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey case MotionEvent.ACTION_UP: { 514a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (mTouchMode == MODE_LABEL) { 515a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey performClick(); 516a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } else if (mTouchMode == MODE_DRAG) { 517a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTrackingStart = 0; 518a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTracking = null; 519a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mValue = mLabelValue; 520a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey dispatchOnSweep(true); 521a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setTranslationX(0); 522a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey setTranslationY(0); 523a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey requestLayout(); 524a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 525a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 526a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mTouchMode = MODE_NONE; 527ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey return true; 528ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 529ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey default: { 530ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey return false; 531ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 532ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 533ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 534ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 535e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey /** 536e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey * Update {@link #mValue} based on current position, including any 537e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey * {@link #onTouchEvent(MotionEvent)} in progress. Typically used when 538e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey * {@link ChartAxis} changes during sweep adjustment. 539e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey */ 540e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey public void updateValueFromPosition() { 541e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final Rect parentContent = getParentContentRect(); 542e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (mFollowAxis == VERTICAL) { 543e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final float effectiveY = getY() - mMargins.top - parentContent.top; 544e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey setValue(mAxis.convertToValue(effectiveY)); 545e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } else { 546e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final float effectiveX = getX() - mMargins.left - parentContent.left; 547e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey setValue(mAxis.convertToValue(effectiveX)); 548e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 549e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 550e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 551e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey public int shouldAdjustAxis() { 552e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey return mAxis.shouldAdjustAxis(getValue()); 553e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 554e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 555e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey private Rect getParentContentRect() { 556e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final View parent = (View) getParent(); 557e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey return new Rect(parent.getPaddingLeft(), parent.getPaddingTop(), 558e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey parent.getWidth() - parent.getPaddingRight(), 559e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey parent.getHeight() - parent.getPaddingBottom()); 560e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 561e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 562d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey @Override 563d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey public void addOnLayoutChangeListener(OnLayoutChangeListener listener) { 564d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey // ignored to keep LayoutTransition from animating us 565d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 566d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 567d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey @Override 568d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey public void removeOnLayoutChangeListener(OnLayoutChangeListener listener) { 569d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey // ignored to keep LayoutTransition from animating us 570d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 571d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 572e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey private long getValidAfterDynamic() { 573d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey final ChartSweepView dynamic = mValidAfterDynamic; 574e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey return dynamic != null && dynamic.isEnabled() ? dynamic.getValue() : Long.MIN_VALUE; 575d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 576d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 577e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey private long getValidBeforeDynamic() { 578d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey final ChartSweepView dynamic = mValidBeforeDynamic; 579e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey return dynamic != null && dynamic.isEnabled() ? dynamic.getValue() : Long.MAX_VALUE; 580d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 581d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey 5824ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey /** 5834ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey * Compute {@link Rect} in {@link #getParent()} coordinates that we should 584d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey * be clamped inside of, usually from {@link #setValidRange(long, long)} 5854ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey * style rules. 5864ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey */ 5874ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey private Rect computeClampRect(Rect parentContent) { 588e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey // create two rectangles, and pick most restrictive combination 589e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final Rect rect = buildClampRect(parentContent, mValidAfter, mValidBefore, 0f); 590e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final Rect dynamicRect = buildClampRect( 591e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey parentContent, getValidAfterDynamic(), getValidBeforeDynamic(), mNeighborMargin); 5924ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 593a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (!rect.intersect(dynamicRect)) { 594a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey rect.setEmpty(); 595a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 596e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey return rect; 597e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey } 598e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 599e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey private Rect buildClampRect( 600e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey Rect parentContent, long afterValue, long beforeValue, float margin) { 601e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (mAxis instanceof InvertedChartAxis) { 602e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey long temp = beforeValue; 603e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey beforeValue = afterValue; 604e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey afterValue = temp; 605d360e5efaaf4ea5f487a84787210a6c5d26337a8Jeff Sharkey } 6064ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 607e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final boolean afterValid = afterValue != Long.MIN_VALUE && afterValue != Long.MAX_VALUE; 608e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final boolean beforeValid = beforeValue != Long.MIN_VALUE && beforeValue != Long.MAX_VALUE; 609e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 610e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final float afterPoint = mAxis.convertToPoint(afterValue) + margin; 611e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final float beforePoint = mAxis.convertToPoint(beforeValue) - margin; 612e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey 613e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey final Rect clampRect = new Rect(parentContent); 6144ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey if (mFollowAxis == VERTICAL) { 615e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (beforeValid) clampRect.bottom = clampRect.top + (int) beforePoint; 616e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (afterValid) clampRect.top += afterPoint; 6174ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey } else { 618e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (beforeValid) clampRect.right = clampRect.left + (int) beforePoint; 619e2afc0f283f58ce60c107643978bfff25ec5d5c1Jeff Sharkey if (afterValid) clampRect.left += afterPoint; 6204ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey } 6214ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey return clampRect; 6224ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey } 6234ec71e79efdb4250f8971eadf195a5ad826cb457Jeff Sharkey 624ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey @Override 62552c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey protected void drawableStateChanged() { 62652c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey super.drawableStateChanged(); 62752c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey if (mSweep.isStateful()) { 62852c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep.setState(getDrawableState()); 62952c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 63052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey } 63152c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey 63252c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey @Override 633ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey protected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) { 634ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 635ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey // TODO: handle vertical labels 636ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (isEnabled() && mLabelLayout != null) { 637ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final int sweepHeight = mSweep.getIntrinsicHeight(); 638ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final int templateHeight = mLabelLayout.getHeight(); 639ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 640ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mSweepOffset.x = 0; 64155d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mSweepOffset.y = 0; 642ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mSweepOffset.y = (int) ((templateHeight / 2) - getTargetInset()); 643ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey setMeasuredDimension(mSweep.getIntrinsicWidth(), Math.max(sweepHeight, templateHeight)); 644ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 645ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } else { 646ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mSweepOffset.x = 0; 647ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mSweepOffset.y = 0; 648ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey setMeasuredDimension(mSweep.getIntrinsicWidth(), mSweep.getIntrinsicHeight()); 649ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 650ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 651ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (mFollowAxis == VERTICAL) { 652ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final int targetHeight = mSweep.getIntrinsicHeight() - mSweepPadding.top 653ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey - mSweepPadding.bottom; 654ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.top = -(mSweepPadding.top + (targetHeight / 2)); 655ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.bottom = 0; 656ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.left = -mSweepPadding.left; 657ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.right = mSweepPadding.right; 658ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } else { 659ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final int targetWidth = mSweep.getIntrinsicWidth() - mSweepPadding.left 660ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey - mSweepPadding.right; 661ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.left = -(mSweepPadding.left + (targetWidth / 2)); 662ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.right = 0; 663ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.top = -mSweepPadding.top; 664ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.bottom = mSweepPadding.bottom; 665ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 666ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 6675d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mContentOffset.set(0, 0, 0, 0); 66855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 66955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey // make touch target area larger 6705d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey final int widthBefore = getMeasuredWidth(); 6715d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey final int heightBefore = getMeasuredHeight(); 67255d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey if (mFollowAxis == HORIZONTAL) { 67355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey final int widthAfter = widthBefore * 3; 6745d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey setMeasuredDimension(widthAfter, heightBefore); 6755d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mContentOffset.left = (widthAfter - widthBefore) / 2; 6765d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey 6775d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey final int offset = mSweepPadding.bottom * 2; 6785d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mContentOffset.bottom -= offset; 6795d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mMargins.bottom += offset; 68055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } else { 681a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey final int heightAfter = heightBefore * 2; 6825d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey setMeasuredDimension(widthBefore, heightAfter); 68355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mContentOffset.offset(0, (heightAfter - heightBefore) / 2); 6845d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey 6855d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey final int offset = mSweepPadding.right * 2; 6865d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mContentOffset.right -= offset; 6875d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mMargins.right += offset; 68855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 68955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 6905d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mSweepOffset.offset(mContentOffset.left, mContentOffset.top); 691ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mMargins.offset(-mSweepOffset.x, -mSweepOffset.y); 692ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 693ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 694ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey @Override 695b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey protected void onLayout(boolean changed, int left, int top, int right, int bottom) { 696b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey super.onLayout(changed, left, top, right, bottom); 697b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey invalidateLabelOffset(); 698b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey } 699b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey 700b98c55bd097e006703352f84f0271dec5181160aJeff Sharkey @Override 701ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey protected void onDraw(Canvas canvas) { 702a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey super.onDraw(canvas); 703a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 704ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final int width = getWidth(); 705ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey final int height = getHeight(); 706ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 707ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey final int labelSize; 708ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (isEnabled() && mLabelLayout != null) { 70955d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey final int count = canvas.save(); 71055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey { 711bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey final float alignOffset = mLabelSize - LARGE_WIDTH; 712bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey canvas.translate( 713bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey mContentOffset.left + alignOffset, mContentOffset.top + mLabelOffset); 71455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey mLabelLayout.draw(canvas); 71555d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 71655d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey canvas.restoreToCount(count); 717b654846300f79e9e4c605ce62d09f9a05d232feeJeff Sharkey labelSize = (int) mLabelSize + mSafeRegion; 718ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } else { 719ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey labelSize = 0; 720ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 721ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 722ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey if (mFollowAxis == VERTICAL) { 7235d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mSweep.setBounds(labelSize, mSweepOffset.y, width + mContentOffset.right, 724ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey mSweepOffset.y + mSweep.getIntrinsicHeight()); 725ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } else { 7265d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey mSweep.setBounds(mSweepOffset.x, labelSize, mSweepOffset.x + mSweep.getIntrinsicWidth(), 7275d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey height + mContentOffset.bottom); 728ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey } 729ec3be8a4a5efa0e98aeefbf22bb876d09f08a023Jeff Sharkey 73052c3f4461b806e4f1ce48455ee2ba0ac05dfdab4Jeff Sharkey mSweep.draw(canvas); 731a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey 732a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey if (DRAW_OUTLINE) { 733a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey mOutlinePaint.setColor(Color.RED); 734a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey canvas.drawRect(0, 0, width, height, mOutlinePaint); 735a53188fe5aa09918dd7b5a9ff79ba050a2bfc4c2Jeff Sharkey } 736ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey } 737ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey 73855d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey public static float getLabelTop(ChartSweepView view) { 7395d70679c02a57416eac46581d7242e2382e4b973Jeff Sharkey return view.getY() + view.mContentOffset.top; 74055d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 74155d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey 74255d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey public static float getLabelBottom(ChartSweepView view) { 74355d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey return getLabelTop(view) + view.mLabelLayout.getHeight(); 74455d18a57e45e11f657346cbaa7fd454f92775229Jeff Sharkey } 745bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey 746bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey public static float getLabelWidth(ChartSweepView view) { 747bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey return Layout.getDesiredWidth(view.mLabelLayout.getText(), view.mLabelLayout.getPaint()); 748bdf98e84ab1eeedf9662ed3c08dc917f5fa80f85Jeff Sharkey } 749ab2d8d3a38857b8c155e6c6393c5821f5a341aaeJeff Sharkey} 750