1 /* 2 * Copyright (C) 2007 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 17 package android.widget; 18 19 import android.annotation.DrawableRes; 20 import android.annotation.NonNull; 21 import android.annotation.Nullable; 22 import android.compat.annotation.UnsupportedAppUsage; 23 import android.content.Context; 24 import android.content.res.ColorStateList; 25 import android.content.res.TypedArray; 26 import android.graphics.BlendMode; 27 import android.graphics.Canvas; 28 import android.graphics.PorterDuff; 29 import android.graphics.drawable.Drawable; 30 import android.graphics.drawable.Icon; 31 import android.os.Parcel; 32 import android.os.Parcelable; 33 import android.util.AttributeSet; 34 import android.util.Log; 35 import android.view.Gravity; 36 import android.view.RemotableViewMethod; 37 import android.view.SoundEffectConstants; 38 import android.view.ViewDebug; 39 import android.view.ViewHierarchyEncoder; 40 import android.view.ViewStructure; 41 import android.view.accessibility.AccessibilityEvent; 42 import android.view.accessibility.AccessibilityNodeInfo; 43 import android.view.autofill.AutofillManager; 44 import android.view.autofill.AutofillValue; 45 import android.view.inspector.InspectableProperty; 46 47 import com.android.internal.R; 48 49 /** 50 * <p> 51 * A button with two states, checked and unchecked. When the button is pressed 52 * or clicked, the state changes automatically. 53 * </p> 54 * 55 * <p><strong>XML attributes</strong></p> 56 * <p> 57 * See {@link android.R.styleable#CompoundButton 58 * CompoundButton Attributes}, {@link android.R.styleable#Button Button 59 * Attributes}, {@link android.R.styleable#TextView TextView Attributes}, {@link 60 * android.R.styleable#View View Attributes} 61 * </p> 62 */ 63 public abstract class CompoundButton extends Button implements Checkable { 64 private static final String LOG_TAG = CompoundButton.class.getSimpleName(); 65 66 private boolean mChecked; 67 @UnsupportedAppUsage 68 private boolean mBroadcasting; 69 70 @UnsupportedAppUsage 71 private Drawable mButtonDrawable; 72 private ColorStateList mButtonTintList = null; 73 private BlendMode mButtonBlendMode = null; 74 private boolean mHasButtonTint = false; 75 private boolean mHasButtonBlendMode = false; 76 77 @UnsupportedAppUsage 78 private OnCheckedChangeListener mOnCheckedChangeListener; 79 private OnCheckedChangeListener mOnCheckedChangeWidgetListener; 80 81 // Indicates whether the toggle state was set from resources or dynamically, so it can be used 82 // to sanitize autofill requests. 83 private boolean mCheckedFromResource = false; 84 85 private CharSequence mCustomStateDescription = null; 86 87 private static final int[] CHECKED_STATE_SET = { 88 R.attr.state_checked 89 }; 90 CompoundButton(Context context)91 public CompoundButton(Context context) { 92 this(context, null); 93 } 94 CompoundButton(Context context, AttributeSet attrs)95 public CompoundButton(Context context, AttributeSet attrs) { 96 this(context, attrs, 0); 97 } 98 CompoundButton(Context context, AttributeSet attrs, int defStyleAttr)99 public CompoundButton(Context context, AttributeSet attrs, int defStyleAttr) { 100 this(context, attrs, defStyleAttr, 0); 101 } 102 CompoundButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes)103 public CompoundButton(Context context, AttributeSet attrs, int defStyleAttr, int defStyleRes) { 104 super(context, attrs, defStyleAttr, defStyleRes); 105 106 final TypedArray a = context.obtainStyledAttributes( 107 attrs, com.android.internal.R.styleable.CompoundButton, defStyleAttr, defStyleRes); 108 saveAttributeDataForStyleable(context, com.android.internal.R.styleable.CompoundButton, 109 attrs, a, defStyleAttr, defStyleRes); 110 111 final Drawable d = a.getDrawable(com.android.internal.R.styleable.CompoundButton_button); 112 if (d != null) { 113 setButtonDrawable(d); 114 } 115 116 if (a.hasValue(R.styleable.CompoundButton_buttonTintMode)) { 117 mButtonBlendMode = Drawable.parseBlendMode(a.getInt( 118 R.styleable.CompoundButton_buttonTintMode, -1), mButtonBlendMode); 119 mHasButtonBlendMode = true; 120 } 121 122 if (a.hasValue(R.styleable.CompoundButton_buttonTint)) { 123 mButtonTintList = a.getColorStateList(R.styleable.CompoundButton_buttonTint); 124 mHasButtonTint = true; 125 } 126 127 final boolean checked = a.getBoolean( 128 com.android.internal.R.styleable.CompoundButton_checked, false); 129 setChecked(checked); 130 mCheckedFromResource = true; 131 132 a.recycle(); 133 134 applyButtonTint(); 135 } 136 137 @Override toggle()138 public void toggle() { 139 setChecked(!mChecked); 140 } 141 142 @Override performClick()143 public boolean performClick() { 144 toggle(); 145 146 final boolean handled = super.performClick(); 147 if (!handled) { 148 // View only makes a sound effect if the onClickListener was 149 // called, so we'll need to make one here instead. 150 playSoundEffect(SoundEffectConstants.CLICK); 151 } 152 153 return handled; 154 } 155 156 @InspectableProperty 157 @ViewDebug.ExportedProperty 158 @Override isChecked()159 public boolean isChecked() { 160 return mChecked; 161 } 162 163 /** @hide */ 164 @NonNull getButtonStateDescription()165 protected CharSequence getButtonStateDescription() { 166 if (isChecked()) { 167 return getResources().getString(R.string.checked); 168 } else { 169 return getResources().getString(R.string.not_checked); 170 } 171 } 172 173 /** 174 * This function is called when an instance or subclass sets the state description. Once this 175 * is called and the argument is not null, the app developer will be responsible for updating 176 * state description when checked state changes and we will not set state description 177 * in {@link #setChecked}. App developers can restore the default behavior by setting the 178 * argument to null. If {@link #setChecked} is called first and then setStateDescription is 179 * called, two state change events will be merged by event throttling and we can still get 180 * the correct state description. 181 * 182 * @param stateDescription The state description. 183 */ 184 @Override setStateDescription(@ullable CharSequence stateDescription)185 public void setStateDescription(@Nullable CharSequence stateDescription) { 186 mCustomStateDescription = stateDescription; 187 if (stateDescription == null) { 188 setDefaultStateDescription(); 189 } else { 190 super.setStateDescription(stateDescription); 191 } 192 } 193 194 /** @hide **/ setDefaultStateDescription()195 protected void setDefaultStateDescription() { 196 if (mCustomStateDescription == null) { 197 super.setStateDescription(getButtonStateDescription()); 198 } 199 } 200 201 /** 202 * <p>Changes the checked state of this button.</p> 203 * 204 * @param checked true to check the button, false to uncheck it 205 */ 206 @Override setChecked(boolean checked)207 public void setChecked(boolean checked) { 208 if (mChecked != checked) { 209 mCheckedFromResource = false; 210 mChecked = checked; 211 refreshDrawableState(); 212 213 // Avoid infinite recursions if setChecked() is called from a listener 214 if (mBroadcasting) { 215 // setStateDescription will not send out event if the description is unchanged. 216 setDefaultStateDescription(); 217 return; 218 } 219 220 mBroadcasting = true; 221 if (mOnCheckedChangeListener != null) { 222 mOnCheckedChangeListener.onCheckedChanged(this, mChecked); 223 } 224 if (mOnCheckedChangeWidgetListener != null) { 225 mOnCheckedChangeWidgetListener.onCheckedChanged(this, mChecked); 226 } 227 final AutofillManager afm = mContext.getSystemService(AutofillManager.class); 228 if (afm != null) { 229 afm.notifyValueChanged(this); 230 } 231 232 mBroadcasting = false; 233 } 234 // setStateDescription will not send out event if the description is unchanged. 235 setDefaultStateDescription(); 236 } 237 238 /** 239 * Register a callback to be invoked when the checked state of this button 240 * changes. 241 * 242 * @param listener the callback to call on checked state change 243 */ setOnCheckedChangeListener(@ullable OnCheckedChangeListener listener)244 public void setOnCheckedChangeListener(@Nullable OnCheckedChangeListener listener) { 245 mOnCheckedChangeListener = listener; 246 } 247 248 /** 249 * Register a callback to be invoked when the checked state of this button 250 * changes. This callback is used for internal purpose only. 251 * 252 * @param listener the callback to call on checked state change 253 * @hide 254 */ setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener)255 void setOnCheckedChangeWidgetListener(OnCheckedChangeListener listener) { 256 mOnCheckedChangeWidgetListener = listener; 257 } 258 259 /** 260 * Interface definition for a callback to be invoked when the checked state 261 * of a compound button changed. 262 */ 263 public static interface OnCheckedChangeListener { 264 /** 265 * Called when the checked state of a compound button has changed. 266 * 267 * @param buttonView The compound button view whose state has changed. 268 * @param isChecked The new checked state of buttonView. 269 */ onCheckedChanged(CompoundButton buttonView, boolean isChecked)270 void onCheckedChanged(CompoundButton buttonView, boolean isChecked); 271 } 272 273 /** 274 * Sets a drawable as the compound button image given its resource 275 * identifier. 276 * 277 * @param resId the resource identifier of the drawable 278 * @attr ref android.R.styleable#CompoundButton_button 279 */ 280 @RemotableViewMethod(asyncImpl = "setButtonDrawableAsync") setButtonDrawable(@rawableRes int resId)281 public void setButtonDrawable(@DrawableRes int resId) { 282 final Drawable d; 283 if (resId != 0) { 284 d = getContext().getDrawable(resId); 285 } else { 286 d = null; 287 } 288 setButtonDrawable(d); 289 } 290 291 /** @hide **/ setButtonDrawableAsync(@rawableRes int resId)292 public Runnable setButtonDrawableAsync(@DrawableRes int resId) { 293 Drawable drawable = resId == 0 ? null : getContext().getDrawable(resId); 294 return () -> setButtonDrawable(drawable); 295 } 296 297 /** 298 * Sets a drawable as the compound button image. 299 * 300 * @param drawable the drawable to set 301 * @attr ref android.R.styleable#CompoundButton_button 302 */ setButtonDrawable(@ullable Drawable drawable)303 public void setButtonDrawable(@Nullable Drawable drawable) { 304 if (mButtonDrawable != drawable) { 305 if (mButtonDrawable != null) { 306 mButtonDrawable.setCallback(null); 307 unscheduleDrawable(mButtonDrawable); 308 } 309 310 mButtonDrawable = drawable; 311 312 if (drawable != null) { 313 drawable.setCallback(this); 314 drawable.setLayoutDirection(getLayoutDirection()); 315 if (drawable.isStateful()) { 316 drawable.setState(getDrawableState()); 317 } 318 drawable.setVisible(getVisibility() == VISIBLE, false); 319 setMinHeight(drawable.getIntrinsicHeight()); 320 applyButtonTint(); 321 } 322 } 323 } 324 325 /** 326 * @hide 327 */ 328 @Override onResolveDrawables(@esolvedLayoutDir int layoutDirection)329 public void onResolveDrawables(@ResolvedLayoutDir int layoutDirection) { 330 super.onResolveDrawables(layoutDirection); 331 if (mButtonDrawable != null) { 332 mButtonDrawable.setLayoutDirection(layoutDirection); 333 } 334 } 335 336 /** 337 * @return the drawable used as the compound button image 338 * @see #setButtonDrawable(Drawable) 339 * @see #setButtonDrawable(int) 340 */ 341 @InspectableProperty(name = "button") 342 @Nullable getButtonDrawable()343 public Drawable getButtonDrawable() { 344 return mButtonDrawable; 345 } 346 347 /** 348 * Sets the button of this CompoundButton to the specified Icon. 349 * 350 * @param icon an Icon holding the desired button, or {@code null} to clear 351 * the button 352 */ 353 @RemotableViewMethod(asyncImpl = "setButtonIconAsync") setButtonIcon(@ullable Icon icon)354 public void setButtonIcon(@Nullable Icon icon) { 355 setButtonDrawable(icon == null ? null : icon.loadDrawable(getContext())); 356 } 357 358 /** @hide **/ setButtonIconAsync(@ullable Icon icon)359 public Runnable setButtonIconAsync(@Nullable Icon icon) { 360 Drawable button = icon == null ? null : icon.loadDrawable(getContext()); 361 return () -> setButtonDrawable(button); 362 } 363 364 /** 365 * Applies a tint to the button drawable. Does not modify the current tint 366 * mode, which is {@link PorterDuff.Mode#SRC_IN} by default. 367 * <p> 368 * Subsequent calls to {@link #setButtonDrawable(Drawable)} will 369 * automatically mutate the drawable and apply the specified tint and tint 370 * mode using 371 * {@link Drawable#setTintList(ColorStateList)}. 372 * 373 * @param tint the tint to apply, may be {@code null} to clear tint 374 * 375 * @attr ref android.R.styleable#CompoundButton_buttonTint 376 * @see #setButtonTintList(ColorStateList) 377 * @see Drawable#setTintList(ColorStateList) 378 */ 379 @RemotableViewMethod setButtonTintList(@ullable ColorStateList tint)380 public void setButtonTintList(@Nullable ColorStateList tint) { 381 mButtonTintList = tint; 382 mHasButtonTint = true; 383 384 applyButtonTint(); 385 } 386 387 /** 388 * @return the tint applied to the button drawable 389 * @attr ref android.R.styleable#CompoundButton_buttonTint 390 * @see #setButtonTintList(ColorStateList) 391 */ 392 @InspectableProperty(name = "buttonTint") 393 @Nullable getButtonTintList()394 public ColorStateList getButtonTintList() { 395 return mButtonTintList; 396 } 397 398 /** 399 * Specifies the blending mode used to apply the tint specified by 400 * {@link #setButtonTintList(ColorStateList)}} to the button drawable. The 401 * default mode is {@link PorterDuff.Mode#SRC_IN}. 402 * 403 * @param tintMode the blending mode used to apply the tint, may be 404 * {@code null} to clear tint 405 * @attr ref android.R.styleable#CompoundButton_buttonTintMode 406 * @see #getButtonTintMode() 407 * @see Drawable#setTintMode(PorterDuff.Mode) 408 */ setButtonTintMode(@ullable PorterDuff.Mode tintMode)409 public void setButtonTintMode(@Nullable PorterDuff.Mode tintMode) { 410 setButtonTintBlendMode(tintMode != null ? BlendMode.fromValue(tintMode.nativeInt) : null); 411 } 412 413 /** 414 * Specifies the blending mode used to apply the tint specified by 415 * {@link #setButtonTintList(ColorStateList)}} to the button drawable. The 416 * default mode is {@link PorterDuff.Mode#SRC_IN}. 417 * 418 * @param tintMode the blending mode used to apply the tint, may be 419 * {@code null} to clear tint 420 * @attr ref android.R.styleable#CompoundButton_buttonTintMode 421 * @see #getButtonTintMode() 422 * @see Drawable#setTintBlendMode(BlendMode) 423 */ 424 @RemotableViewMethod setButtonTintBlendMode(@ullable BlendMode tintMode)425 public void setButtonTintBlendMode(@Nullable BlendMode tintMode) { 426 mButtonBlendMode = tintMode; 427 mHasButtonBlendMode = true; 428 429 applyButtonTint(); 430 } 431 432 /** 433 * @return the blending mode used to apply the tint to the button drawable 434 * @attr ref android.R.styleable#CompoundButton_buttonTintMode 435 * @see #setButtonTintMode(PorterDuff.Mode) 436 */ 437 @InspectableProperty(name = "buttonTintMode") 438 @Nullable getButtonTintMode()439 public PorterDuff.Mode getButtonTintMode() { 440 return mButtonBlendMode != null ? BlendMode.blendModeToPorterDuffMode(mButtonBlendMode) : 441 null; 442 } 443 444 /** 445 * @return the blending mode used to apply the tint to the button drawable 446 * @attr ref android.R.styleable#CompoundButton_buttonTintMode 447 * @see #setButtonTintBlendMode(BlendMode) 448 */ 449 @InspectableProperty(name = "buttonBlendMode", 450 attributeId = R.styleable.CompoundButton_buttonTintMode) 451 @Nullable getButtonTintBlendMode()452 public BlendMode getButtonTintBlendMode() { 453 return mButtonBlendMode; 454 } 455 applyButtonTint()456 private void applyButtonTint() { 457 if (mButtonDrawable != null && (mHasButtonTint || mHasButtonBlendMode)) { 458 mButtonDrawable = mButtonDrawable.mutate(); 459 460 if (mHasButtonTint) { 461 mButtonDrawable.setTintList(mButtonTintList); 462 } 463 464 if (mHasButtonBlendMode) { 465 mButtonDrawable.setTintBlendMode(mButtonBlendMode); 466 } 467 468 // The drawable (or one of its children) may not have been 469 // stateful before applying the tint, so let's try again. 470 if (mButtonDrawable.isStateful()) { 471 mButtonDrawable.setState(getDrawableState()); 472 } 473 } 474 } 475 476 @Override getAccessibilityClassName()477 public CharSequence getAccessibilityClassName() { 478 return CompoundButton.class.getName(); 479 } 480 481 /** @hide */ 482 @Override onInitializeAccessibilityEventInternal(AccessibilityEvent event)483 public void onInitializeAccessibilityEventInternal(AccessibilityEvent event) { 484 super.onInitializeAccessibilityEventInternal(event); 485 event.setChecked(mChecked); 486 } 487 488 /** @hide */ 489 @Override onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info)490 public void onInitializeAccessibilityNodeInfoInternal(AccessibilityNodeInfo info) { 491 super.onInitializeAccessibilityNodeInfoInternal(info); 492 info.setCheckable(true); 493 info.setChecked(mChecked); 494 } 495 496 @Override getCompoundPaddingLeft()497 public int getCompoundPaddingLeft() { 498 int padding = super.getCompoundPaddingLeft(); 499 if (!isLayoutRtl()) { 500 final Drawable buttonDrawable = mButtonDrawable; 501 if (buttonDrawable != null) { 502 padding += buttonDrawable.getIntrinsicWidth(); 503 } 504 } 505 return padding; 506 } 507 508 @Override getCompoundPaddingRight()509 public int getCompoundPaddingRight() { 510 int padding = super.getCompoundPaddingRight(); 511 if (isLayoutRtl()) { 512 final Drawable buttonDrawable = mButtonDrawable; 513 if (buttonDrawable != null) { 514 padding += buttonDrawable.getIntrinsicWidth(); 515 } 516 } 517 return padding; 518 } 519 520 /** 521 * @hide 522 */ 523 @Override getHorizontalOffsetForDrawables()524 public int getHorizontalOffsetForDrawables() { 525 final Drawable buttonDrawable = mButtonDrawable; 526 return (buttonDrawable != null) ? buttonDrawable.getIntrinsicWidth() : 0; 527 } 528 529 @Override onDraw(Canvas canvas)530 protected void onDraw(Canvas canvas) { 531 final Drawable buttonDrawable = mButtonDrawable; 532 if (buttonDrawable != null) { 533 final int verticalGravity = getGravity() & Gravity.VERTICAL_GRAVITY_MASK; 534 final int drawableHeight = buttonDrawable.getIntrinsicHeight(); 535 final int drawableWidth = buttonDrawable.getIntrinsicWidth(); 536 537 final int top; 538 switch (verticalGravity) { 539 case Gravity.BOTTOM: 540 top = getHeight() - drawableHeight; 541 break; 542 case Gravity.CENTER_VERTICAL: 543 top = (getHeight() - drawableHeight) / 2; 544 break; 545 default: 546 top = 0; 547 } 548 final int bottom = top + drawableHeight; 549 final int left = isLayoutRtl() ? getWidth() - drawableWidth : 0; 550 final int right = isLayoutRtl() ? getWidth() : drawableWidth; 551 552 buttonDrawable.setBounds(left, top, right, bottom); 553 554 final Drawable background = getBackground(); 555 if (background != null) { 556 background.setHotspotBounds(left, top, right, bottom); 557 } 558 } 559 560 super.onDraw(canvas); 561 562 if (buttonDrawable != null) { 563 final int scrollX = mScrollX; 564 final int scrollY = mScrollY; 565 if (scrollX == 0 && scrollY == 0) { 566 buttonDrawable.draw(canvas); 567 } else { 568 canvas.translate(scrollX, scrollY); 569 buttonDrawable.draw(canvas); 570 canvas.translate(-scrollX, -scrollY); 571 } 572 } 573 } 574 575 @Override onCreateDrawableState(int extraSpace)576 protected int[] onCreateDrawableState(int extraSpace) { 577 final int[] drawableState = super.onCreateDrawableState(extraSpace + 1); 578 if (isChecked()) { 579 mergeDrawableStates(drawableState, CHECKED_STATE_SET); 580 } 581 return drawableState; 582 } 583 584 @Override drawableStateChanged()585 protected void drawableStateChanged() { 586 super.drawableStateChanged(); 587 588 final Drawable buttonDrawable = mButtonDrawable; 589 if (buttonDrawable != null && buttonDrawable.isStateful() 590 && buttonDrawable.setState(getDrawableState())) { 591 invalidateDrawable(buttonDrawable); 592 } 593 } 594 595 @Override drawableHotspotChanged(float x, float y)596 public void drawableHotspotChanged(float x, float y) { 597 super.drawableHotspotChanged(x, y); 598 599 if (mButtonDrawable != null) { 600 mButtonDrawable.setHotspot(x, y); 601 } 602 } 603 604 @Override verifyDrawable(@onNull Drawable who)605 protected boolean verifyDrawable(@NonNull Drawable who) { 606 return super.verifyDrawable(who) || who == mButtonDrawable; 607 } 608 609 @Override jumpDrawablesToCurrentState()610 public void jumpDrawablesToCurrentState() { 611 super.jumpDrawablesToCurrentState(); 612 if (mButtonDrawable != null) mButtonDrawable.jumpToCurrentState(); 613 } 614 615 static class SavedState extends BaseSavedState { 616 boolean checked; 617 618 /** 619 * Constructor called from {@link CompoundButton#onSaveInstanceState()} 620 */ SavedState(Parcelable superState)621 SavedState(Parcelable superState) { 622 super(superState); 623 } 624 625 /** 626 * Constructor called from {@link #CREATOR} 627 */ SavedState(Parcel in)628 private SavedState(Parcel in) { 629 super(in); 630 checked = (Boolean)in.readValue(null); 631 } 632 633 @Override writeToParcel(Parcel out, int flags)634 public void writeToParcel(Parcel out, int flags) { 635 super.writeToParcel(out, flags); 636 out.writeValue(checked); 637 } 638 639 @Override toString()640 public String toString() { 641 return "CompoundButton.SavedState{" 642 + Integer.toHexString(System.identityHashCode(this)) 643 + " checked=" + checked + "}"; 644 } 645 646 @SuppressWarnings("hiding") 647 public static final @android.annotation.NonNull Parcelable.Creator<SavedState> CREATOR = 648 new Parcelable.Creator<SavedState>() { 649 @Override 650 public SavedState createFromParcel(Parcel in) { 651 return new SavedState(in); 652 } 653 654 @Override 655 public SavedState[] newArray(int size) { 656 return new SavedState[size]; 657 } 658 }; 659 } 660 661 @Override onSaveInstanceState()662 public Parcelable onSaveInstanceState() { 663 Parcelable superState = super.onSaveInstanceState(); 664 665 SavedState ss = new SavedState(superState); 666 667 ss.checked = isChecked(); 668 return ss; 669 } 670 671 @Override onRestoreInstanceState(Parcelable state)672 public void onRestoreInstanceState(Parcelable state) { 673 SavedState ss = (SavedState) state; 674 675 super.onRestoreInstanceState(ss.getSuperState()); 676 setChecked(ss.checked); 677 requestLayout(); 678 } 679 680 /** @hide */ 681 @Override encodeProperties(@onNull ViewHierarchyEncoder stream)682 protected void encodeProperties(@NonNull ViewHierarchyEncoder stream) { 683 super.encodeProperties(stream); 684 stream.addProperty("checked", isChecked()); 685 } 686 687 688 /** @hide */ 689 @Override onProvideStructure(@onNull ViewStructure structure, @ViewStructureType int viewFor, int flags)690 protected void onProvideStructure(@NonNull ViewStructure structure, 691 @ViewStructureType int viewFor, int flags) { 692 super.onProvideStructure(structure, viewFor, flags); 693 694 if (viewFor == VIEW_STRUCTURE_FOR_AUTOFILL) { 695 structure.setDataIsSensitive(!mCheckedFromResource); 696 } 697 } 698 699 @Override autofill(AutofillValue value)700 public void autofill(AutofillValue value) { 701 if (!isEnabled()) return; 702 703 if (!value.isToggle()) { 704 Log.w(LOG_TAG, value + " could not be autofilled into " + this); 705 return; 706 } 707 708 setChecked(value.getToggleValue()); 709 } 710 711 @Override getAutofillType()712 public @AutofillType int getAutofillType() { 713 return isEnabled() ? AUTOFILL_TYPE_TOGGLE : AUTOFILL_TYPE_NONE; 714 } 715 716 @Override getAutofillValue()717 public AutofillValue getAutofillValue() { 718 return isEnabled() ? AutofillValue.forToggle(isChecked()) : null; 719 } 720 } 721