• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
ExpandableSwitchItem()69   public ExpandableSwitchItem() {
70     super();
71     setIconGravity(Gravity.TOP);
72   }
73 
ExpandableSwitchItem(Context context, AttributeSet attrs)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
getDefaultLayoutResource()84   protected int getDefaultLayoutResource() {
85     return R.layout.sud_items_expandable_switch;
86   }
87 
88   @Override
getSummary()89   public CharSequence getSummary() {
90     return isExpanded ? getExpandedSummary() : getCollapsedSummary();
91   }
92 
93   /** @return True if the item is currently expanded. */
isExpanded()94   public boolean isExpanded() {
95     return isExpanded;
96   }
97 
98   /** Sets whether the item should be expanded. */
setExpanded(boolean 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. */
getCollapsedSummary()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    */
setCollapsedSummary(CharSequence collapsedSummary)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. */
getExpandedSummary()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    */
setExpandedSummary(CharSequence expandedSummary)132   public void setExpandedSummary(CharSequence expandedSummary) {
133     this.expandedSummary = expandedSummary;
134     if (isExpanded()) {
135       notifyChanged();
136     }
137   }
138 
139   @Override
onBindView(View view)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
onClick(View v)168   public void onClick(View v) {
169     setExpanded(!isExpanded());
170   }
171 
172   // Tint the expand arrow with the text color
tintCompoundDrawables(View view)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