blob: 5b8e8549fd4d92de4b6707656175307d8e878eed [file] [log] [blame]
Adam Powell12190b32010-11-28 19:07:53 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.widget;
18
Alan Viverettecc2688d2013-09-17 17:00:12 -070019import android.animation.ObjectAnimator;
Adam Powell12190b32010-11-28 19:07:53 -080020import android.content.Context;
21import android.content.res.ColorStateList;
22import android.content.res.Resources;
23import android.content.res.TypedArray;
24import android.graphics.Canvas;
Alan Viverette661e6362014-05-12 10:55:37 -070025import android.graphics.Insets;
Adam Powell12190b32010-11-28 19:07:53 -080026import android.graphics.Paint;
27import android.graphics.Rect;
28import android.graphics.Typeface;
Alan Viverette661e6362014-05-12 10:55:37 -070029import android.graphics.Region.Op;
Adam Powell12190b32010-11-28 19:07:53 -080030import android.graphics.drawable.Drawable;
31import android.text.Layout;
32import android.text.StaticLayout;
33import android.text.TextPaint;
34import android.text.TextUtils;
Daniel Sandler4c3308d2012-04-19 11:04:39 -040035import android.text.method.AllCapsTransformationMethod;
36import android.text.method.TransformationMethod2;
Adam Powell12190b32010-11-28 19:07:53 -080037import android.util.AttributeSet;
Alan Viverettecc2688d2013-09-17 17:00:12 -070038import android.util.FloatProperty;
39import android.util.MathUtils;
Adam Powell12190b32010-11-28 19:07:53 -080040import android.view.Gravity;
41import android.view.MotionEvent;
42import android.view.VelocityTracker;
43import android.view.ViewConfiguration;
Svetoslav Ganov63bce032011-07-23 19:52:17 -070044import android.view.accessibility.AccessibilityEvent;
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -080045import android.view.accessibility.AccessibilityNodeInfo;
Adam Powell12190b32010-11-28 19:07:53 -080046
Adam Powellbe0a4532010-11-29 17:47:48 -080047import com.android.internal.R;
48
Adam Powell12190b32010-11-28 19:07:53 -080049/**
50 * A Switch is a two-state toggle switch widget that can select between two
51 * options. The user may drag the "thumb" back and forth to choose the selected option,
Chet Haase150176d2011-08-26 09:54:06 -070052 * or simply tap to toggle as if it were a checkbox. The {@link #setText(CharSequence) text}
53 * property controls the text displayed in the label for the switch, whereas the
54 * {@link #setTextOff(CharSequence) off} and {@link #setTextOn(CharSequence) on} text
55 * controls the text on the thumb. Similarly, the
56 * {@link #setTextAppearance(android.content.Context, int) textAppearance} and the related
57 * setTypeface() methods control the typeface and style of label text, whereas the
58 * {@link #setSwitchTextAppearance(android.content.Context, int) switchTextAppearance} and
59 * the related seSwitchTypeface() methods control that of the thumb.
Adam Powell12190b32010-11-28 19:07:53 -080060 *
Scott Main4c359b72012-07-24 15:51:27 -070061 * <p>See the <a href="{@docRoot}guide/topics/ui/controls/togglebutton.html">Toggle Buttons</a>
62 * guide.</p>
63 *
64 * @attr ref android.R.styleable#Switch_textOn
65 * @attr ref android.R.styleable#Switch_textOff
66 * @attr ref android.R.styleable#Switch_switchMinWidth
67 * @attr ref android.R.styleable#Switch_switchPadding
68 * @attr ref android.R.styleable#Switch_switchTextAppearance
69 * @attr ref android.R.styleable#Switch_thumb
70 * @attr ref android.R.styleable#Switch_thumbTextPadding
71 * @attr ref android.R.styleable#Switch_track
Adam Powell12190b32010-11-28 19:07:53 -080072 */
73public class Switch extends CompoundButton {
Alan Viverettecc2688d2013-09-17 17:00:12 -070074 private static final int THUMB_ANIMATION_DURATION = 250;
75
Adam Powell12190b32010-11-28 19:07:53 -080076 private static final int TOUCH_MODE_IDLE = 0;
77 private static final int TOUCH_MODE_DOWN = 1;
78 private static final int TOUCH_MODE_DRAGGING = 2;
79
80 // Enum for the "typeface" XML parameter.
81 private static final int SANS = 1;
82 private static final int SERIF = 2;
83 private static final int MONOSPACE = 3;
84
85 private Drawable mThumbDrawable;
86 private Drawable mTrackDrawable;
87 private int mThumbTextPadding;
88 private int mSwitchMinWidth;
89 private int mSwitchPadding;
Alan Viverette661e6362014-05-12 10:55:37 -070090 private boolean mSplitTrack;
Adam Powell12190b32010-11-28 19:07:53 -080091 private CharSequence mTextOn;
92 private CharSequence mTextOff;
Alan Viverette2a37cf8d2014-06-19 17:09:10 -070093 private boolean mShowText;
Adam Powell12190b32010-11-28 19:07:53 -080094
95 private int mTouchMode;
96 private int mTouchSlop;
97 private float mTouchX;
98 private float mTouchY;
99 private VelocityTracker mVelocityTracker = VelocityTracker.obtain();
100 private int mMinFlingVelocity;
101
102 private float mThumbPosition;
103 private int mSwitchWidth;
104 private int mSwitchHeight;
105 private int mThumbWidth; // Does not include padding
106
107 private int mSwitchLeft;
108 private int mSwitchTop;
109 private int mSwitchRight;
110 private int mSwitchBottom;
111
112 private TextPaint mTextPaint;
113 private ColorStateList mTextColors;
114 private Layout mOnLayout;
115 private Layout mOffLayout;
Daniel Sandler4c3308d2012-04-19 11:04:39 -0400116 private TransformationMethod2 mSwitchTransformationMethod;
Alan Viverettecc2688d2013-09-17 17:00:12 -0700117 private ObjectAnimator mPositionAnimator;
Adam Powell12190b32010-11-28 19:07:53 -0800118
Adam Powellbe0a4532010-11-29 17:47:48 -0800119 @SuppressWarnings("hiding")
Adam Powell12190b32010-11-28 19:07:53 -0800120 private final Rect mTempRect = new Rect();
121
122 private static final int[] CHECKED_STATE_SET = {
123 R.attr.state_checked
124 };
125
126 /**
127 * Construct a new Switch with default styling.
128 *
129 * @param context The Context that will determine this widget's theming.
130 */
131 public Switch(Context context) {
132 this(context, null);
133 }
134
135 /**
136 * Construct a new Switch with default styling, overriding specific style
137 * attributes as requested.
138 *
139 * @param context The Context that will determine this widget's theming.
140 * @param attrs Specification of attributes that should deviate from default styling.
141 */
142 public Switch(Context context, AttributeSet attrs) {
143 this(context, attrs, com.android.internal.R.attr.switchStyle);
144 }
145
146 /**
147 * Construct a new Switch with a default style determined by the given theme attribute,
148 * overriding specific style attributes as requested.
149 *
150 * @param context The Context that will determine this widget's theming.
151 * @param attrs Specification of attributes that should deviate from the default styling.
Alan Viverette617feb92013-09-09 18:09:13 -0700152 * @param defStyleAttr An attribute in the current theme that contains a
153 * reference to a style resource that supplies default values for
154 * the view. Can be 0 to not look for defaults.
Adam Powell12190b32010-11-28 19:07:53 -0800155 */
Alan Viverette617feb92013-09-09 18:09:13 -0700156 public Switch(Context context, AttributeSet attrs, int defStyleAttr) {
157 this(context, attrs, defStyleAttr, 0);
158 }
159
160
161 /**
162 * Construct a new Switch with a default style determined by the given theme
163 * attribute or style resource, overriding specific style attributes as
164 * requested.
165 *
166 * @param context The Context that will determine this widget's theming.
167 * @param attrs Specification of attributes that should deviate from the
168 * default styling.
169 * @param defStyleAttr An attribute in the current theme that contains a
170 * reference to a style resource that supplies default values for
171 * the view. Can be 0 to not look for defaults.
172 * @param defStyleRes A resource identifier of a style resource that
173 * supplies default values for the view, used only if
174 * defStyleAttr is 0 or can not be found in the theme. Can be 0
175 * to not look for defaults.
176 */
177 public Switch(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) {
178 super(context, attrs, defStyleAttr, defStyleRes);
Adam Powell12190b32010-11-28 19:07:53 -0800179
180 mTextPaint = new TextPaint(Paint.ANTI_ALIAS_FLAG);
Alan Viverette661e6362014-05-12 10:55:37 -0700181
182 final Resources res = getResources();
Adam Powell12190b32010-11-28 19:07:53 -0800183 mTextPaint.density = res.getDisplayMetrics().density;
184 mTextPaint.setCompatibilityScaling(res.getCompatibilityInfo().applicationScale);
185
Alan Viverette617feb92013-09-09 18:09:13 -0700186 final TypedArray a = context.obtainStyledAttributes(
187 attrs, com.android.internal.R.styleable.Switch, defStyleAttr, defStyleRes);
Chet Haase150176d2011-08-26 09:54:06 -0700188 mThumbDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_thumb);
189 mTrackDrawable = a.getDrawable(com.android.internal.R.styleable.Switch_track);
Adam Powell12190b32010-11-28 19:07:53 -0800190 mTextOn = a.getText(com.android.internal.R.styleable.Switch_textOn);
191 mTextOff = a.getText(com.android.internal.R.styleable.Switch_textOff);
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700192 mShowText = a.getBoolean(com.android.internal.R.styleable.Switch_showText, true);
Adam Powell12190b32010-11-28 19:07:53 -0800193 mThumbTextPadding = a.getDimensionPixelSize(
194 com.android.internal.R.styleable.Switch_thumbTextPadding, 0);
195 mSwitchMinWidth = a.getDimensionPixelSize(
196 com.android.internal.R.styleable.Switch_switchMinWidth, 0);
197 mSwitchPadding = a.getDimensionPixelSize(
198 com.android.internal.R.styleable.Switch_switchPadding, 0);
Alan Viverette661e6362014-05-12 10:55:37 -0700199 mSplitTrack = a.getBoolean(com.android.internal.R.styleable.Switch_splitTrack, false);
Adam Powell12190b32010-11-28 19:07:53 -0800200
Alan Viverette661e6362014-05-12 10:55:37 -0700201 final int appearance = a.getResourceId(
Adam Powell12190b32010-11-28 19:07:53 -0800202 com.android.internal.R.styleable.Switch_switchTextAppearance, 0);
203 if (appearance != 0) {
Chet Haase150176d2011-08-26 09:54:06 -0700204 setSwitchTextAppearance(context, appearance);
Adam Powell12190b32010-11-28 19:07:53 -0800205 }
206 a.recycle();
207
Alan Viverette661e6362014-05-12 10:55:37 -0700208 final ViewConfiguration config = ViewConfiguration.get(context);
Adam Powell12190b32010-11-28 19:07:53 -0800209 mTouchSlop = config.getScaledTouchSlop();
210 mMinFlingVelocity = config.getScaledMinimumFlingVelocity();
211
212 // Refresh display with current params
Gilles Debunnee724ee42011-08-31 11:20:27 -0700213 refreshDrawableState();
Adam Powell12190b32010-11-28 19:07:53 -0800214 setChecked(isChecked());
215 }
216
217 /**
218 * Sets the switch text color, size, style, hint color, and highlight color
219 * from the specified TextAppearance resource.
Adam Powell6c86e1b2012-03-08 15:11:46 -0800220 *
221 * @attr ref android.R.styleable#Switch_switchTextAppearance
Adam Powell12190b32010-11-28 19:07:53 -0800222 */
Chet Haase150176d2011-08-26 09:54:06 -0700223 public void setSwitchTextAppearance(Context context, int resid) {
Adam Powell12190b32010-11-28 19:07:53 -0800224 TypedArray appearance =
Chet Haase150176d2011-08-26 09:54:06 -0700225 context.obtainStyledAttributes(resid,
Adam Powell12190b32010-11-28 19:07:53 -0800226 com.android.internal.R.styleable.TextAppearance);
227
228 ColorStateList colors;
229 int ts;
230
231 colors = appearance.getColorStateList(com.android.internal.R.styleable.
232 TextAppearance_textColor);
233 if (colors != null) {
234 mTextColors = colors;
Chet Haase150176d2011-08-26 09:54:06 -0700235 } else {
236 // If no color set in TextAppearance, default to the view's textColor
237 mTextColors = getTextColors();
Adam Powell12190b32010-11-28 19:07:53 -0800238 }
239
240 ts = appearance.getDimensionPixelSize(com.android.internal.R.styleable.
241 TextAppearance_textSize, 0);
242 if (ts != 0) {
243 if (ts != mTextPaint.getTextSize()) {
244 mTextPaint.setTextSize(ts);
245 requestLayout();
246 }
247 }
248
249 int typefaceIndex, styleIndex;
250
251 typefaceIndex = appearance.getInt(com.android.internal.R.styleable.
252 TextAppearance_typeface, -1);
253 styleIndex = appearance.getInt(com.android.internal.R.styleable.
254 TextAppearance_textStyle, -1);
255
256 setSwitchTypefaceByIndex(typefaceIndex, styleIndex);
257
Daniel Sandler4c3308d2012-04-19 11:04:39 -0400258 boolean allCaps = appearance.getBoolean(com.android.internal.R.styleable.
259 TextAppearance_textAllCaps, false);
260 if (allCaps) {
261 mSwitchTransformationMethod = new AllCapsTransformationMethod(getContext());
262 mSwitchTransformationMethod.setLengthChangesAllowed(true);
263 } else {
264 mSwitchTransformationMethod = null;
265 }
266
Adam Powell12190b32010-11-28 19:07:53 -0800267 appearance.recycle();
268 }
269
270 private void setSwitchTypefaceByIndex(int typefaceIndex, int styleIndex) {
271 Typeface tf = null;
272 switch (typefaceIndex) {
273 case SANS:
274 tf = Typeface.SANS_SERIF;
275 break;
276
277 case SERIF:
278 tf = Typeface.SERIF;
279 break;
280
281 case MONOSPACE:
282 tf = Typeface.MONOSPACE;
283 break;
284 }
285
286 setSwitchTypeface(tf, styleIndex);
287 }
288
289 /**
290 * Sets the typeface and style in which the text should be displayed on the
291 * switch, and turns on the fake bold and italic bits in the Paint if the
292 * Typeface that you provided does not have all the bits in the
293 * style that you specified.
294 */
295 public void setSwitchTypeface(Typeface tf, int style) {
296 if (style > 0) {
297 if (tf == null) {
298 tf = Typeface.defaultFromStyle(style);
299 } else {
300 tf = Typeface.create(tf, style);
301 }
302
303 setSwitchTypeface(tf);
304 // now compute what (if any) algorithmic styling is needed
305 int typefaceStyle = tf != null ? tf.getStyle() : 0;
306 int need = style & ~typefaceStyle;
307 mTextPaint.setFakeBoldText((need & Typeface.BOLD) != 0);
308 mTextPaint.setTextSkewX((need & Typeface.ITALIC) != 0 ? -0.25f : 0);
309 } else {
Victoria Leaseaa0980a2012-06-11 14:46:04 -0700310 mTextPaint.setFakeBoldText(false);
Adam Powell12190b32010-11-28 19:07:53 -0800311 mTextPaint.setTextSkewX(0);
312 setSwitchTypeface(tf);
313 }
314 }
315
316 /**
Chet Haase150176d2011-08-26 09:54:06 -0700317 * Sets the typeface in which the text should be displayed on the switch.
Adam Powell12190b32010-11-28 19:07:53 -0800318 * Note that not all Typeface families actually have bold and italic
319 * variants, so you may need to use
320 * {@link #setSwitchTypeface(Typeface, int)} to get the appearance
321 * that you actually want.
322 *
323 * @attr ref android.R.styleable#TextView_typeface
324 * @attr ref android.R.styleable#TextView_textStyle
325 */
326 public void setSwitchTypeface(Typeface tf) {
327 if (mTextPaint.getTypeface() != tf) {
328 mTextPaint.setTypeface(tf);
329
330 requestLayout();
331 invalidate();
332 }
333 }
334
335 /**
Adam Powell6c86e1b2012-03-08 15:11:46 -0800336 * Set the amount of horizontal padding between the switch and the associated text.
337 *
338 * @param pixels Amount of padding in pixels
339 *
340 * @attr ref android.R.styleable#Switch_switchPadding
341 */
342 public void setSwitchPadding(int pixels) {
343 mSwitchPadding = pixels;
344 requestLayout();
345 }
346
347 /**
348 * Get the amount of horizontal padding between the switch and the associated text.
349 *
350 * @return Amount of padding in pixels
351 *
352 * @attr ref android.R.styleable#Switch_switchPadding
353 */
354 public int getSwitchPadding() {
355 return mSwitchPadding;
356 }
357
358 /**
359 * Set the minimum width of the switch in pixels. The switch's width will be the maximum
360 * of this value and its measured width as determined by the switch drawables and text used.
361 *
362 * @param pixels Minimum width of the switch in pixels
363 *
364 * @attr ref android.R.styleable#Switch_switchMinWidth
365 */
366 public void setSwitchMinWidth(int pixels) {
367 mSwitchMinWidth = pixels;
368 requestLayout();
369 }
370
371 /**
372 * Get the minimum width of the switch in pixels. The switch's width will be the maximum
373 * of this value and its measured width as determined by the switch drawables and text used.
374 *
375 * @return Minimum width of the switch in pixels
376 *
377 * @attr ref android.R.styleable#Switch_switchMinWidth
378 */
379 public int getSwitchMinWidth() {
380 return mSwitchMinWidth;
381 }
382
383 /**
384 * Set the horizontal padding around the text drawn on the switch itself.
385 *
386 * @param pixels Horizontal padding for switch thumb text in pixels
387 *
388 * @attr ref android.R.styleable#Switch_thumbTextPadding
389 */
390 public void setThumbTextPadding(int pixels) {
391 mThumbTextPadding = pixels;
392 requestLayout();
393 }
394
395 /**
396 * Get the horizontal padding around the text drawn on the switch itself.
397 *
398 * @return Horizontal padding for switch thumb text in pixels
399 *
400 * @attr ref android.R.styleable#Switch_thumbTextPadding
401 */
402 public int getThumbTextPadding() {
403 return mThumbTextPadding;
404 }
405
406 /**
407 * Set the drawable used for the track that the switch slides within.
408 *
409 * @param track Track drawable
410 *
411 * @attr ref android.R.styleable#Switch_track
412 */
413 public void setTrackDrawable(Drawable track) {
414 mTrackDrawable = track;
415 requestLayout();
416 }
417
418 /**
Adam Powelld9c7be62012-03-08 19:43:43 -0800419 * Set the drawable used for the track that the switch slides within.
420 *
Adam Powelldca510e2012-03-08 20:06:39 -0800421 * @param resId Resource ID of a track drawable
Adam Powelld9c7be62012-03-08 19:43:43 -0800422 *
423 * @attr ref android.R.styleable#Switch_track
424 */
425 public void setTrackResource(int resId) {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800426 setTrackDrawable(getContext().getDrawable(resId));
Adam Powelld9c7be62012-03-08 19:43:43 -0800427 }
428
429 /**
Adam Powell6c86e1b2012-03-08 15:11:46 -0800430 * Get the drawable used for the track that the switch slides within.
431 *
432 * @return Track drawable
433 *
434 * @attr ref android.R.styleable#Switch_track
435 */
436 public Drawable getTrackDrawable() {
437 return mTrackDrawable;
438 }
439
440 /**
441 * Set the drawable used for the switch "thumb" - the piece that the user
442 * can physically touch and drag along the track.
443 *
444 * @param thumb Thumb drawable
445 *
446 * @attr ref android.R.styleable#Switch_thumb
447 */
448 public void setThumbDrawable(Drawable thumb) {
449 mThumbDrawable = thumb;
450 requestLayout();
451 }
452
453 /**
Adam Powelld9c7be62012-03-08 19:43:43 -0800454 * Set the drawable used for the switch "thumb" - the piece that the user
455 * can physically touch and drag along the track.
456 *
Adam Powelldca510e2012-03-08 20:06:39 -0800457 * @param resId Resource ID of a thumb drawable
Adam Powelld9c7be62012-03-08 19:43:43 -0800458 *
459 * @attr ref android.R.styleable#Switch_thumb
460 */
461 public void setThumbResource(int resId) {
Alan Viverette8eea3ea2014-02-03 18:40:20 -0800462 setThumbDrawable(getContext().getDrawable(resId));
Adam Powelld9c7be62012-03-08 19:43:43 -0800463 }
464
465 /**
Adam Powell6c86e1b2012-03-08 15:11:46 -0800466 * Get the drawable used for the switch "thumb" - the piece that the user
467 * can physically touch and drag along the track.
468 *
469 * @return Thumb drawable
470 *
471 * @attr ref android.R.styleable#Switch_thumb
472 */
473 public Drawable getThumbDrawable() {
474 return mThumbDrawable;
475 }
476
477 /**
Alan Viverette661e6362014-05-12 10:55:37 -0700478 * Specifies whether the track should be split by the thumb. When true,
479 * the thumb's optical bounds will be clipped out of the track drawable,
480 * then the thumb will be drawn into the resulting gap.
481 *
482 * @param splitTrack Whether the track should be split by the thumb
483 *
484 * @attr ref android.R.styleable#Switch_splitTrack
485 */
486 public void setSplitTrack(boolean splitTrack) {
487 mSplitTrack = splitTrack;
488 invalidate();
489 }
490
491 /**
492 * Returns whether the track should be split by the thumb.
493 *
494 * @attr ref android.R.styleable#Switch_splitTrack
495 */
496 public boolean getSplitTrack() {
497 return mSplitTrack;
498 }
499
500 /**
Chet Haase150176d2011-08-26 09:54:06 -0700501 * Returns the text displayed when the button is in the checked state.
Adam Powell6c86e1b2012-03-08 15:11:46 -0800502 *
503 * @attr ref android.R.styleable#Switch_textOn
Adam Powell12190b32010-11-28 19:07:53 -0800504 */
505 public CharSequence getTextOn() {
506 return mTextOn;
507 }
508
509 /**
Chet Haase150176d2011-08-26 09:54:06 -0700510 * Sets the text displayed when the button is in the checked state.
Adam Powell6c86e1b2012-03-08 15:11:46 -0800511 *
512 * @attr ref android.R.styleable#Switch_textOn
Adam Powell12190b32010-11-28 19:07:53 -0800513 */
514 public void setTextOn(CharSequence textOn) {
515 mTextOn = textOn;
516 requestLayout();
517 }
518
519 /**
Chet Haase150176d2011-08-26 09:54:06 -0700520 * Returns the text displayed when the button is not in the checked state.
Adam Powell6c86e1b2012-03-08 15:11:46 -0800521 *
522 * @attr ref android.R.styleable#Switch_textOff
Adam Powell12190b32010-11-28 19:07:53 -0800523 */
524 public CharSequence getTextOff() {
525 return mTextOff;
526 }
527
528 /**
Chet Haase150176d2011-08-26 09:54:06 -0700529 * Sets the text displayed when the button is not in the checked state.
Adam Powell6c86e1b2012-03-08 15:11:46 -0800530 *
531 * @attr ref android.R.styleable#Switch_textOff
Adam Powell12190b32010-11-28 19:07:53 -0800532 */
533 public void setTextOff(CharSequence textOff) {
534 mTextOff = textOff;
535 requestLayout();
536 }
537
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700538 /**
539 * Sets whether the on/off text should be displayed.
540 *
541 * @param showText {@code true} to display on/off text
542 * @hide
543 */
544 public void setShowText(boolean showText) {
545 if (mShowText != showText) {
546 mShowText = showText;
547 requestLayout();
548 }
549 }
550
551 /**
552 * @return whether the on/off text should be displayed
553 * @hide
554 */
555 public boolean getShowText() {
556 return mShowText;
557 }
558
Adam Powell12190b32010-11-28 19:07:53 -0800559 @Override
560 public void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700561 if (mShowText) {
562 if (mOnLayout == null) {
563 mOnLayout = makeLayout(mTextOn);
564 }
Alan Viverette5876ff42014-03-03 17:40:46 -0800565
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700566 if (mOffLayout == null) {
567 mOffLayout = makeLayout(mTextOff);
568 }
Adam Powell12190b32010-11-28 19:07:53 -0800569 }
570
571 mTrackDrawable.getPadding(mTempRect);
Alan Viverette5876ff42014-03-03 17:40:46 -0800572
Alan Viverette661e6362014-05-12 10:55:37 -0700573 final int maxTextWidth = Math.max(mOnLayout.getWidth(), mOffLayout.getWidth())
574 + mThumbTextPadding * 2;
575 mThumbWidth = Math.max(maxTextWidth, mThumbDrawable.getIntrinsicWidth());
576
Adam Powell12190b32010-11-28 19:07:53 -0800577 final int switchWidth = Math.max(mSwitchMinWidth,
Alan Viverette661e6362014-05-12 10:55:37 -0700578 2 * mThumbWidth + mTempRect.left + mTempRect.right);
Alan Viverette5876ff42014-03-03 17:40:46 -0800579 final int switchHeight = Math.max(mTrackDrawable.getIntrinsicHeight(),
580 mThumbDrawable.getIntrinsicHeight());
Adam Powell12190b32010-11-28 19:07:53 -0800581
Adam Powell12190b32010-11-28 19:07:53 -0800582
Adam Powell12190b32010-11-28 19:07:53 -0800583 mSwitchWidth = switchWidth;
584 mSwitchHeight = switchHeight;
585
586 super.onMeasure(widthMeasureSpec, heightMeasureSpec);
Adam Powell12190b32010-11-28 19:07:53 -0800587 final int measuredHeight = getMeasuredHeight();
588 if (measuredHeight < switchHeight) {
Dianne Hackborn189ee182010-12-02 21:48:53 -0800589 setMeasuredDimension(getMeasuredWidthAndState(), switchHeight);
Adam Powell12190b32010-11-28 19:07:53 -0800590 }
591 }
592
Svetoslav Ganov63bce032011-07-23 19:52:17 -0700593 @Override
594 public void onPopulateAccessibilityEvent(AccessibilityEvent event) {
595 super.onPopulateAccessibilityEvent(event);
Alan Viverette2a37cf8d2014-06-19 17:09:10 -0700596
597 final CharSequence text = isChecked() ? mTextOn : mTextOff;
598 if (text != null) {
599 event.getText().add(text);
Svetoslav Ganov76502592011-07-29 10:44:59 -0700600 }
Svetoslav Ganov63bce032011-07-23 19:52:17 -0700601 }
602
Adam Powell12190b32010-11-28 19:07:53 -0800603 private Layout makeLayout(CharSequence text) {
Daniel Sandler4c3308d2012-04-19 11:04:39 -0400604 final CharSequence transformed = (mSwitchTransformationMethod != null)
605 ? mSwitchTransformationMethod.getTransformation(text, this)
606 : text;
607
608 return new StaticLayout(transformed, mTextPaint,
609 (int) Math.ceil(Layout.getDesiredWidth(transformed, mTextPaint)),
Adam Powell12190b32010-11-28 19:07:53 -0800610 Layout.Alignment.ALIGN_NORMAL, 1.f, 0, true);
611 }
612
613 /**
614 * @return true if (x, y) is within the target area of the switch thumb
615 */
616 private boolean hitThumb(float x, float y) {
Alan Viverettecc2688d2013-09-17 17:00:12 -0700617 // Relies on mTempRect, MUST be called first!
618 final int thumbOffset = getThumbOffset();
619
Adam Powell12190b32010-11-28 19:07:53 -0800620 mThumbDrawable.getPadding(mTempRect);
621 final int thumbTop = mSwitchTop - mTouchSlop;
Alan Viverettecc2688d2013-09-17 17:00:12 -0700622 final int thumbLeft = mSwitchLeft + thumbOffset - mTouchSlop;
Adam Powell12190b32010-11-28 19:07:53 -0800623 final int thumbRight = thumbLeft + mThumbWidth +
624 mTempRect.left + mTempRect.right + mTouchSlop;
625 final int thumbBottom = mSwitchBottom + mTouchSlop;
626 return x > thumbLeft && x < thumbRight && y > thumbTop && y < thumbBottom;
627 }
628
629 @Override
630 public boolean onTouchEvent(MotionEvent ev) {
631 mVelocityTracker.addMovement(ev);
632 final int action = ev.getActionMasked();
633 switch (action) {
634 case MotionEvent.ACTION_DOWN: {
635 final float x = ev.getX();
636 final float y = ev.getY();
Gilles Debunnec2ab0d62011-06-13 12:52:48 -0700637 if (isEnabled() && hitThumb(x, y)) {
Adam Powell12190b32010-11-28 19:07:53 -0800638 mTouchMode = TOUCH_MODE_DOWN;
639 mTouchX = x;
640 mTouchY = y;
641 }
642 break;
643 }
644
645 case MotionEvent.ACTION_MOVE: {
646 switch (mTouchMode) {
647 case TOUCH_MODE_IDLE:
648 // Didn't target the thumb, treat normally.
649 break;
650
651 case TOUCH_MODE_DOWN: {
652 final float x = ev.getX();
653 final float y = ev.getY();
654 if (Math.abs(x - mTouchX) > mTouchSlop ||
655 Math.abs(y - mTouchY) > mTouchSlop) {
656 mTouchMode = TOUCH_MODE_DRAGGING;
657 getParent().requestDisallowInterceptTouchEvent(true);
658 mTouchX = x;
659 mTouchY = y;
660 return true;
661 }
662 break;
663 }
664
665 case TOUCH_MODE_DRAGGING: {
666 final float x = ev.getX();
Alan Viverettecc2688d2013-09-17 17:00:12 -0700667 final int thumbScrollRange = getThumbScrollRange();
668 final float thumbScrollOffset = x - mTouchX;
669 float dPos;
670 if (thumbScrollRange != 0) {
671 dPos = thumbScrollOffset / thumbScrollRange;
672 } else {
673 // If the thumb scroll range is empty, just use the
674 // movement direction to snap on or off.
675 dPos = thumbScrollOffset > 0 ? 1 : -1;
676 }
677 if (isLayoutRtl()) {
678 dPos = -dPos;
679 }
680 final float newPos = MathUtils.constrain(mThumbPosition + dPos, 0, 1);
Adam Powell12190b32010-11-28 19:07:53 -0800681 if (newPos != mThumbPosition) {
Adam Powell12190b32010-11-28 19:07:53 -0800682 mTouchX = x;
Alan Viverettecc2688d2013-09-17 17:00:12 -0700683 setThumbPosition(newPos);
Adam Powell12190b32010-11-28 19:07:53 -0800684 }
685 return true;
686 }
687 }
688 break;
689 }
690
691 case MotionEvent.ACTION_UP:
692 case MotionEvent.ACTION_CANCEL: {
693 if (mTouchMode == TOUCH_MODE_DRAGGING) {
694 stopDrag(ev);
Alan Viverettead2f8e32014-05-16 13:28:33 -0700695 // Allow super class to handle pressed state, etc.
696 super.onTouchEvent(ev);
Adam Powell12190b32010-11-28 19:07:53 -0800697 return true;
698 }
699 mTouchMode = TOUCH_MODE_IDLE;
700 mVelocityTracker.clear();
701 break;
702 }
703 }
704
705 return super.onTouchEvent(ev);
706 }
707
708 private void cancelSuperTouch(MotionEvent ev) {
709 MotionEvent cancel = MotionEvent.obtain(ev);
710 cancel.setAction(MotionEvent.ACTION_CANCEL);
711 super.onTouchEvent(cancel);
712 cancel.recycle();
713 }
714
715 /**
716 * Called from onTouchEvent to end a drag operation.
717 *
718 * @param ev Event that triggered the end of drag mode - ACTION_UP or ACTION_CANCEL
719 */
720 private void stopDrag(MotionEvent ev) {
721 mTouchMode = TOUCH_MODE_IDLE;
Adam Powell12190b32010-11-28 19:07:53 -0800722
Alan Viverette86453ff2013-09-26 14:46:08 -0700723 // Commit the change if the event is up and not canceled and the switch
724 // has not been disabled during the drag.
725 final boolean commitChange = ev.getAction() == MotionEvent.ACTION_UP && isEnabled();
726 final boolean newState;
Adam Powell12190b32010-11-28 19:07:53 -0800727 if (commitChange) {
Adam Powell12190b32010-11-28 19:07:53 -0800728 mVelocityTracker.computeCurrentVelocity(1000);
Alan Viverette86453ff2013-09-26 14:46:08 -0700729 final float xvel = mVelocityTracker.getXVelocity();
Adam Powell12190b32010-11-28 19:07:53 -0800730 if (Math.abs(xvel) > mMinFlingVelocity) {
Fabrice Di Meglio28efba32012-06-01 16:52:31 -0700731 newState = isLayoutRtl() ? (xvel < 0) : (xvel > 0);
Adam Powell12190b32010-11-28 19:07:53 -0800732 } else {
733 newState = getTargetCheckedState();
734 }
Adam Powell12190b32010-11-28 19:07:53 -0800735 } else {
Alan Viverette86453ff2013-09-26 14:46:08 -0700736 newState = isChecked();
Adam Powell12190b32010-11-28 19:07:53 -0800737 }
Alan Viverette86453ff2013-09-26 14:46:08 -0700738
739 setChecked(newState);
740 cancelSuperTouch(ev);
Adam Powell12190b32010-11-28 19:07:53 -0800741 }
742
743 private void animateThumbToCheckedState(boolean newCheckedState) {
Alan Viverettecc2688d2013-09-17 17:00:12 -0700744 final float targetPosition = newCheckedState ? 1 : 0;
745 mPositionAnimator = ObjectAnimator.ofFloat(this, THUMB_POS, targetPosition);
746 mPositionAnimator.setDuration(THUMB_ANIMATION_DURATION);
747 mPositionAnimator.setAutoCancel(true);
748 mPositionAnimator.start();
749 }
750
751 private void cancelPositionAnimator() {
752 if (mPositionAnimator != null) {
753 mPositionAnimator.cancel();
754 }
Adam Powell12190b32010-11-28 19:07:53 -0800755 }
756
757 private boolean getTargetCheckedState() {
Alan Viverettecc2688d2013-09-17 17:00:12 -0700758 return mThumbPosition > 0.5f;
Fabrice Di Meglio28efba32012-06-01 16:52:31 -0700759 }
760
Alan Viverettecc2688d2013-09-17 17:00:12 -0700761 /**
762 * Sets the thumb position as a decimal value between 0 (off) and 1 (on).
763 *
764 * @param position new position between [0,1]
765 */
766 private void setThumbPosition(float position) {
767 mThumbPosition = position;
768 invalidate();
769 }
770
771 @Override
772 public void toggle() {
Alan Viverette86453ff2013-09-26 14:46:08 -0700773 setChecked(!isChecked());
Adam Powell12190b32010-11-28 19:07:53 -0800774 }
775
776 @Override
777 public void setChecked(boolean checked) {
778 super.setChecked(checked);
Alan Viverettecc2688d2013-09-17 17:00:12 -0700779
Alan Viverette86453ff2013-09-26 14:46:08 -0700780 if (isAttachedToWindow() && isLaidOut()) {
781 animateThumbToCheckedState(checked);
782 } else {
783 // Immediately move the thumb to the new position.
784 cancelPositionAnimator();
785 setThumbPosition(checked ? 1 : 0);
786 }
Adam Powell12190b32010-11-28 19:07:53 -0800787 }
788
789 @Override
790 protected void onLayout(boolean changed, int left, int top, int right, int bottom) {
791 super.onLayout(changed, left, top, right, bottom);
792
Fabrice Di Meglio28efba32012-06-01 16:52:31 -0700793 int switchRight;
794 int switchLeft;
795
796 if (isLayoutRtl()) {
797 switchLeft = getPaddingLeft();
798 switchRight = switchLeft + mSwitchWidth;
799 } else {
800 switchRight = getWidth() - getPaddingRight();
801 switchLeft = switchRight - mSwitchWidth;
802 }
803
Adam Powell12190b32010-11-28 19:07:53 -0800804 int switchTop = 0;
805 int switchBottom = 0;
806 switch (getGravity() & Gravity.VERTICAL_GRAVITY_MASK) {
807 default:
808 case Gravity.TOP:
809 switchTop = getPaddingTop();
810 switchBottom = switchTop + mSwitchHeight;
811 break;
812
813 case Gravity.CENTER_VERTICAL:
814 switchTop = (getPaddingTop() + getHeight() - getPaddingBottom()) / 2 -
815 mSwitchHeight / 2;
816 switchBottom = switchTop + mSwitchHeight;
817 break;
818
819 case Gravity.BOTTOM:
820 switchBottom = getHeight() - getPaddingBottom();
821 switchTop = switchBottom - mSwitchHeight;
822 break;
823 }
824
825 mSwitchLeft = switchLeft;
826 mSwitchTop = switchTop;
827 mSwitchBottom = switchBottom;
828 mSwitchRight = switchRight;
829 }
830
831 @Override
Alan Viverettead2f8e32014-05-16 13:28:33 -0700832 public void draw(Canvas c) {
Alan Viverette5876ff42014-03-03 17:40:46 -0800833 final Rect tempRect = mTempRect;
834 final Drawable trackDrawable = mTrackDrawable;
835 final Drawable thumbDrawable = mThumbDrawable;
836
Alan Viverette661e6362014-05-12 10:55:37 -0700837 // Layout the track.
Alan Viverette5876ff42014-03-03 17:40:46 -0800838 final int switchLeft = mSwitchLeft;
839 final int switchTop = mSwitchTop;
840 final int switchRight = mSwitchRight;
841 final int switchBottom = mSwitchBottom;
842 trackDrawable.setBounds(switchLeft, switchTop, switchRight, switchBottom);
Alan Viverette5876ff42014-03-03 17:40:46 -0800843 trackDrawable.getPadding(tempRect);
Alan Viverette61956602014-04-22 19:07:06 -0700844
Alan Viverette5876ff42014-03-03 17:40:46 -0800845 final int switchInnerLeft = switchLeft + tempRect.left;
Adam Powell12190b32010-11-28 19:07:53 -0800846
Alan Viverettecc2688d2013-09-17 17:00:12 -0700847 // Relies on mTempRect, MUST be called first!
848 final int thumbPos = getThumbOffset();
849
Alan Viverette661e6362014-05-12 10:55:37 -0700850 // Layout the thumb.
Alan Viverette5876ff42014-03-03 17:40:46 -0800851 thumbDrawable.getPadding(tempRect);
Alan Viverette661e6362014-05-12 10:55:37 -0700852 final int thumbLeft = switchInnerLeft - tempRect.left + thumbPos;
853 final int thumbRight = switchInnerLeft + thumbPos + mThumbWidth + tempRect.right;
Alan Viverette5876ff42014-03-03 17:40:46 -0800854 thumbDrawable.setBounds(thumbLeft, switchTop, thumbRight, switchBottom);
Alan Viverette61956602014-04-22 19:07:06 -0700855
856 final Drawable background = getBackground();
Alan Viverettec80ad992014-05-19 15:46:17 -0700857 if (background != null) {
Alan Viverette61956602014-04-22 19:07:06 -0700858 background.setHotspotBounds(thumbLeft, switchTop, thumbRight, switchBottom);
859 }
860
Alan Viverettead2f8e32014-05-16 13:28:33 -0700861 // Draw the background.
862 super.draw(c);
863 }
864
865 @Override
866 protected void onDraw(Canvas canvas) {
Alan Viverette61956602014-04-22 19:07:06 -0700867 super.onDraw(canvas);
868
Alan Viverettead2f8e32014-05-16 13:28:33 -0700869 final Rect tempRect = mTempRect;
870 final Drawable trackDrawable = mTrackDrawable;
871 final Drawable thumbDrawable = mThumbDrawable;
872 trackDrawable.getPadding(tempRect);
873
874 final int switchTop = mSwitchTop;
875 final int switchBottom = mSwitchBottom;
876 final int switchInnerLeft = mSwitchLeft + tempRect.left;
877 final int switchInnerTop = switchTop + tempRect.top;
878 final int switchInnerRight = mSwitchRight - tempRect.right;
879 final int switchInnerBottom = switchBottom - tempRect.bottom;
880
Alan Viverette661e6362014-05-12 10:55:37 -0700881 if (mSplitTrack) {
882 final Insets insets = thumbDrawable.getOpticalInsets();
883 thumbDrawable.copyBounds(tempRect);
884 tempRect.left += insets.left;
885 tempRect.right -= insets.right;
886
887 final int saveCount = canvas.save();
888 canvas.clipRect(tempRect, Op.DIFFERENCE);
889 trackDrawable.draw(canvas);
890 canvas.restoreToCount(saveCount);
891 } else {
892 trackDrawable.draw(canvas);
893 }
Alan Viverette61956602014-04-22 19:07:06 -0700894
895 final int saveCount = canvas.save();
896 canvas.clipRect(switchInnerLeft, switchTop, switchInnerRight, switchBottom);
Alan Viverette5876ff42014-03-03 17:40:46 -0800897 thumbDrawable.draw(canvas);
Adam Powell12190b32010-11-28 19:07:53 -0800898
Alan Viverette5876ff42014-03-03 17:40:46 -0800899 final Layout switchText = getTargetCheckedState() ? mOnLayout : mOffLayout;
Fabrice Di Megliobe06e322012-09-11 17:42:45 -0700900 if (switchText != null) {
Alan Viverette661e6362014-05-12 10:55:37 -0700901 final int drawableState[] = getDrawableState();
902 if (mTextColors != null) {
903 mTextPaint.setColor(mTextColors.getColorForState(drawableState, 0));
904 }
905 mTextPaint.drawableState = drawableState;
906
Alan Viverettead2f8e32014-05-16 13:28:33 -0700907 final Rect thumbBounds = thumbDrawable.getBounds();
908 final int left = (thumbBounds.left + thumbBounds.right) / 2 - switchText.getWidth() / 2;
Alan Viverette5876ff42014-03-03 17:40:46 -0800909 final int top = (switchInnerTop + switchInnerBottom) / 2 - switchText.getHeight() / 2;
910 canvas.translate(left, top);
Fabrice Di Megliobe06e322012-09-11 17:42:45 -0700911 switchText.draw(canvas);
912 }
Adam Powell12190b32010-11-28 19:07:53 -0800913
Alan Viverette5876ff42014-03-03 17:40:46 -0800914 canvas.restoreToCount(saveCount);
Adam Powell12190b32010-11-28 19:07:53 -0800915 }
916
917 @Override
Fabrice Di Meglio28efba32012-06-01 16:52:31 -0700918 public int getCompoundPaddingLeft() {
919 if (!isLayoutRtl()) {
920 return super.getCompoundPaddingLeft();
921 }
922 int padding = super.getCompoundPaddingLeft() + mSwitchWidth;
923 if (!TextUtils.isEmpty(getText())) {
924 padding += mSwitchPadding;
925 }
926 return padding;
927 }
928
929 @Override
Adam Powell12190b32010-11-28 19:07:53 -0800930 public int getCompoundPaddingRight() {
Fabrice Di Meglio28efba32012-06-01 16:52:31 -0700931 if (isLayoutRtl()) {
932 return super.getCompoundPaddingRight();
933 }
Adam Powell12190b32010-11-28 19:07:53 -0800934 int padding = super.getCompoundPaddingRight() + mSwitchWidth;
935 if (!TextUtils.isEmpty(getText())) {
936 padding += mSwitchPadding;
937 }
938 return padding;
939 }
940
Alan Viverettecc2688d2013-09-17 17:00:12 -0700941 /**
942 * Translates thumb position to offset according to current RTL setting and
943 * thumb scroll range.
944 *
945 * @return thumb offset
946 */
947 private int getThumbOffset() {
948 final float thumbPosition;
949 if (isLayoutRtl()) {
950 thumbPosition = 1 - mThumbPosition;
951 } else {
952 thumbPosition = mThumbPosition;
953 }
954 return (int) (thumbPosition * getThumbScrollRange() + 0.5f);
955 }
956
Adam Powell12190b32010-11-28 19:07:53 -0800957 private int getThumbScrollRange() {
958 if (mTrackDrawable == null) {
959 return 0;
960 }
961 mTrackDrawable.getPadding(mTempRect);
962 return mSwitchWidth - mThumbWidth - mTempRect.left - mTempRect.right;
963 }
964
965 @Override
966 protected int[] onCreateDrawableState(int extraSpace) {
967 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1);
968 if (isChecked()) {
969 mergeDrawableStates(drawableState, CHECKED_STATE_SET);
970 }
971 return drawableState;
972 }
973
974 @Override
975 protected void drawableStateChanged() {
976 super.drawableStateChanged();
977
Alan Viverette661e6362014-05-12 10:55:37 -0700978 final int[] myDrawableState = getDrawableState();
Adam Powell12190b32010-11-28 19:07:53 -0800979
Alan Viverette2356c5e2014-05-22 22:43:59 -0700980 if (mThumbDrawable != null) {
981 mThumbDrawable.setState(myDrawableState);
Alan Viverette661e6362014-05-12 10:55:37 -0700982 }
983
984 if (mTrackDrawable != null) {
985 mTrackDrawable.setState(myDrawableState);
986 }
Adam Powell12190b32010-11-28 19:07:53 -0800987
988 invalidate();
989 }
990
Alan Viverettecebc6ba2014-06-13 15:52:13 -0700991 @Override
Alan Viverette8de14942014-06-18 18:05:15 -0700992 public void drawableHotspotChanged(float x, float y) {
993 super.drawableHotspotChanged(x, y);
Alan Viverettecebc6ba2014-06-13 15:52:13 -0700994
995 if (mThumbDrawable != null) {
996 mThumbDrawable.setHotspot(x, y);
997 }
998
999 if (mTrackDrawable != null) {
1000 mTrackDrawable.setHotspot(x, y);
1001 }
1002 }
1003
Adam Powell12190b32010-11-28 19:07:53 -08001004 @Override
Alan Viverette2356c5e2014-05-22 22:43:59 -07001005 public void invalidateDrawable(Drawable drawable) {
1006 super.invalidateDrawable(drawable);
1007
1008 if (drawable == mThumbDrawable) {
1009 // Handle changes to thumb width and height.
1010 requestLayout();
1011 }
1012 }
1013
1014 @Override
Adam Powell12190b32010-11-28 19:07:53 -08001015 protected boolean verifyDrawable(Drawable who) {
1016 return super.verifyDrawable(who) || who == mThumbDrawable || who == mTrackDrawable;
1017 }
1018
1019 @Override
1020 public void jumpDrawablesToCurrentState() {
1021 super.jumpDrawablesToCurrentState();
1022 mThumbDrawable.jumpToCurrentState();
1023 mTrackDrawable.jumpToCurrentState();
1024 }
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001025
1026 @Override
1027 public void onInitializeAccessibilityEvent(AccessibilityEvent event) {
1028 super.onInitializeAccessibilityEvent(event);
1029 event.setClassName(Switch.class.getName());
1030 }
1031
1032 @Override
1033 public void onInitializeAccessibilityNodeInfo(AccessibilityNodeInfo info) {
1034 super.onInitializeAccessibilityNodeInfo(info);
1035 info.setClassName(Switch.class.getName());
Svetoslav Ganov78bcc152012-04-12 17:17:19 -07001036 CharSequence switchText = isChecked() ? mTextOn : mTextOff;
1037 if (!TextUtils.isEmpty(switchText)) {
1038 CharSequence oldText = info.getText();
1039 if (TextUtils.isEmpty(oldText)) {
1040 info.setText(switchText);
1041 } else {
1042 StringBuilder newText = new StringBuilder();
1043 newText.append(oldText).append(' ').append(switchText);
1044 info.setText(newText);
1045 }
1046 }
Svetoslav Ganov8a78fd42012-01-17 14:36:46 -08001047 }
Alan Viverettecc2688d2013-09-17 17:00:12 -07001048
1049 private static final FloatProperty<Switch> THUMB_POS = new FloatProperty<Switch>("thumbPos") {
1050 @Override
1051 public Float get(Switch object) {
1052 return object.mThumbPosition;
1053 }
1054
1055 @Override
1056 public void setValue(Switch object, float value) {
1057 object.setThumbPosition(value);
1058 }
1059 };
Adam Powell12190b32010-11-28 19:07:53 -08001060}