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