Home | History | Annotate | Download | only in items
      1 /*
      2  * Copyright (C) 2017 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 com.google.android.setupdesign.items;
     18 
     19 import android.content.Context;
     20 import android.content.res.ColorStateList;
     21 import android.content.res.TypedArray;
     22 import android.graphics.PorterDuff.Mode;
     23 import android.graphics.drawable.Drawable;
     24 import android.os.Build.VERSION;
     25 import android.os.Build.VERSION_CODES;
     26 import androidx.core.view.AccessibilityDelegateCompat;
     27 import androidx.core.view.ViewCompat;
     28 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat;
     29 import androidx.core.view.accessibility.AccessibilityNodeInfoCompat.AccessibilityActionCompat;
     30 import android.util.AttributeSet;
     31 import android.view.Gravity;
     32 import android.view.View;
     33 import android.view.View.OnClickListener;
     34 import android.widget.CompoundButton.OnCheckedChangeListener;
     35 import android.widget.TextView;
     36 import com.google.android.setupdesign.R;
     37 import com.google.android.setupdesign.view.CheckableLinearLayout;
     38 
     39 /**
     40  * A switch item which is divided into two parts: the start (left for LTR) side shows the title and
     41  * summary, and when that is clicked, will expand to show a longer summary. The end (right for LTR)
     42  * side is a switch which can be toggled by the user.
     43  *
     44  * <p>Note: It is highly recommended to use this item with recycler view rather than list view,
     45  * because list view draws the touch ripple effect on top of the item, rather than letting the item
     46  * handle it. Therefore you might see a double-ripple, one for the expandable area and one for the
     47  * entire list item, when using this in list view.
     48  */
     49 public class ExpandableSwitchItem extends SwitchItem
     50     implements OnCheckedChangeListener, OnClickListener {
     51 
     52   private CharSequence collapsedSummary;
     53   private CharSequence expandedSummary;
     54   private boolean isExpanded = false;
     55 
     56   private final AccessibilityDelegateCompat accessibilityDelegate =
     57       new AccessibilityDelegateCompat() {
     58         @Override
     59         public void onInitializeAccessibilityNodeInfo(
     60             View view, AccessibilityNodeInfoCompat nodeInfo) {
     61           super.onInitializeAccessibilityNodeInfo(view, nodeInfo);
     62           nodeInfo.addAction(
     63               isExpanded()
     64                   ? AccessibilityActionCompat.ACTION_COLLAPSE
     65                   : AccessibilityActionCompat.ACTION_EXPAND);
     66         }
     67       };
     68 
     69   public ExpandableSwitchItem() {
     70     super();
     71     setIconGravity(Gravity.TOP);
     72   }
     73 
     74   public ExpandableSwitchItem(Context context, AttributeSet attrs) {
     75     super(context, attrs);
     76     final TypedArray a = context.obtainStyledAttributes(attrs, R.styleable.SudExpandableSwitchItem);
     77     collapsedSummary = a.getText(R.styleable.SudExpandableSwitchItem_sudCollapsedSummary);
     78     expandedSummary = a.getText(R.styleable.SudExpandableSwitchItem_sudExpandedSummary);
     79     setIconGravity(a.getInt(R.styleable.SudItem_sudIconGravity, Gravity.TOP));
     80     a.recycle();
     81   }
     82 
     83   @Override
     84   protected int getDefaultLayoutResource() {
     85     return R.layout.sud_items_expandable_switch;
     86   }
     87 
     88   @Override
     89   public CharSequence getSummary() {
     90     return isExpanded ? getExpandedSummary() : getCollapsedSummary();
     91   }
     92 
     93   /** @return True if the item is currently expanded. */
     94   public boolean isExpanded() {
     95     return isExpanded;
     96   }
     97 
     98   /** Sets whether the item should be expanded. */
     99   public void setExpanded(boolean expanded) {
    100     if (isExpanded == expanded) {
    101       return;
    102     }
    103     isExpanded = expanded;
    104     notifyItemChanged();
    105   }
    106 
    107   /** @return The summary shown when in collapsed state. */
    108   public CharSequence getCollapsedSummary() {
    109     return collapsedSummary;
    110   }
    111 
    112   /**
    113    * Sets the summary text shown when the item is collapsed. Corresponds to the {@code
    114    * app:sudCollapsedSummary} XML attribute.
    115    */
    116   public void setCollapsedSummary(CharSequence collapsedSummary) {
    117     this.collapsedSummary = collapsedSummary;
    118     if (!isExpanded()) {
    119       notifyChanged();
    120     }
    121   }
    122 
    123   /** @return The summary shown when in expanded state. */
    124   public CharSequence getExpandedSummary() {
    125     return expandedSummary;
    126   }
    127 
    128   /**
    129    * Sets the summary text shown when the item is expanded. Corresponds to the {@code
    130    * app:sudExpandedSummary} XML attribute.
    131    */
    132   public void setExpandedSummary(CharSequence expandedSummary) {
    133     this.expandedSummary = expandedSummary;
    134     if (isExpanded()) {
    135       notifyChanged();
    136     }
    137   }
    138 
    139   @Override
    140   public void onBindView(View view) {
    141     // TODO: If it is possible to detect, log a warning if this is being used with ListView.
    142     super.onBindView(view);
    143     View content = view.findViewById(R.id.sud_items_expandable_switch_content);
    144     content.setOnClickListener(this);
    145 
    146     if (content instanceof CheckableLinearLayout) {
    147       CheckableLinearLayout checkableLinearLayout = (CheckableLinearLayout) content;
    148       checkableLinearLayout.setChecked(isExpanded());
    149 
    150       // On lower versions
    151       ViewCompat.setAccessibilityLiveRegion(
    152           checkableLinearLayout,
    153           isExpanded()
    154               ? ViewCompat.ACCESSIBILITY_LIVE_REGION_POLITE
    155               : ViewCompat.ACCESSIBILITY_LIVE_REGION_NONE);
    156 
    157       ViewCompat.setAccessibilityDelegate(checkableLinearLayout, accessibilityDelegate);
    158     }
    159 
    160     tintCompoundDrawables(view);
    161 
    162     // Expandable switch item has focusability on the expandable layout on the left, and the
    163     // switch on the right, but not the item itself.
    164     view.setFocusable(false);
    165   }
    166 
    167   @Override
    168   public void onClick(View v) {
    169     setExpanded(!isExpanded());
    170   }
    171 
    172   // Tint the expand arrow with the text color
    173   private void tintCompoundDrawables(View view) {
    174     final TypedArray a =
    175         view.getContext().obtainStyledAttributes(new int[] {android.R.attr.textColorPrimary});
    176     final ColorStateList tintColor = a.getColorStateList(0);
    177     a.recycle();
    178 
    179     if (tintColor != null) {
    180       TextView titleView = (TextView) view.findViewById(R.id.sud_items_title);
    181       for (Drawable drawable : titleView.getCompoundDrawables()) {
    182         if (drawable != null) {
    183           drawable.setColorFilter(tintColor.getDefaultColor(), Mode.SRC_IN);
    184         }
    185       }
    186       if (VERSION.SDK_INT >= VERSION_CODES.JELLY_BEAN_MR1) {
    187         for (Drawable drawable : titleView.getCompoundDrawablesRelative()) {
    188           if (drawable != null) {
    189             drawable.setColorFilter(tintColor.getDefaultColor(), Mode.SRC_IN);
    190           }
    191         }
    192       }
    193     }
    194   }
    195 }
    196